sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052         }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059           
1060         // sort?? a.sort().equals(b.sort());
1061           
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064             return false;
1065             }
1066         }
1067         return true;
1068     } 
1069     
1070     
1071     
1072     
1073 });
1074
1075 Roo.applyIf(Array, {
1076  /**
1077      * from
1078      * @static
1079      * @param {Array} o Or Array like object (eg. nodelist)
1080      * @returns {Array} 
1081      */
1082     from : function(o)
1083     {
1084         var ret= [];
1085     
1086         for (var i =0; i < o.length; i++) { 
1087             ret[i] = o[i];
1088         }
1089         return ret;
1090       
1091     }
1092 });
1093 /*
1094  * Based on:
1095  * Ext JS Library 1.1.1
1096  * Copyright(c) 2006-2007, Ext JS, LLC.
1097  *
1098  * Originally Released Under LGPL - original licence link has changed is not relivant.
1099  *
1100  * Fork - LGPL
1101  * <script type="text/javascript">
1102  */
1103
1104 /**
1105  * @class Date
1106  *
1107  * The date parsing and format syntax is a subset of
1108  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1109  * supported will provide results equivalent to their PHP versions.
1110  *
1111  * Following is the list of all currently supported formats:
1112  *<pre>
1113 Sample date:
1114 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1115
1116 Format  Output      Description
1117 ------  ----------  --------------------------------------------------------------
1118   d      10         Day of the month, 2 digits with leading zeros
1119   D      Wed        A textual representation of a day, three letters
1120   j      10         Day of the month without leading zeros
1121   l      Wednesday  A full textual representation of the day of the week
1122   S      th         English ordinal day of month suffix, 2 chars (use with j)
1123   w      3          Numeric representation of the day of the week
1124   z      9          The julian date, or day of the year (0-365)
1125   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1126   F      January    A full textual representation of the month
1127   m      01         Numeric representation of a month, with leading zeros
1128   M      Jan        Month name abbreviation, three letters
1129   n      1          Numeric representation of a month, without leading zeros
1130   t      31         Number of days in the given month
1131   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1132   Y      2007       A full numeric representation of a year, 4 digits
1133   y      07         A two digit representation of a year
1134   a      pm         Lowercase Ante meridiem and Post meridiem
1135   A      PM         Uppercase Ante meridiem and Post meridiem
1136   g      3          12-hour format of an hour without leading zeros
1137   G      15         24-hour format of an hour without leading zeros
1138   h      03         12-hour format of an hour with leading zeros
1139   H      15         24-hour format of an hour with leading zeros
1140   i      05         Minutes with leading zeros
1141   s      01         Seconds, with leading zeros
1142   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1143   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1144   T      CST        Timezone setting of the machine running the code
1145   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1146 </pre>
1147  *
1148  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1149  * <pre><code>
1150 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1151 document.write(dt.format('Y-m-d'));                         //2007-01-10
1152 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1153 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
1154  </code></pre>
1155  *
1156  * Here are some standard date/time patterns that you might find helpful.  They
1157  * are not part of the source of Date.js, but to use them you can simply copy this
1158  * block of code into any script that is included after Date.js and they will also become
1159  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1160  * <pre><code>
1161 Date.patterns = {
1162     ISO8601Long:"Y-m-d H:i:s",
1163     ISO8601Short:"Y-m-d",
1164     ShortDate: "n/j/Y",
1165     LongDate: "l, F d, Y",
1166     FullDateTime: "l, F d, Y g:i:s A",
1167     MonthDay: "F d",
1168     ShortTime: "g:i A",
1169     LongTime: "g:i:s A",
1170     SortableDateTime: "Y-m-d\\TH:i:s",
1171     UniversalSortableDateTime: "Y-m-d H:i:sO",
1172     YearMonth: "F, Y"
1173 };
1174 </code></pre>
1175  *
1176  * Example usage:
1177  * <pre><code>
1178 var dt = new Date();
1179 document.write(dt.format(Date.patterns.ShortDate));
1180  </code></pre>
1181  */
1182
1183 /*
1184  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1185  * They generate precompiled functions from date formats instead of parsing and
1186  * processing the pattern every time you format a date.  These functions are available
1187  * on every Date object (any javascript function).
1188  *
1189  * The original article and download are here:
1190  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1191  *
1192  */
1193  
1194  
1195  // was in core
1196 /**
1197  Returns the number of milliseconds between this date and date
1198  @param {Date} date (optional) Defaults to now
1199  @return {Number} The diff in milliseconds
1200  @member Date getElapsed
1201  */
1202 Date.prototype.getElapsed = function(date) {
1203         return Math.abs((date || new Date()).getTime()-this.getTime());
1204 };
1205 // was in date file..
1206
1207
1208 // private
1209 Date.parseFunctions = {count:0};
1210 // private
1211 Date.parseRegexes = [];
1212 // private
1213 Date.formatFunctions = {count:0};
1214
1215 // private
1216 Date.prototype.dateFormat = function(format) {
1217     if (Date.formatFunctions[format] == null) {
1218         Date.createNewFormat(format);
1219     }
1220     var func = Date.formatFunctions[format];
1221     return this[func]();
1222 };
1223
1224
1225 /**
1226  * Formats a date given the supplied format string
1227  * @param {String} format The format string
1228  * @return {String} The formatted date
1229  * @method
1230  */
1231 Date.prototype.format = Date.prototype.dateFormat;
1232
1233 // private
1234 Date.createNewFormat = function(format) {
1235     var funcName = "format" + Date.formatFunctions.count++;
1236     Date.formatFunctions[format] = funcName;
1237     var code = "Date.prototype." + funcName + " = function(){return ";
1238     var special = false;
1239     var ch = '';
1240     for (var i = 0; i < format.length; ++i) {
1241         ch = format.charAt(i);
1242         if (!special && ch == "\\") {
1243             special = true;
1244         }
1245         else if (special) {
1246             special = false;
1247             code += "'" + String.escape(ch) + "' + ";
1248         }
1249         else {
1250             code += Date.getFormatCode(ch);
1251         }
1252     }
1253     /** eval:var:zzzzzzzzzzzzz */
1254     eval(code.substring(0, code.length - 3) + ";}");
1255 };
1256
1257 // private
1258 Date.getFormatCode = function(character) {
1259     switch (character) {
1260     case "d":
1261         return "String.leftPad(this.getDate(), 2, '0') + ";
1262     case "D":
1263         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1264     case "j":
1265         return "this.getDate() + ";
1266     case "l":
1267         return "Date.dayNames[this.getDay()] + ";
1268     case "S":
1269         return "this.getSuffix() + ";
1270     case "w":
1271         return "this.getDay() + ";
1272     case "z":
1273         return "this.getDayOfYear() + ";
1274     case "W":
1275         return "this.getWeekOfYear() + ";
1276     case "F":
1277         return "Date.monthNames[this.getMonth()] + ";
1278     case "m":
1279         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1280     case "M":
1281         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1282     case "n":
1283         return "(this.getMonth() + 1) + ";
1284     case "t":
1285         return "this.getDaysInMonth() + ";
1286     case "L":
1287         return "(this.isLeapYear() ? 1 : 0) + ";
1288     case "Y":
1289         return "this.getFullYear() + ";
1290     case "y":
1291         return "('' + this.getFullYear()).substring(2, 4) + ";
1292     case "a":
1293         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1294     case "A":
1295         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1296     case "g":
1297         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1298     case "G":
1299         return "this.getHours() + ";
1300     case "h":
1301         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1302     case "H":
1303         return "String.leftPad(this.getHours(), 2, '0') + ";
1304     case "i":
1305         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1306     case "s":
1307         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1308     case "O":
1309         return "this.getGMTOffset() + ";
1310     case "P":
1311         return "this.getGMTColonOffset() + ";
1312     case "T":
1313         return "this.getTimezone() + ";
1314     case "Z":
1315         return "(this.getTimezoneOffset() * -60) + ";
1316     default:
1317         return "'" + String.escape(character) + "' + ";
1318     }
1319 };
1320
1321 /**
1322  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1323  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1324  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1325  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1326  * string or the parse operation will fail.
1327  * Example Usage:
1328 <pre><code>
1329 //dt = Fri May 25 2007 (current date)
1330 var dt = new Date();
1331
1332 //dt = Thu May 25 2006 (today's month/day in 2006)
1333 dt = Date.parseDate("2006", "Y");
1334
1335 //dt = Sun Jan 15 2006 (all date parts specified)
1336 dt = Date.parseDate("2006-1-15", "Y-m-d");
1337
1338 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1339 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1340 </code></pre>
1341  * @param {String} input The unparsed date as a string
1342  * @param {String} format The format the date is in
1343  * @return {Date} The parsed date
1344  * @static
1345  */
1346 Date.parseDate = function(input, format) {
1347     if (Date.parseFunctions[format] == null) {
1348         Date.createParser(format);
1349     }
1350     var func = Date.parseFunctions[format];
1351     return Date[func](input);
1352 };
1353 /**
1354  * @private
1355  */
1356
1357 Date.createParser = function(format) {
1358     var funcName = "parse" + Date.parseFunctions.count++;
1359     var regexNum = Date.parseRegexes.length;
1360     var currentGroup = 1;
1361     Date.parseFunctions[format] = funcName;
1362
1363     var code = "Date." + funcName + " = function(input){\n"
1364         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1365         + "var d = new Date();\n"
1366         + "y = d.getFullYear();\n"
1367         + "m = d.getMonth();\n"
1368         + "d = d.getDate();\n"
1369         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1370         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1371         + "if (results && results.length > 0) {";
1372     var regex = "";
1373
1374     var special = false;
1375     var ch = '';
1376     for (var i = 0; i < format.length; ++i) {
1377         ch = format.charAt(i);
1378         if (!special && ch == "\\") {
1379             special = true;
1380         }
1381         else if (special) {
1382             special = false;
1383             regex += String.escape(ch);
1384         }
1385         else {
1386             var obj = Date.formatCodeToRegex(ch, currentGroup);
1387             currentGroup += obj.g;
1388             regex += obj.s;
1389             if (obj.g && obj.c) {
1390                 code += obj.c;
1391             }
1392         }
1393     }
1394
1395     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1396         + "{v = new Date(y, m, d, h, i, s);}\n"
1397         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1398         + "{v = new Date(y, m, d, h, i);}\n"
1399         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1400         + "{v = new Date(y, m, d, h);}\n"
1401         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1402         + "{v = new Date(y, m, d);}\n"
1403         + "else if (y >= 0 && m >= 0)\n"
1404         + "{v = new Date(y, m);}\n"
1405         + "else if (y >= 0)\n"
1406         + "{v = new Date(y);}\n"
1407         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1408         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1409         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1410         + ";}";
1411
1412     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1413     /** eval:var:zzzzzzzzzzzzz */
1414     eval(code);
1415 };
1416
1417 // private
1418 Date.formatCodeToRegex = function(character, currentGroup) {
1419     switch (character) {
1420     case "D":
1421         return {g:0,
1422         c:null,
1423         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1424     case "j":
1425         return {g:1,
1426             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1427             s:"(\\d{1,2})"}; // day of month without leading zeroes
1428     case "d":
1429         return {g:1,
1430             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1431             s:"(\\d{2})"}; // day of month with leading zeroes
1432     case "l":
1433         return {g:0,
1434             c:null,
1435             s:"(?:" + Date.dayNames.join("|") + ")"};
1436     case "S":
1437         return {g:0,
1438             c:null,
1439             s:"(?:st|nd|rd|th)"};
1440     case "w":
1441         return {g:0,
1442             c:null,
1443             s:"\\d"};
1444     case "z":
1445         return {g:0,
1446             c:null,
1447             s:"(?:\\d{1,3})"};
1448     case "W":
1449         return {g:0,
1450             c:null,
1451             s:"(?:\\d{2})"};
1452     case "F":
1453         return {g:1,
1454             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1455             s:"(" + Date.monthNames.join("|") + ")"};
1456     case "M":
1457         return {g:1,
1458             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1459             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1460     case "n":
1461         return {g:1,
1462             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1463             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1464     case "m":
1465         return {g:1,
1466             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1467             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1468     case "t":
1469         return {g:0,
1470             c:null,
1471             s:"\\d{1,2}"};
1472     case "L":
1473         return {g:0,
1474             c:null,
1475             s:"(?:1|0)"};
1476     case "Y":
1477         return {g:1,
1478             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1479             s:"(\\d{4})"};
1480     case "y":
1481         return {g:1,
1482             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1483                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1484             s:"(\\d{1,2})"};
1485     case "a":
1486         return {g:1,
1487             c:"if (results[" + currentGroup + "] == 'am') {\n"
1488                 + "if (h == 12) { h = 0; }\n"
1489                 + "} else { if (h < 12) { h += 12; }}",
1490             s:"(am|pm)"};
1491     case "A":
1492         return {g:1,
1493             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1494                 + "if (h == 12) { h = 0; }\n"
1495                 + "} else { if (h < 12) { h += 12; }}",
1496             s:"(AM|PM)"};
1497     case "g":
1498     case "G":
1499         return {g:1,
1500             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1501             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1502     case "h":
1503     case "H":
1504         return {g:1,
1505             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1506             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1507     case "i":
1508         return {g:1,
1509             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{2})"};
1511     case "s":
1512         return {g:1,
1513             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{2})"};
1515     case "O":
1516         return {g:1,
1517             c:[
1518                 "o = results[", currentGroup, "];\n",
1519                 "var sn = o.substring(0,1);\n", // get + / - sign
1520                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1521                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1522                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1523                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1524             ].join(""),
1525             s:"([+\-]\\d{2,4})"};
1526     
1527     
1528     case "P":
1529         return {g:1,
1530                 c:[
1531                    "o = results[", currentGroup, "];\n",
1532                    "var sn = o.substring(0,1);\n",
1533                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1534                    "var mn = o.substring(4,6) % 60;\n",
1535                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1536                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1537             ].join(""),
1538             s:"([+\-]\\d{4})"};
1539     case "T":
1540         return {g:0,
1541             c:null,
1542             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1543     case "Z":
1544         return {g:1,
1545             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1546                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1547             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1548     default:
1549         return {g:0,
1550             c:null,
1551             s:String.escape(character)};
1552     }
1553 };
1554
1555 /**
1556  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1557  * @return {String} The abbreviated timezone name (e.g. 'CST')
1558  */
1559 Date.prototype.getTimezone = function() {
1560     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1561 };
1562
1563 /**
1564  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1565  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1566  */
1567 Date.prototype.getGMTOffset = function() {
1568     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1569         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1570         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1571 };
1572
1573 /**
1574  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1575  * @return {String} 2-characters representing hours and 2-characters representing minutes
1576  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1577  */
1578 Date.prototype.getGMTColonOffset = function() {
1579         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1580                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1581                 + ":"
1582                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1583 }
1584
1585 /**
1586  * Get the numeric day number of the year, adjusted for leap year.
1587  * @return {Number} 0 through 364 (365 in leap years)
1588  */
1589 Date.prototype.getDayOfYear = function() {
1590     var num = 0;
1591     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1592     for (var i = 0; i < this.getMonth(); ++i) {
1593         num += Date.daysInMonth[i];
1594     }
1595     return num + this.getDate() - 1;
1596 };
1597
1598 /**
1599  * Get the string representation of the numeric week number of the year
1600  * (equivalent to the format specifier 'W').
1601  * @return {String} '00' through '52'
1602  */
1603 Date.prototype.getWeekOfYear = function() {
1604     // Skip to Thursday of this week
1605     var now = this.getDayOfYear() + (4 - this.getDay());
1606     // Find the first Thursday of the year
1607     var jan1 = new Date(this.getFullYear(), 0, 1);
1608     var then = (7 - jan1.getDay() + 4);
1609     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1610 };
1611
1612 /**
1613  * Whether or not the current date is in a leap year.
1614  * @return {Boolean} True if the current date is in a leap year, else false
1615  */
1616 Date.prototype.isLeapYear = function() {
1617     var year = this.getFullYear();
1618     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1619 };
1620
1621 /**
1622  * Get the first day of the current month, adjusted for leap year.  The returned value
1623  * is the numeric day index within the week (0-6) which can be used in conjunction with
1624  * the {@link #monthNames} array to retrieve the textual day name.
1625  * Example:
1626  *<pre><code>
1627 var dt = new Date('1/10/2007');
1628 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1629 </code></pre>
1630  * @return {Number} The day number (0-6)
1631  */
1632 Date.prototype.getFirstDayOfMonth = function() {
1633     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1634     return (day < 0) ? (day + 7) : day;
1635 };
1636
1637 /**
1638  * Get the last day of the current month, adjusted for leap year.  The returned value
1639  * is the numeric day index within the week (0-6) which can be used in conjunction with
1640  * the {@link #monthNames} array to retrieve the textual day name.
1641  * Example:
1642  *<pre><code>
1643 var dt = new Date('1/10/2007');
1644 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1645 </code></pre>
1646  * @return {Number} The day number (0-6)
1647  */
1648 Date.prototype.getLastDayOfMonth = function() {
1649     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1650     return (day < 0) ? (day + 7) : day;
1651 };
1652
1653
1654 /**
1655  * Get the first date of this date's month
1656  * @return {Date}
1657  */
1658 Date.prototype.getFirstDateOfMonth = function() {
1659     return new Date(this.getFullYear(), this.getMonth(), 1);
1660 };
1661
1662 /**
1663  * Get the last date of this date's month
1664  * @return {Date}
1665  */
1666 Date.prototype.getLastDateOfMonth = function() {
1667     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1668 };
1669 /**
1670  * Get the number of days in the current month, adjusted for leap year.
1671  * @return {Number} The number of days in the month
1672  */
1673 Date.prototype.getDaysInMonth = function() {
1674     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1675     return Date.daysInMonth[this.getMonth()];
1676 };
1677
1678 /**
1679  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1680  * @return {String} 'st, 'nd', 'rd' or 'th'
1681  */
1682 Date.prototype.getSuffix = function() {
1683     switch (this.getDate()) {
1684         case 1:
1685         case 21:
1686         case 31:
1687             return "st";
1688         case 2:
1689         case 22:
1690             return "nd";
1691         case 3:
1692         case 23:
1693             return "rd";
1694         default:
1695             return "th";
1696     }
1697 };
1698
1699 // private
1700 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1701
1702 /**
1703  * An array of textual month names.
1704  * Override these values for international dates, for example...
1705  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1706  * @type Array
1707  * @static
1708  */
1709 Date.monthNames =
1710    ["January",
1711     "February",
1712     "March",
1713     "April",
1714     "May",
1715     "June",
1716     "July",
1717     "August",
1718     "September",
1719     "October",
1720     "November",
1721     "December"];
1722
1723 /**
1724  * An array of textual day names.
1725  * Override these values for international dates, for example...
1726  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1727  * @type Array
1728  * @static
1729  */
1730 Date.dayNames =
1731    ["Sunday",
1732     "Monday",
1733     "Tuesday",
1734     "Wednesday",
1735     "Thursday",
1736     "Friday",
1737     "Saturday"];
1738
1739 // private
1740 Date.y2kYear = 50;
1741 // private
1742 Date.monthNumbers = {
1743     Jan:0,
1744     Feb:1,
1745     Mar:2,
1746     Apr:3,
1747     May:4,
1748     Jun:5,
1749     Jul:6,
1750     Aug:7,
1751     Sep:8,
1752     Oct:9,
1753     Nov:10,
1754     Dec:11};
1755
1756 /**
1757  * Creates and returns a new Date instance with the exact same date value as the called instance.
1758  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1759  * variable will also be changed.  When the intention is to create a new variable that will not
1760  * modify the original instance, you should create a clone.
1761  *
1762  * Example of correctly cloning a date:
1763  * <pre><code>
1764 //wrong way:
1765 var orig = new Date('10/1/2006');
1766 var copy = orig;
1767 copy.setDate(5);
1768 document.write(orig);  //returns 'Thu Oct 05 2006'!
1769
1770 //correct way:
1771 var orig = new Date('10/1/2006');
1772 var copy = orig.clone();
1773 copy.setDate(5);
1774 document.write(orig);  //returns 'Thu Oct 01 2006'
1775 </code></pre>
1776  * @return {Date} The new Date instance
1777  */
1778 Date.prototype.clone = function() {
1779         return new Date(this.getTime());
1780 };
1781
1782 /**
1783  * Clears any time information from this date
1784  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1785  @return {Date} this or the clone
1786  */
1787 Date.prototype.clearTime = function(clone){
1788     if(clone){
1789         return this.clone().clearTime();
1790     }
1791     this.setHours(0);
1792     this.setMinutes(0);
1793     this.setSeconds(0);
1794     this.setMilliseconds(0);
1795     return this;
1796 };
1797
1798 // private
1799 // safari setMonth is broken -- check that this is only donw once...
1800 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1801     Date.brokenSetMonth = Date.prototype.setMonth;
1802         Date.prototype.setMonth = function(num){
1803                 if(num <= -1){
1804                         var n = Math.ceil(-num);
1805                         var back_year = Math.ceil(n/12);
1806                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1807                         this.setFullYear(this.getFullYear() - back_year);
1808                         return Date.brokenSetMonth.call(this, month);
1809                 } else {
1810                         return Date.brokenSetMonth.apply(this, arguments);
1811                 }
1812         };
1813 }
1814
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MILLI = "ms";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.SECOND = "s";
1823 /** Date interval constant 
1824 * @static 
1825 * @type String */
1826 Date.MINUTE = "mi";
1827 /** Date interval constant 
1828 * @static 
1829 * @type String */
1830 Date.HOUR = "h";
1831 /** Date interval constant 
1832 * @static 
1833 * @type String */
1834 Date.DAY = "d";
1835 /** Date interval constant 
1836 * @static 
1837 * @type String */
1838 Date.MONTH = "mo";
1839 /** Date interval constant 
1840 * @static 
1841 * @type String */
1842 Date.YEAR = "y";
1843
1844 /**
1845  * Provides a convenient method of performing basic date arithmetic.  This method
1846  * does not modify the Date instance being called - it creates and returns
1847  * a new Date instance containing the resulting date value.
1848  *
1849  * Examples:
1850  * <pre><code>
1851 //Basic usage:
1852 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1853 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1854
1855 //Negative values will subtract correctly:
1856 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1857 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1858
1859 //You can even chain several calls together in one line!
1860 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1861 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1862  </code></pre>
1863  *
1864  * @param {String} interval   A valid date interval enum value
1865  * @param {Number} value      The amount to add to the current date
1866  * @return {Date} The new Date instance
1867  */
1868 Date.prototype.add = function(interval, value){
1869   var d = this.clone();
1870   if (!interval || value === 0) { return d; }
1871   switch(interval.toLowerCase()){
1872     case Date.MILLI:
1873       d.setMilliseconds(this.getMilliseconds() + value);
1874       break;
1875     case Date.SECOND:
1876       d.setSeconds(this.getSeconds() + value);
1877       break;
1878     case Date.MINUTE:
1879       d.setMinutes(this.getMinutes() + value);
1880       break;
1881     case Date.HOUR:
1882       d.setHours(this.getHours() + value);
1883       break;
1884     case Date.DAY:
1885       d.setDate(this.getDate() + value);
1886       break;
1887     case Date.MONTH:
1888       var day = this.getDate();
1889       if(day > 28){
1890           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1891       }
1892       d.setDate(day);
1893       d.setMonth(this.getMonth() + value);
1894       break;
1895     case Date.YEAR:
1896       d.setFullYear(this.getFullYear() + value);
1897       break;
1898   }
1899   return d;
1900 };
1901 /**
1902  * @class Roo.lib.Dom
1903  * @licence LGPL
1904  * @static
1905  * 
1906  * Dom utils (from YIU afaik)
1907  *
1908  * 
1909  **/
1910 Roo.lib.Dom = {
1911     /**
1912      * Get the view width
1913      * @param {Boolean} full True will get the full document, otherwise it's the view width
1914      * @return {Number} The width
1915      */
1916      
1917     getViewWidth : function(full) {
1918         return full ? this.getDocumentWidth() : this.getViewportWidth();
1919     },
1920     /**
1921      * Get the view height
1922      * @param {Boolean} full True will get the full document, otherwise it's the view height
1923      * @return {Number} The height
1924      */
1925     getViewHeight : function(full) {
1926         return full ? this.getDocumentHeight() : this.getViewportHeight();
1927     },
1928     /**
1929      * Get the Full Document height 
1930      * @return {Number} The height
1931      */
1932     getDocumentHeight: function() {
1933         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1934         return Math.max(scrollHeight, this.getViewportHeight());
1935     },
1936     /**
1937      * Get the Full Document width
1938      * @return {Number} The width
1939      */
1940     getDocumentWidth: function() {
1941         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1942         return Math.max(scrollWidth, this.getViewportWidth());
1943     },
1944     /**
1945      * Get the Window Viewport height
1946      * @return {Number} The height
1947      */
1948     getViewportHeight: function() {
1949         var height = self.innerHeight;
1950         var mode = document.compatMode;
1951
1952         if ((mode || Roo.isIE) && !Roo.isOpera) {
1953             height = (mode == "CSS1Compat") ?
1954                      document.documentElement.clientHeight :
1955                      document.body.clientHeight;
1956         }
1957
1958         return height;
1959     },
1960     /**
1961      * Get the Window Viewport width
1962      * @return {Number} The width
1963      */
1964     getViewportWidth: function() {
1965         var width = self.innerWidth;
1966         var mode = document.compatMode;
1967
1968         if (mode || Roo.isIE) {
1969             width = (mode == "CSS1Compat") ?
1970                     document.documentElement.clientWidth :
1971                     document.body.clientWidth;
1972         }
1973         return width;
1974     },
1975
1976     isAncestor : function(p, c) {
1977         p = Roo.getDom(p);
1978         c = Roo.getDom(c);
1979         if (!p || !c) {
1980             return false;
1981         }
1982
1983         if (p.contains && !Roo.isSafari) {
1984             return p.contains(c);
1985         } else if (p.compareDocumentPosition) {
1986             return !!(p.compareDocumentPosition(c) & 16);
1987         } else {
1988             var parent = c.parentNode;
1989             while (parent) {
1990                 if (parent == p) {
1991                     return true;
1992                 }
1993                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1994                     return false;
1995                 }
1996                 parent = parent.parentNode;
1997             }
1998             return false;
1999         }
2000     },
2001
2002     getRegion : function(el) {
2003         return Roo.lib.Region.getRegion(el);
2004     },
2005
2006     getY : function(el) {
2007         return this.getXY(el)[1];
2008     },
2009
2010     getX : function(el) {
2011         return this.getXY(el)[0];
2012     },
2013
2014     getXY : function(el) {
2015         var p, pe, b, scroll, bd = document.body;
2016         el = Roo.getDom(el);
2017         var fly = Roo.lib.AnimBase.fly;
2018         if (el.getBoundingClientRect) {
2019             b = el.getBoundingClientRect();
2020             scroll = fly(document).getScroll();
2021             return [b.left + scroll.left, b.top + scroll.top];
2022         }
2023         var x = 0, y = 0;
2024
2025         p = el;
2026
2027         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2028
2029         while (p) {
2030
2031             x += p.offsetLeft;
2032             y += p.offsetTop;
2033
2034             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2035                 hasAbsolute = true;
2036             }
2037
2038             if (Roo.isGecko) {
2039                 pe = fly(p);
2040
2041                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2042                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2043
2044
2045                 x += bl;
2046                 y += bt;
2047
2048
2049                 if (p != el && pe.getStyle('overflow') != 'visible') {
2050                     x += bl;
2051                     y += bt;
2052                 }
2053             }
2054             p = p.offsetParent;
2055         }
2056
2057         if (Roo.isSafari && hasAbsolute) {
2058             x -= bd.offsetLeft;
2059             y -= bd.offsetTop;
2060         }
2061
2062         if (Roo.isGecko && !hasAbsolute) {
2063             var dbd = fly(bd);
2064             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2065             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2066         }
2067
2068         p = el.parentNode;
2069         while (p && p != bd) {
2070             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2071                 x -= p.scrollLeft;
2072                 y -= p.scrollTop;
2073             }
2074             p = p.parentNode;
2075         }
2076         return [x, y];
2077     },
2078  
2079   
2080
2081
2082     setXY : function(el, xy) {
2083         el = Roo.fly(el, '_setXY');
2084         el.position();
2085         var pts = el.translatePoints(xy);
2086         if (xy[0] !== false) {
2087             el.dom.style.left = pts.left + "px";
2088         }
2089         if (xy[1] !== false) {
2090             el.dom.style.top = pts.top + "px";
2091         }
2092     },
2093
2094     setX : function(el, x) {
2095         this.setXY(el, [x, false]);
2096     },
2097
2098     setY : function(el, y) {
2099         this.setXY(el, [false, y]);
2100     }
2101 };
2102 /*
2103  * Portions of this file are based on pieces of Yahoo User Interface Library
2104  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2105  * YUI licensed under the BSD License:
2106  * http://developer.yahoo.net/yui/license.txt
2107  * <script type="text/javascript">
2108  *
2109  */
2110
2111 Roo.lib.Event = function() {
2112     var loadComplete = false;
2113     var listeners = [];
2114     var unloadListeners = [];
2115     var retryCount = 0;
2116     var onAvailStack = [];
2117     var counter = 0;
2118     var lastError = null;
2119
2120     return {
2121         POLL_RETRYS: 200,
2122         POLL_INTERVAL: 20,
2123         EL: 0,
2124         TYPE: 1,
2125         FN: 2,
2126         WFN: 3,
2127         OBJ: 3,
2128         ADJ_SCOPE: 4,
2129         _interval: null,
2130
2131         startInterval: function() {
2132             if (!this._interval) {
2133                 var self = this;
2134                 var callback = function() {
2135                     self._tryPreloadAttach();
2136                 };
2137                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2138
2139             }
2140         },
2141
2142         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2143             onAvailStack.push({ id:         p_id,
2144                 fn:         p_fn,
2145                 obj:        p_obj,
2146                 override:   p_override,
2147                 checkReady: false    });
2148
2149             retryCount = this.POLL_RETRYS;
2150             this.startInterval();
2151         },
2152
2153
2154         addListener: function(el, eventName, fn) {
2155             el = Roo.getDom(el);
2156             if (!el || !fn) {
2157                 return false;
2158             }
2159
2160             if ("unload" == eventName) {
2161                 unloadListeners[unloadListeners.length] =
2162                 [el, eventName, fn];
2163                 return true;
2164             }
2165
2166             var wrappedFn = function(e) {
2167                 return fn(Roo.lib.Event.getEvent(e));
2168             };
2169
2170             var li = [el, eventName, fn, wrappedFn];
2171
2172             var index = listeners.length;
2173             listeners[index] = li;
2174
2175             this.doAdd(el, eventName, wrappedFn, false);
2176             return true;
2177
2178         },
2179
2180
2181         removeListener: function(el, eventName, fn) {
2182             var i, len;
2183
2184             el = Roo.getDom(el);
2185
2186             if(!fn) {
2187                 return this.purgeElement(el, false, eventName);
2188             }
2189
2190
2191             if ("unload" == eventName) {
2192
2193                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2194                     var li = unloadListeners[i];
2195                     if (li &&
2196                         li[0] == el &&
2197                         li[1] == eventName &&
2198                         li[2] == fn) {
2199                         unloadListeners.splice(i, 1);
2200                         return true;
2201                     }
2202                 }
2203
2204                 return false;
2205             }
2206
2207             var cacheItem = null;
2208
2209
2210             var index = arguments[3];
2211
2212             if ("undefined" == typeof index) {
2213                 index = this._getCacheIndex(el, eventName, fn);
2214             }
2215
2216             if (index >= 0) {
2217                 cacheItem = listeners[index];
2218             }
2219
2220             if (!el || !cacheItem) {
2221                 return false;
2222             }
2223
2224             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2225
2226             delete listeners[index][this.WFN];
2227             delete listeners[index][this.FN];
2228             listeners.splice(index, 1);
2229
2230             return true;
2231
2232         },
2233
2234
2235         getTarget: function(ev, resolveTextNode) {
2236             ev = ev.browserEvent || ev;
2237             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2238             var t = ev.target || ev.srcElement;
2239             return this.resolveTextNode(t);
2240         },
2241
2242
2243         resolveTextNode: function(node) {
2244             if (Roo.isSafari && node && 3 == node.nodeType) {
2245                 return node.parentNode;
2246             } else {
2247                 return node;
2248             }
2249         },
2250
2251
2252         getPageX: function(ev) {
2253             ev = ev.browserEvent || ev;
2254             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2255             var x = ev.pageX;
2256             if (!x && 0 !== x) {
2257                 x = ev.clientX || 0;
2258
2259                 if (Roo.isIE) {
2260                     x += this.getScroll()[1];
2261                 }
2262             }
2263
2264             return x;
2265         },
2266
2267
2268         getPageY: function(ev) {
2269             ev = ev.browserEvent || ev;
2270             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2271             var y = ev.pageY;
2272             if (!y && 0 !== y) {
2273                 y = ev.clientY || 0;
2274
2275                 if (Roo.isIE) {
2276                     y += this.getScroll()[0];
2277                 }
2278             }
2279
2280
2281             return y;
2282         },
2283
2284
2285         getXY: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             return [this.getPageX(ev), this.getPageY(ev)];
2289         },
2290
2291
2292         getRelatedTarget: function(ev) {
2293             ev = ev.browserEvent || ev;
2294             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2295             var t = ev.relatedTarget;
2296             if (!t) {
2297                 if (ev.type == "mouseout") {
2298                     t = ev.toElement;
2299                 } else if (ev.type == "mouseover") {
2300                     t = ev.fromElement;
2301                 }
2302             }
2303
2304             return this.resolveTextNode(t);
2305         },
2306
2307
2308         getTime: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2311             if (!ev.time) {
2312                 var t = new Date().getTime();
2313                 try {
2314                     ev.time = t;
2315                 } catch(ex) {
2316                     this.lastError = ex;
2317                     return t;
2318                 }
2319             }
2320
2321             return ev.time;
2322         },
2323
2324
2325         stopEvent: function(ev) {
2326             this.stopPropagation(ev);
2327             this.preventDefault(ev);
2328         },
2329
2330
2331         stopPropagation: function(ev) {
2332             ev = ev.browserEvent || ev;
2333             if (ev.stopPropagation) {
2334                 ev.stopPropagation();
2335             } else {
2336                 ev.cancelBubble = true;
2337             }
2338         },
2339
2340
2341         preventDefault: function(ev) {
2342             ev = ev.browserEvent || ev;
2343             if(ev.preventDefault) {
2344                 ev.preventDefault();
2345             } else {
2346                 ev.returnValue = false;
2347             }
2348         },
2349
2350
2351         getEvent: function(e) {
2352             var ev = e || window.event;
2353             if (!ev) {
2354                 var c = this.getEvent.caller;
2355                 while (c) {
2356                     ev = c.arguments[0];
2357                     if (ev && Event == ev.constructor) {
2358                         break;
2359                     }
2360                     c = c.caller;
2361                 }
2362             }
2363             return ev;
2364         },
2365
2366
2367         getCharCode: function(ev) {
2368             ev = ev.browserEvent || ev;
2369             return ev.charCode || ev.keyCode || 0;
2370         },
2371
2372
2373         _getCacheIndex: function(el, eventName, fn) {
2374             for (var i = 0,len = listeners.length; i < len; ++i) {
2375                 var li = listeners[i];
2376                 if (li &&
2377                     li[this.FN] == fn &&
2378                     li[this.EL] == el &&
2379                     li[this.TYPE] == eventName) {
2380                     return i;
2381                 }
2382             }
2383
2384             return -1;
2385         },
2386
2387
2388         elCache: {},
2389
2390
2391         getEl: function(id) {
2392             return document.getElementById(id);
2393         },
2394
2395
2396         clearCache: function() {
2397         },
2398
2399
2400         _load: function(e) {
2401             loadComplete = true;
2402             var EU = Roo.lib.Event;
2403
2404
2405             if (Roo.isIE) {
2406                 EU.doRemove(window, "load", EU._load);
2407             }
2408         },
2409
2410
2411         _tryPreloadAttach: function() {
2412
2413             if (this.locked) {
2414                 return false;
2415             }
2416
2417             this.locked = true;
2418
2419
2420             var tryAgain = !loadComplete;
2421             if (!tryAgain) {
2422                 tryAgain = (retryCount > 0);
2423             }
2424
2425
2426             var notAvail = [];
2427             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2428                 var item = onAvailStack[i];
2429                 if (item) {
2430                     var el = this.getEl(item.id);
2431
2432                     if (el) {
2433                         if (!item.checkReady ||
2434                             loadComplete ||
2435                             el.nextSibling ||
2436                             (document && document.body)) {
2437
2438                             var scope = el;
2439                             if (item.override) {
2440                                 if (item.override === true) {
2441                                     scope = item.obj;
2442                                 } else {
2443                                     scope = item.override;
2444                                 }
2445                             }
2446                             item.fn.call(scope, item.obj);
2447                             onAvailStack[i] = null;
2448                         }
2449                     } else {
2450                         notAvail.push(item);
2451                     }
2452                 }
2453             }
2454
2455             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2456
2457             if (tryAgain) {
2458
2459                 this.startInterval();
2460             } else {
2461                 clearInterval(this._interval);
2462                 this._interval = null;
2463             }
2464
2465             this.locked = false;
2466
2467             return true;
2468
2469         },
2470
2471
2472         purgeElement: function(el, recurse, eventName) {
2473             var elListeners = this.getListeners(el, eventName);
2474             if (elListeners) {
2475                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2476                     var l = elListeners[i];
2477                     this.removeListener(el, l.type, l.fn);
2478                 }
2479             }
2480
2481             if (recurse && el && el.childNodes) {
2482                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2483                     this.purgeElement(el.childNodes[i], recurse, eventName);
2484                 }
2485             }
2486         },
2487
2488
2489         getListeners: function(el, eventName) {
2490             var results = [], searchLists;
2491             if (!eventName) {
2492                 searchLists = [listeners, unloadListeners];
2493             } else if (eventName == "unload") {
2494                 searchLists = [unloadListeners];
2495             } else {
2496                 searchLists = [listeners];
2497             }
2498
2499             for (var j = 0; j < searchLists.length; ++j) {
2500                 var searchList = searchLists[j];
2501                 if (searchList && searchList.length > 0) {
2502                     for (var i = 0,len = searchList.length; i < len; ++i) {
2503                         var l = searchList[i];
2504                         if (l && l[this.EL] === el &&
2505                             (!eventName || eventName === l[this.TYPE])) {
2506                             results.push({
2507                                 type:   l[this.TYPE],
2508                                 fn:     l[this.FN],
2509                                 obj:    l[this.OBJ],
2510                                 adjust: l[this.ADJ_SCOPE],
2511                                 index:  i
2512                             });
2513                         }
2514                     }
2515                 }
2516             }
2517
2518             return (results.length) ? results : null;
2519         },
2520
2521
2522         _unload: function(e) {
2523
2524             var EU = Roo.lib.Event, i, j, l, len, index;
2525
2526             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2527                 l = unloadListeners[i];
2528                 if (l) {
2529                     var scope = window;
2530                     if (l[EU.ADJ_SCOPE]) {
2531                         if (l[EU.ADJ_SCOPE] === true) {
2532                             scope = l[EU.OBJ];
2533                         } else {
2534                             scope = l[EU.ADJ_SCOPE];
2535                         }
2536                     }
2537                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2538                     unloadListeners[i] = null;
2539                     l = null;
2540                     scope = null;
2541                 }
2542             }
2543
2544             unloadListeners = null;
2545
2546             if (listeners && listeners.length > 0) {
2547                 j = listeners.length;
2548                 while (j) {
2549                     index = j - 1;
2550                     l = listeners[index];
2551                     if (l) {
2552                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2553                                 l[EU.FN], index);
2554                     }
2555                     j = j - 1;
2556                 }
2557                 l = null;
2558
2559                 EU.clearCache();
2560             }
2561
2562             EU.doRemove(window, "unload", EU._unload);
2563
2564         },
2565
2566
2567         getScroll: function() {
2568             var dd = document.documentElement, db = document.body;
2569             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2570                 return [dd.scrollTop, dd.scrollLeft];
2571             } else if (db) {
2572                 return [db.scrollTop, db.scrollLeft];
2573             } else {
2574                 return [0, 0];
2575             }
2576         },
2577
2578
2579         doAdd: function () {
2580             if (window.addEventListener) {
2581                 return function(el, eventName, fn, capture) {
2582                     el.addEventListener(eventName, fn, (capture));
2583                 };
2584             } else if (window.attachEvent) {
2585                 return function(el, eventName, fn, capture) {
2586                     el.attachEvent("on" + eventName, fn);
2587                 };
2588             } else {
2589                 return function() {
2590                 };
2591             }
2592         }(),
2593
2594
2595         doRemove: function() {
2596             if (window.removeEventListener) {
2597                 return function (el, eventName, fn, capture) {
2598                     el.removeEventListener(eventName, fn, (capture));
2599                 };
2600             } else if (window.detachEvent) {
2601                 return function (el, eventName, fn) {
2602                     el.detachEvent("on" + eventName, fn);
2603                 };
2604             } else {
2605                 return function() {
2606                 };
2607             }
2608         }()
2609     };
2610     
2611 }();
2612 (function() {     
2613    
2614     var E = Roo.lib.Event;
2615     E.on = E.addListener;
2616     E.un = E.removeListener;
2617
2618     if (document && document.body) {
2619         E._load();
2620     } else {
2621         E.doAdd(window, "load", E._load);
2622     }
2623     E.doAdd(window, "unload", E._unload);
2624     E._tryPreloadAttach();
2625 })();
2626
2627  
2628
2629 (function() {
2630     /**
2631      * @class Roo.lib.Ajax
2632      *
2633      * provide a simple Ajax request utility functions
2634      * 
2635      * Portions of this file are based on pieces of Yahoo User Interface Library
2636     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2637     * YUI licensed under the BSD License:
2638     * http://developer.yahoo.net/yui/license.txt
2639     * <script type="text/javascript">
2640     *
2641      *
2642      */
2643     Roo.lib.Ajax = {
2644         /**
2645          * @static 
2646          */
2647         request : function(method, uri, cb, data, options) {
2648             if(options){
2649                 var hs = options.headers;
2650                 if(hs){
2651                     for(var h in hs){
2652                         if(hs.hasOwnProperty(h)){
2653                             this.initHeader(h, hs[h], false);
2654                         }
2655                     }
2656                 }
2657                 if(options.xmlData){
2658                     this.initHeader('Content-Type', 'text/xml', false);
2659                     method = 'POST';
2660                     data = options.xmlData;
2661                 }
2662             }
2663
2664             return this.asyncRequest(method, uri, cb, data);
2665         },
2666         /**
2667          * serialize a form
2668          *
2669          * @static
2670          * @param {DomForm} form element
2671          * @return {String} urlencode form output.
2672          */
2673         serializeForm : function(form) {
2674             if(typeof form == 'string') {
2675                 form = (document.getElementById(form) || document.forms[form]);
2676             }
2677
2678             var el, name, val, disabled, data = '', hasSubmit = false;
2679             for (var i = 0; i < form.elements.length; i++) {
2680                 el = form.elements[i];
2681                 disabled = form.elements[i].disabled;
2682                 name = form.elements[i].name;
2683                 val = form.elements[i].value;
2684
2685                 if (!disabled && name){
2686                     switch (el.type)
2687                             {
2688                         case 'select-one':
2689                         case 'select-multiple':
2690                             for (var j = 0; j < el.options.length; j++) {
2691                                 if (el.options[j].selected) {
2692                                     if (Roo.isIE) {
2693                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2694                                     }
2695                                     else {
2696                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2697                                     }
2698                                 }
2699                             }
2700                             break;
2701                         case 'radio':
2702                         case 'checkbox':
2703                             if (el.checked) {
2704                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2705                             }
2706                             break;
2707                         case 'file':
2708
2709                         case undefined:
2710
2711                         case 'reset':
2712
2713                         case 'button':
2714
2715                             break;
2716                         case 'submit':
2717                             if(hasSubmit == false) {
2718                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2719                                 hasSubmit = true;
2720                             }
2721                             break;
2722                         default:
2723                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2724                             break;
2725                     }
2726                 }
2727             }
2728             data = data.substr(0, data.length - 1);
2729             return data;
2730         },
2731
2732         headers:{},
2733
2734         hasHeaders:false,
2735
2736         useDefaultHeader:true,
2737
2738         defaultPostHeader:'application/x-www-form-urlencoded',
2739
2740         useDefaultXhrHeader:true,
2741
2742         defaultXhrHeader:'XMLHttpRequest',
2743
2744         hasDefaultHeaders:true,
2745
2746         defaultHeaders:{},
2747
2748         poll:{},
2749
2750         timeout:{},
2751
2752         pollInterval:50,
2753
2754         transactionId:0,
2755
2756         setProgId:function(id)
2757         {
2758             this.activeX.unshift(id);
2759         },
2760
2761         setDefaultPostHeader:function(b)
2762         {
2763             this.useDefaultHeader = b;
2764         },
2765
2766         setDefaultXhrHeader:function(b)
2767         {
2768             this.useDefaultXhrHeader = b;
2769         },
2770
2771         setPollingInterval:function(i)
2772         {
2773             if (typeof i == 'number' && isFinite(i)) {
2774                 this.pollInterval = i;
2775             }
2776         },
2777
2778         createXhrObject:function(transactionId)
2779         {
2780             var obj,http;
2781             try
2782             {
2783
2784                 http = new XMLHttpRequest();
2785
2786                 obj = { conn:http, tId:transactionId };
2787             }
2788             catch(e)
2789             {
2790                 for (var i = 0; i < this.activeX.length; ++i) {
2791                     try
2792                     {
2793
2794                         http = new ActiveXObject(this.activeX[i]);
2795
2796                         obj = { conn:http, tId:transactionId };
2797                         break;
2798                     }
2799                     catch(e) {
2800                     }
2801                 }
2802             }
2803             finally
2804             {
2805                 return obj;
2806             }
2807         },
2808
2809         getConnectionObject:function()
2810         {
2811             var o;
2812             var tId = this.transactionId;
2813
2814             try
2815             {
2816                 o = this.createXhrObject(tId);
2817                 if (o) {
2818                     this.transactionId++;
2819                 }
2820             }
2821             catch(e) {
2822             }
2823             finally
2824             {
2825                 return o;
2826             }
2827         },
2828
2829         asyncRequest:function(method, uri, callback, postData)
2830         {
2831             var o = this.getConnectionObject();
2832
2833             if (!o) {
2834                 return null;
2835             }
2836             else {
2837                 o.conn.open(method, uri, true);
2838
2839                 if (this.useDefaultXhrHeader) {
2840                     if (!this.defaultHeaders['X-Requested-With']) {
2841                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2842                     }
2843                 }
2844
2845                 if(postData && this.useDefaultHeader){
2846                     this.initHeader('Content-Type', this.defaultPostHeader);
2847                 }
2848
2849                  if (this.hasDefaultHeaders || this.hasHeaders) {
2850                     this.setHeader(o);
2851                 }
2852
2853                 this.handleReadyState(o, callback);
2854                 o.conn.send(postData || null);
2855
2856                 return o;
2857             }
2858         },
2859
2860         handleReadyState:function(o, callback)
2861         {
2862             var oConn = this;
2863
2864             if (callback && callback.timeout) {
2865                 
2866                 this.timeout[o.tId] = window.setTimeout(function() {
2867                     oConn.abort(o, callback, true);
2868                 }, callback.timeout);
2869             }
2870
2871             this.poll[o.tId] = window.setInterval(
2872                     function() {
2873                         if (o.conn && o.conn.readyState == 4) {
2874                             window.clearInterval(oConn.poll[o.tId]);
2875                             delete oConn.poll[o.tId];
2876
2877                             if(callback && callback.timeout) {
2878                                 window.clearTimeout(oConn.timeout[o.tId]);
2879                                 delete oConn.timeout[o.tId];
2880                             }
2881
2882                             oConn.handleTransactionResponse(o, callback);
2883                         }
2884                     }
2885                     , this.pollInterval);
2886         },
2887
2888         handleTransactionResponse:function(o, callback, isAbort)
2889         {
2890
2891             if (!callback) {
2892                 this.releaseObject(o);
2893                 return;
2894             }
2895
2896             var httpStatus, responseObject;
2897
2898             try
2899             {
2900                 if (o.conn.status !== undefined && o.conn.status != 0) {
2901                     httpStatus = o.conn.status;
2902                 }
2903                 else {
2904                     httpStatus = 13030;
2905                 }
2906             }
2907             catch(e) {
2908
2909
2910                 httpStatus = 13030;
2911             }
2912
2913             if (httpStatus >= 200 && httpStatus < 300) {
2914                 responseObject = this.createResponseObject(o, callback.argument);
2915                 if (callback.success) {
2916                     if (!callback.scope) {
2917                         callback.success(responseObject);
2918                     }
2919                     else {
2920
2921
2922                         callback.success.apply(callback.scope, [responseObject]);
2923                     }
2924                 }
2925             }
2926             else {
2927                 switch (httpStatus) {
2928
2929                     case 12002:
2930                     case 12029:
2931                     case 12030:
2932                     case 12031:
2933                     case 12152:
2934                     case 13030:
2935                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2936                         if (callback.failure) {
2937                             if (!callback.scope) {
2938                                 callback.failure(responseObject);
2939                             }
2940                             else {
2941                                 callback.failure.apply(callback.scope, [responseObject]);
2942                             }
2943                         }
2944                         break;
2945                     default:
2946                         responseObject = this.createResponseObject(o, callback.argument);
2947                         if (callback.failure) {
2948                             if (!callback.scope) {
2949                                 callback.failure(responseObject);
2950                             }
2951                             else {
2952                                 callback.failure.apply(callback.scope, [responseObject]);
2953                             }
2954                         }
2955                 }
2956             }
2957
2958             this.releaseObject(o);
2959             responseObject = null;
2960         },
2961
2962         createResponseObject:function(o, callbackArg)
2963         {
2964             var obj = {};
2965             var headerObj = {};
2966
2967             try
2968             {
2969                 var headerStr = o.conn.getAllResponseHeaders();
2970                 var header = headerStr.split('\n');
2971                 for (var i = 0; i < header.length; i++) {
2972                     var delimitPos = header[i].indexOf(':');
2973                     if (delimitPos != -1) {
2974                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2975                     }
2976                 }
2977             }
2978             catch(e) {
2979             }
2980
2981             obj.tId = o.tId;
2982             obj.status = o.conn.status;
2983             obj.statusText = o.conn.statusText;
2984             obj.getResponseHeader = headerObj;
2985             obj.getAllResponseHeaders = headerStr;
2986             obj.responseText = o.conn.responseText;
2987             obj.responseXML = o.conn.responseXML;
2988
2989             if (typeof callbackArg !== undefined) {
2990                 obj.argument = callbackArg;
2991             }
2992
2993             return obj;
2994         },
2995
2996         createExceptionObject:function(tId, callbackArg, isAbort)
2997         {
2998             var COMM_CODE = 0;
2999             var COMM_ERROR = 'communication failure';
3000             var ABORT_CODE = -1;
3001             var ABORT_ERROR = 'transaction aborted';
3002
3003             var obj = {};
3004
3005             obj.tId = tId;
3006             if (isAbort) {
3007                 obj.status = ABORT_CODE;
3008                 obj.statusText = ABORT_ERROR;
3009             }
3010             else {
3011                 obj.status = COMM_CODE;
3012                 obj.statusText = COMM_ERROR;
3013             }
3014
3015             if (callbackArg) {
3016                 obj.argument = callbackArg;
3017             }
3018
3019             return obj;
3020         },
3021
3022         initHeader:function(label, value, isDefault)
3023         {
3024             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3025
3026             if (headerObj[label] === undefined) {
3027                 headerObj[label] = value;
3028             }
3029             else {
3030
3031
3032                 headerObj[label] = value + "," + headerObj[label];
3033             }
3034
3035             if (isDefault) {
3036                 this.hasDefaultHeaders = true;
3037             }
3038             else {
3039                 this.hasHeaders = true;
3040             }
3041         },
3042
3043
3044         setHeader:function(o)
3045         {
3046             if (this.hasDefaultHeaders) {
3047                 for (var prop in this.defaultHeaders) {
3048                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3049                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3050                     }
3051                 }
3052             }
3053
3054             if (this.hasHeaders) {
3055                 for (var prop in this.headers) {
3056                     if (this.headers.hasOwnProperty(prop)) {
3057                         o.conn.setRequestHeader(prop, this.headers[prop]);
3058                     }
3059                 }
3060                 this.headers = {};
3061                 this.hasHeaders = false;
3062             }
3063         },
3064
3065         resetDefaultHeaders:function() {
3066             delete this.defaultHeaders;
3067             this.defaultHeaders = {};
3068             this.hasDefaultHeaders = false;
3069         },
3070
3071         abort:function(o, callback, isTimeout)
3072         {
3073             if(this.isCallInProgress(o)) {
3074                 o.conn.abort();
3075                 window.clearInterval(this.poll[o.tId]);
3076                 delete this.poll[o.tId];
3077                 if (isTimeout) {
3078                     delete this.timeout[o.tId];
3079                 }
3080
3081                 this.handleTransactionResponse(o, callback, true);
3082
3083                 return true;
3084             }
3085             else {
3086                 return false;
3087             }
3088         },
3089
3090
3091         isCallInProgress:function(o)
3092         {
3093             if (o && o.conn) {
3094                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3095             }
3096             else {
3097
3098                 return false;
3099             }
3100         },
3101
3102
3103         releaseObject:function(o)
3104         {
3105
3106             o.conn = null;
3107
3108             o = null;
3109         },
3110
3111         activeX:[
3112         'MSXML2.XMLHTTP.3.0',
3113         'MSXML2.XMLHTTP',
3114         'Microsoft.XMLHTTP'
3115         ]
3116
3117
3118     };
3119 })();/*
3120  * Portions of this file are based on pieces of Yahoo User Interface Library
3121  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3122  * YUI licensed under the BSD License:
3123  * http://developer.yahoo.net/yui/license.txt
3124  * <script type="text/javascript">
3125  *
3126  */
3127
3128 Roo.lib.Region = function(t, r, b, l) {
3129     this.top = t;
3130     this[1] = t;
3131     this.right = r;
3132     this.bottom = b;
3133     this.left = l;
3134     this[0] = l;
3135 };
3136
3137
3138 Roo.lib.Region.prototype = {
3139     contains : function(region) {
3140         return ( region.left >= this.left &&
3141                  region.right <= this.right &&
3142                  region.top >= this.top &&
3143                  region.bottom <= this.bottom    );
3144
3145     },
3146
3147     getArea : function() {
3148         return ( (this.bottom - this.top) * (this.right - this.left) );
3149     },
3150
3151     intersect : function(region) {
3152         var t = Math.max(this.top, region.top);
3153         var r = Math.min(this.right, region.right);
3154         var b = Math.min(this.bottom, region.bottom);
3155         var l = Math.max(this.left, region.left);
3156
3157         if (b >= t && r >= l) {
3158             return new Roo.lib.Region(t, r, b, l);
3159         } else {
3160             return null;
3161         }
3162     },
3163     union : function(region) {
3164         var t = Math.min(this.top, region.top);
3165         var r = Math.max(this.right, region.right);
3166         var b = Math.max(this.bottom, region.bottom);
3167         var l = Math.min(this.left, region.left);
3168
3169         return new Roo.lib.Region(t, r, b, l);
3170     },
3171
3172     adjust : function(t, l, b, r) {
3173         this.top += t;
3174         this.left += l;
3175         this.right += r;
3176         this.bottom += b;
3177         return this;
3178     }
3179 };
3180
3181 Roo.lib.Region.getRegion = function(el) {
3182     var p = Roo.lib.Dom.getXY(el);
3183
3184     var t = p[1];
3185     var r = p[0] + el.offsetWidth;
3186     var b = p[1] + el.offsetHeight;
3187     var l = p[0];
3188
3189     return new Roo.lib.Region(t, r, b, l);
3190 };
3191 /*
3192  * Portions of this file are based on pieces of Yahoo User Interface Library
3193  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3194  * YUI licensed under the BSD License:
3195  * http://developer.yahoo.net/yui/license.txt
3196  * <script type="text/javascript">
3197  *
3198  */
3199 //@@dep Roo.lib.Region
3200
3201
3202 Roo.lib.Point = function(x, y) {
3203     if (x instanceof Array) {
3204         y = x[1];
3205         x = x[0];
3206     }
3207     this.x = this.right = this.left = this[0] = x;
3208     this.y = this.top = this.bottom = this[1] = y;
3209 };
3210
3211 Roo.lib.Point.prototype = new Roo.lib.Region();
3212 /*
3213  * Portions of this file are based on pieces of Yahoo User Interface Library
3214  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3215  * YUI licensed under the BSD License:
3216  * http://developer.yahoo.net/yui/license.txt
3217  * <script type="text/javascript">
3218  *
3219  */
3220  
3221 (function() {   
3222
3223     Roo.lib.Anim = {
3224         scroll : function(el, args, duration, easing, cb, scope) {
3225             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3226         },
3227
3228         motion : function(el, args, duration, easing, cb, scope) {
3229             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3230         },
3231
3232         color : function(el, args, duration, easing, cb, scope) {
3233             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3234         },
3235
3236         run : function(el, args, duration, easing, cb, scope, type) {
3237             type = type || Roo.lib.AnimBase;
3238             if (typeof easing == "string") {
3239                 easing = Roo.lib.Easing[easing];
3240             }
3241             var anim = new type(el, args, duration, easing);
3242             anim.animateX(function() {
3243                 Roo.callback(cb, scope);
3244             });
3245             return anim;
3246         }
3247     };
3248 })();/*
3249  * Portions of this file are based on pieces of Yahoo User Interface Library
3250  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3251  * YUI licensed under the BSD License:
3252  * http://developer.yahoo.net/yui/license.txt
3253  * <script type="text/javascript">
3254  *
3255  */
3256
3257 (function() {    
3258     var libFlyweight;
3259     
3260     function fly(el) {
3261         if (!libFlyweight) {
3262             libFlyweight = new Roo.Element.Flyweight();
3263         }
3264         libFlyweight.dom = el;
3265         return libFlyweight;
3266     }
3267
3268     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3269     
3270    
3271     
3272     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3273         if (el) {
3274             this.init(el, attributes, duration, method);
3275         }
3276     };
3277
3278     Roo.lib.AnimBase.fly = fly;
3279     
3280     
3281     
3282     Roo.lib.AnimBase.prototype = {
3283
3284         toString: function() {
3285             var el = this.getEl();
3286             var id = el.id || el.tagName;
3287             return ("Anim " + id);
3288         },
3289
3290         patterns: {
3291             noNegatives:        /width|height|opacity|padding/i,
3292             offsetAttribute:  /^((width|height)|(top|left))$/,
3293             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3294             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3295         },
3296
3297
3298         doMethod: function(attr, start, end) {
3299             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3300         },
3301
3302
3303         setAttribute: function(attr, val, unit) {
3304             if (this.patterns.noNegatives.test(attr)) {
3305                 val = (val > 0) ? val : 0;
3306             }
3307
3308             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3309         },
3310
3311
3312         getAttribute: function(attr) {
3313             var el = this.getEl();
3314             var val = fly(el).getStyle(attr);
3315
3316             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3317                 return parseFloat(val);
3318             }
3319
3320             var a = this.patterns.offsetAttribute.exec(attr) || [];
3321             var pos = !!( a[3] );
3322             var box = !!( a[2] );
3323
3324
3325             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3326                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3327             } else {
3328                 val = 0;
3329             }
3330
3331             return val;
3332         },
3333
3334
3335         getDefaultUnit: function(attr) {
3336             if (this.patterns.defaultUnit.test(attr)) {
3337                 return 'px';
3338             }
3339
3340             return '';
3341         },
3342
3343         animateX : function(callback, scope) {
3344             var f = function() {
3345                 this.onComplete.removeListener(f);
3346                 if (typeof callback == "function") {
3347                     callback.call(scope || this, this);
3348                 }
3349             };
3350             this.onComplete.addListener(f, this);
3351             this.animate();
3352         },
3353
3354
3355         setRuntimeAttribute: function(attr) {
3356             var start;
3357             var end;
3358             var attributes = this.attributes;
3359
3360             this.runtimeAttributes[attr] = {};
3361
3362             var isset = function(prop) {
3363                 return (typeof prop !== 'undefined');
3364             };
3365
3366             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3367                 return false;
3368             }
3369
3370             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3371
3372
3373             if (isset(attributes[attr]['to'])) {
3374                 end = attributes[attr]['to'];
3375             } else if (isset(attributes[attr]['by'])) {
3376                 if (start.constructor == Array) {
3377                     end = [];
3378                     for (var i = 0, len = start.length; i < len; ++i) {
3379                         end[i] = start[i] + attributes[attr]['by'][i];
3380                     }
3381                 } else {
3382                     end = start + attributes[attr]['by'];
3383                 }
3384             }
3385
3386             this.runtimeAttributes[attr].start = start;
3387             this.runtimeAttributes[attr].end = end;
3388
3389
3390             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3391         },
3392
3393
3394         init: function(el, attributes, duration, method) {
3395
3396             var isAnimated = false;
3397
3398
3399             var startTime = null;
3400
3401
3402             var actualFrames = 0;
3403
3404
3405             el = Roo.getDom(el);
3406
3407
3408             this.attributes = attributes || {};
3409
3410
3411             this.duration = duration || 1;
3412
3413
3414             this.method = method || Roo.lib.Easing.easeNone;
3415
3416
3417             this.useSeconds = true;
3418
3419
3420             this.currentFrame = 0;
3421
3422
3423             this.totalFrames = Roo.lib.AnimMgr.fps;
3424
3425
3426             this.getEl = function() {
3427                 return el;
3428             };
3429
3430
3431             this.isAnimated = function() {
3432                 return isAnimated;
3433             };
3434
3435
3436             this.getStartTime = function() {
3437                 return startTime;
3438             };
3439
3440             this.runtimeAttributes = {};
3441
3442
3443             this.animate = function() {
3444                 if (this.isAnimated()) {
3445                     return false;
3446                 }
3447
3448                 this.currentFrame = 0;
3449
3450                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3451
3452                 Roo.lib.AnimMgr.registerElement(this);
3453             };
3454
3455
3456             this.stop = function(finish) {
3457                 if (finish) {
3458                     this.currentFrame = this.totalFrames;
3459                     this._onTween.fire();
3460                 }
3461                 Roo.lib.AnimMgr.stop(this);
3462             };
3463
3464             var onStart = function() {
3465                 this.onStart.fire();
3466
3467                 this.runtimeAttributes = {};
3468                 for (var attr in this.attributes) {
3469                     this.setRuntimeAttribute(attr);
3470                 }
3471
3472                 isAnimated = true;
3473                 actualFrames = 0;
3474                 startTime = new Date();
3475             };
3476
3477
3478             var onTween = function() {
3479                 var data = {
3480                     duration: new Date() - this.getStartTime(),
3481                     currentFrame: this.currentFrame
3482                 };
3483
3484                 data.toString = function() {
3485                     return (
3486                             'duration: ' + data.duration +
3487                             ', currentFrame: ' + data.currentFrame
3488                             );
3489                 };
3490
3491                 this.onTween.fire(data);
3492
3493                 var runtimeAttributes = this.runtimeAttributes;
3494
3495                 for (var attr in runtimeAttributes) {
3496                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3497                 }
3498
3499                 actualFrames += 1;
3500             };
3501
3502             var onComplete = function() {
3503                 var actual_duration = (new Date() - startTime) / 1000 ;
3504
3505                 var data = {
3506                     duration: actual_duration,
3507                     frames: actualFrames,
3508                     fps: actualFrames / actual_duration
3509                 };
3510
3511                 data.toString = function() {
3512                     return (
3513                             'duration: ' + data.duration +
3514                             ', frames: ' + data.frames +
3515                             ', fps: ' + data.fps
3516                             );
3517                 };
3518
3519                 isAnimated = false;
3520                 actualFrames = 0;
3521                 this.onComplete.fire(data);
3522             };
3523
3524
3525             this._onStart = new Roo.util.Event(this);
3526             this.onStart = new Roo.util.Event(this);
3527             this.onTween = new Roo.util.Event(this);
3528             this._onTween = new Roo.util.Event(this);
3529             this.onComplete = new Roo.util.Event(this);
3530             this._onComplete = new Roo.util.Event(this);
3531             this._onStart.addListener(onStart);
3532             this._onTween.addListener(onTween);
3533             this._onComplete.addListener(onComplete);
3534         }
3535     };
3536 })();
3537 /*
3538  * Portions of this file are based on pieces of Yahoo User Interface Library
3539  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3540  * YUI licensed under the BSD License:
3541  * http://developer.yahoo.net/yui/license.txt
3542  * <script type="text/javascript">
3543  *
3544  */
3545
3546 Roo.lib.AnimMgr = new function() {
3547
3548     var thread = null;
3549
3550
3551     var queue = [];
3552
3553
3554     var tweenCount = 0;
3555
3556
3557     this.fps = 1000;
3558
3559
3560     this.delay = 1;
3561
3562
3563     this.registerElement = function(tween) {
3564         queue[queue.length] = tween;
3565         tweenCount += 1;
3566         tween._onStart.fire();
3567         this.start();
3568     };
3569
3570
3571     this.unRegister = function(tween, index) {
3572         tween._onComplete.fire();
3573         index = index || getIndex(tween);
3574         if (index != -1) {
3575             queue.splice(index, 1);
3576         }
3577
3578         tweenCount -= 1;
3579         if (tweenCount <= 0) {
3580             this.stop();
3581         }
3582     };
3583
3584
3585     this.start = function() {
3586         if (thread === null) {
3587             thread = setInterval(this.run, this.delay);
3588         }
3589     };
3590
3591
3592     this.stop = function(tween) {
3593         if (!tween) {
3594             clearInterval(thread);
3595
3596             for (var i = 0, len = queue.length; i < len; ++i) {
3597                 if (queue[0].isAnimated()) {
3598                     this.unRegister(queue[0], 0);
3599                 }
3600             }
3601
3602             queue = [];
3603             thread = null;
3604             tweenCount = 0;
3605         }
3606         else {
3607             this.unRegister(tween);
3608         }
3609     };
3610
3611
3612     this.run = function() {
3613         for (var i = 0, len = queue.length; i < len; ++i) {
3614             var tween = queue[i];
3615             if (!tween || !tween.isAnimated()) {
3616                 continue;
3617             }
3618
3619             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3620             {
3621                 tween.currentFrame += 1;
3622
3623                 if (tween.useSeconds) {
3624                     correctFrame(tween);
3625                 }
3626                 tween._onTween.fire();
3627             }
3628             else {
3629                 Roo.lib.AnimMgr.stop(tween, i);
3630             }
3631         }
3632     };
3633
3634     var getIndex = function(anim) {
3635         for (var i = 0, len = queue.length; i < len; ++i) {
3636             if (queue[i] == anim) {
3637                 return i;
3638             }
3639         }
3640         return -1;
3641     };
3642
3643
3644     var correctFrame = function(tween) {
3645         var frames = tween.totalFrames;
3646         var frame = tween.currentFrame;
3647         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3648         var elapsed = (new Date() - tween.getStartTime());
3649         var tweak = 0;
3650
3651         if (elapsed < tween.duration * 1000) {
3652             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3653         } else {
3654             tweak = frames - (frame + 1);
3655         }
3656         if (tweak > 0 && isFinite(tweak)) {
3657             if (tween.currentFrame + tweak >= frames) {
3658                 tweak = frames - (frame + 1);
3659             }
3660
3661             tween.currentFrame += tweak;
3662         }
3663     };
3664 };
3665
3666     /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Bezier = new function() {
3675
3676         this.getPosition = function(points, t) {
3677             var n = points.length;
3678             var tmp = [];
3679
3680             for (var i = 0; i < n; ++i) {
3681                 tmp[i] = [points[i][0], points[i][1]];
3682             }
3683
3684             for (var j = 1; j < n; ++j) {
3685                 for (i = 0; i < n - j; ++i) {
3686                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3687                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3688                 }
3689             }
3690
3691             return [ tmp[0][0], tmp[0][1] ];
3692
3693         };
3694     }; 
3695
3696 /**
3697  * @class Roo.lib.Color
3698  * @constructor
3699  * An abstract Color implementation. Concrete Color implementations should use
3700  * an instance of this function as their prototype, and implement the getRGB and
3701  * getHSL functions. getRGB should return an object representing the RGB
3702  * components of this Color, with the red, green, and blue components in the
3703  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3704  * return an object representing the HSL components of this Color, with the hue
3705  * component in the range [0,360), the saturation and lightness components in
3706  * the range [0,100], and the alpha component in the range [0,1].
3707  *
3708  *
3709  * Color.js
3710  *
3711  * Functions for Color handling and processing.
3712  *
3713  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3714  *
3715  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3716  * rights to this program, with the intention of it becoming part of the public
3717  * domain. Because this program is released into the public domain, it comes with
3718  * no warranty either expressed or implied, to the extent permitted by law.
3719  * 
3720  * For more free and public domain JavaScript code by the same author, visit:
3721  * http://www.safalra.com/web-design/javascript/
3722  * 
3723  */
3724 Roo.lib.Color = function() { }
3725
3726
3727 Roo.apply(Roo.lib.Color.prototype, {
3728   
3729   rgb : null,
3730   hsv : null,
3731   hsl : null,
3732   
3733   /**
3734    * getIntegerRGB
3735    * @return {Object} an object representing the RGBA components of this Color. The red,
3736    * green, and blue components are converted to integers in the range [0,255].
3737    * The alpha is a value in the range [0,1].
3738    */
3739   getIntegerRGB : function(){
3740
3741     // get the RGB components of this Color
3742     var rgb = this.getRGB();
3743
3744     // return the integer components
3745     return {
3746       'r' : Math.round(rgb.r),
3747       'g' : Math.round(rgb.g),
3748       'b' : Math.round(rgb.b),
3749       'a' : rgb.a
3750     };
3751
3752   },
3753
3754   /**
3755    * getPercentageRGB
3756    * @return {Object} an object representing the RGBA components of this Color. The red,
3757    * green, and blue components are converted to numbers in the range [0,100].
3758    * The alpha is a value in the range [0,1].
3759    */
3760   getPercentageRGB : function(){
3761
3762     // get the RGB components of this Color
3763     var rgb = this.getRGB();
3764
3765     // return the percentage components
3766     return {
3767       'r' : 100 * rgb.r / 255,
3768       'g' : 100 * rgb.g / 255,
3769       'b' : 100 * rgb.b / 255,
3770       'a' : rgb.a
3771     };
3772
3773   },
3774
3775   /**
3776    * getCSSHexadecimalRGB
3777    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3778    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3779    * are two-digit hexadecimal numbers.
3780    */
3781   getCSSHexadecimalRGB : function()
3782   {
3783
3784     // get the integer RGB components
3785     var rgb = this.getIntegerRGB();
3786
3787     // determine the hexadecimal equivalents
3788     var r16 = rgb.r.toString(16);
3789     var g16 = rgb.g.toString(16);
3790     var b16 = rgb.b.toString(16);
3791
3792     // return the CSS RGB Color value
3793     return '#'
3794         + (r16.length == 2 ? r16 : '0' + r16)
3795         + (g16.length == 2 ? g16 : '0' + g16)
3796         + (b16.length == 2 ? b16 : '0' + b16);
3797
3798   },
3799
3800   /**
3801    * getCSSIntegerRGB
3802    * @return {String} a string representing this Color as a CSS integer RGB Color
3803    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3804    * are integers in the range [0,255].
3805    */
3806   getCSSIntegerRGB : function(){
3807
3808     // get the integer RGB components
3809     var rgb = this.getIntegerRGB();
3810
3811     // return the CSS RGB Color value
3812     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3813
3814   },
3815
3816   /**
3817    * getCSSIntegerRGBA
3818    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3819    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3820    * b are integers in the range [0,255] and a is in the range [0,1].
3821    */
3822   getCSSIntegerRGBA : function(){
3823
3824     // get the integer RGB components
3825     var rgb = this.getIntegerRGB();
3826
3827     // return the CSS integer RGBA Color value
3828     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3829
3830   },
3831
3832   /**
3833    * getCSSPercentageRGB
3834    * @return {String} a string representing this Color as a CSS percentage RGB Color
3835    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3836    * b are in the range [0,100].
3837    */
3838   getCSSPercentageRGB : function(){
3839
3840     // get the percentage RGB components
3841     var rgb = this.getPercentageRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3845
3846   },
3847
3848   /**
3849    * getCSSPercentageRGBA
3850    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3851    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3852    * and b are in the range [0,100] and a is in the range [0,1].
3853    */
3854   getCSSPercentageRGBA : function(){
3855
3856     // get the percentage RGB components
3857     var rgb = this.getPercentageRGB();
3858
3859     // return the CSS percentage RGBA Color value
3860     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSHSL
3866    * @return {String} a string representing this Color as a CSS HSL Color value - that
3867    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3868    * s and l are in the range [0,100].
3869    */
3870   getCSSHSL : function(){
3871
3872     // get the HSL components
3873     var hsl = this.getHSL();
3874
3875     // return the CSS HSL Color value
3876     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSHSLA
3882    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3883    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3884    * s and l are in the range [0,100], and a is in the range [0,1].
3885    */
3886   getCSSHSLA : function(){
3887
3888     // get the HSL components
3889     var hsl = this.getHSL();
3890
3891     // return the CSS HSL Color value
3892     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3893
3894   },
3895
3896   /**
3897    * Sets the Color of the specified node to this Color. This functions sets
3898    * the CSS 'color' property for the node. The parameter is:
3899    * 
3900    * @param {DomElement} node - the node whose Color should be set
3901    */
3902   setNodeColor : function(node){
3903
3904     // set the Color of the node
3905     node.style.color = this.getCSSHexadecimalRGB();
3906
3907   },
3908
3909   /**
3910    * Sets the background Color of the specified node to this Color. This
3911    * functions sets the CSS 'background-color' property for the node. The
3912    * parameter is:
3913    *
3914    * @param {DomElement} node - the node whose background Color should be set
3915    */
3916   setNodeBackgroundColor : function(node){
3917
3918     // set the background Color of the node
3919     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3920
3921   },
3922   // convert between formats..
3923   toRGB: function()
3924   {
3925     var r = this.getIntegerRGB();
3926     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3927     
3928   },
3929   toHSL : function()
3930   {
3931      var hsl = this.getHSL();
3932   // return the CSS HSL Color value
3933     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3934     
3935   },
3936   
3937   toHSV : function()
3938   {
3939     var rgb = this.toRGB();
3940     var hsv = rgb.getHSV();
3941    // return the CSS HSL Color value
3942     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3943     
3944   },
3945   
3946   // modify  v = 0 ... 1 (eg. 0.5)
3947   saturate : function(v)
3948   {
3949       var rgb = this.toRGB();
3950       var hsv = rgb.getHSV();
3951       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3952       
3953   
3954   },
3955   
3956    
3957   /**
3958    * getRGB
3959    * @return {Object} the RGB and alpha components of this Color as an object with r,
3960    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3961    * the range [0,1].
3962    */
3963   getRGB: function(){
3964    
3965     // return the RGB components
3966     return {
3967       'r' : this.rgb.r,
3968       'g' : this.rgb.g,
3969       'b' : this.rgb.b,
3970       'a' : this.alpha
3971     };
3972
3973   },
3974
3975   /**
3976    * getHSV
3977    * @return {Object} the HSV and alpha components of this Color as an object with h,
3978    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3979    * [0,100], and a is in the range [0,1].
3980    */
3981   getHSV : function()
3982   {
3983     
3984     // calculate the HSV components if necessary
3985     if (this.hsv == null) {
3986       this.calculateHSV();
3987     }
3988
3989     // return the HSV components
3990     return {
3991       'h' : this.hsv.h,
3992       's' : this.hsv.s,
3993       'v' : this.hsv.v,
3994       'a' : this.alpha
3995     };
3996
3997   },
3998
3999   /**
4000    * getHSL
4001    * @return {Object} the HSL and alpha components of this Color as an object with h,
4002    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4003    * [0,100], and a is in the range [0,1].
4004    */
4005   getHSL : function(){
4006     
4007      
4008     // calculate the HSV components if necessary
4009     if (this.hsl == null) { this.calculateHSL(); }
4010
4011     // return the HSL components
4012     return {
4013       'h' : this.hsl.h,
4014       's' : this.hsl.s,
4015       'l' : this.hsl.l,
4016       'a' : this.alpha
4017     };
4018
4019   }
4020   
4021
4022 });
4023
4024
4025 /**
4026  * @class Roo.lib.RGBColor
4027  * @extends Roo.lib.Color
4028  * Creates a Color specified in the RGB Color space, with an optional alpha
4029  * component. The parameters are:
4030  * @constructor
4031  * 
4032
4033  * @param {Number} r - the red component, clipped to the range [0,255]
4034  * @param {Number} g - the green component, clipped to the range [0,255]
4035  * @param {Number} b - the blue component, clipped to the range [0,255]
4036  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4037  *     optional and defaults to 1
4038  */
4039 Roo.lib.RGBColor = function (r, g, b, a){
4040
4041   // store the alpha component after clipping it if necessary
4042   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4043
4044   // store the RGB components after clipping them if necessary
4045   this.rgb =
4046       {
4047         'r' : Math.max(0, Math.min(255, r)),
4048         'g' : Math.max(0, Math.min(255, g)),
4049         'b' : Math.max(0, Math.min(255, b))
4050       };
4051
4052   // initialise the HSV and HSL components to null
4053   
4054
4055   /* 
4056    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4057    * range [0,360). The parameters are:
4058    *
4059    * maximum - the maximum of the RGB component values
4060    * range   - the range of the RGB component values
4061    */
4062    
4063
4064 }
4065 // this does an 'exteds'
4066 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4067
4068   
4069     getHue  : function(maximum, range)
4070     {
4071       var rgb = this.rgb;
4072        
4073       // check whether the range is zero
4074       if (range == 0){
4075   
4076         // set the hue to zero (any hue is acceptable as the Color is grey)
4077         var hue = 0;
4078   
4079       }else{
4080   
4081         // determine which of the components has the highest value and set the hue
4082         switch (maximum){
4083   
4084           // red has the highest value
4085           case rgb.r:
4086             var hue = (rgb.g - rgb.b) / range * 60;
4087             if (hue < 0) { hue += 360; }
4088             break;
4089   
4090           // green has the highest value
4091           case rgb.g:
4092             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4093             break;
4094   
4095           // blue has the highest value
4096           case rgb.b:
4097             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4098             break;
4099   
4100         }
4101   
4102       }
4103   
4104       // return the hue
4105       return hue;
4106   
4107     },
4108
4109   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4110    * be returned be the getHSV function.
4111    */
4112    calculateHSV : function(){
4113     var rgb = this.rgb;
4114     // get the maximum and range of the RGB component values
4115     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4116     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4117
4118     // store the HSV components
4119     this.hsv =
4120         {
4121           'h' : this.getHue(maximum, range),
4122           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4123           'v' : maximum / 2.55
4124         };
4125
4126   },
4127
4128   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4129    * be returned be the getHSL function.
4130    */
4131    calculateHSL : function(){
4132     var rgb = this.rgb;
4133     // get the maximum and range of the RGB component values
4134     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4135     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4136
4137     // determine the lightness in the range [0,1]
4138     var l = maximum / 255 - range / 510;
4139
4140     // store the HSL components
4141     this.hsl =
4142         {
4143           'h' : this.getHue(maximum, range),
4144           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4145           'l' : 100 * l
4146         };
4147
4148   }
4149
4150 });
4151
4152 /**
4153  * @class Roo.lib.HSVColor
4154  * @extends Roo.lib.Color
4155  * Creates a Color specified in the HSV Color space, with an optional alpha
4156  * component. The parameters are:
4157  * @constructor
4158  *
4159  * @param {Number} h - the hue component, wrapped to the range [0,360)
4160  * @param {Number} s - the saturation component, clipped to the range [0,100]
4161  * @param {Number} v - the value component, clipped to the range [0,100]
4162  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4163  *     optional and defaults to 1
4164  */
4165 Roo.lib.HSVColor = function (h, s, v, a){
4166
4167   // store the alpha component after clipping it if necessary
4168   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4169
4170   // store the HSV components after clipping or wrapping them if necessary
4171   this.hsv =
4172       {
4173         'h' : (h % 360 + 360) % 360,
4174         's' : Math.max(0, Math.min(100, s)),
4175         'v' : Math.max(0, Math.min(100, v))
4176       };
4177
4178   // initialise the RGB and HSL components to null
4179   this.rgb = null;
4180   this.hsl = null;
4181 }
4182
4183 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4184   /* Calculates and stores the RGB components of this HSVColor so that they can
4185    * be returned be the getRGB function.
4186    */
4187   calculateRGB: function ()
4188   {
4189     var hsv = this.hsv;
4190     // check whether the saturation is zero
4191     if (hsv.s == 0){
4192
4193       // set the Color to the appropriate shade of grey
4194       var r = hsv.v;
4195       var g = hsv.v;
4196       var b = hsv.v;
4197
4198     }else{
4199
4200       // set some temporary values
4201       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4202       var p  = hsv.v * (1 - hsv.s / 100);
4203       var q  = hsv.v * (1 - hsv.s / 100 * f);
4204       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4205
4206       // set the RGB Color components to their temporary values
4207       switch (Math.floor(hsv.h / 60)){
4208         case 0: var r = hsv.v; var g = t; var b = p; break;
4209         case 1: var r = q; var g = hsv.v; var b = p; break;
4210         case 2: var r = p; var g = hsv.v; var b = t; break;
4211         case 3: var r = p; var g = q; var b = hsv.v; break;
4212         case 4: var r = t; var g = p; var b = hsv.v; break;
4213         case 5: var r = hsv.v; var g = p; var b = q; break;
4214       }
4215
4216     }
4217
4218     // store the RGB components
4219     this.rgb =
4220         {
4221           'r' : r * 2.55,
4222           'g' : g * 2.55,
4223           'b' : b * 2.55
4224         };
4225
4226   },
4227
4228   /* Calculates and stores the HSL components of this HSVColor so that they can
4229    * be returned be the getHSL function.
4230    */
4231   calculateHSL : function (){
4232
4233     var hsv = this.hsv;
4234     // determine the lightness in the range [0,100]
4235     var l = (2 - hsv.s / 100) * hsv.v / 2;
4236
4237     // store the HSL components
4238     this.hsl =
4239         {
4240           'h' : hsv.h,
4241           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4242           'l' : l
4243         };
4244
4245     // correct a division-by-zero error
4246     if (isNaN(hsl.s)) { hsl.s = 0; }
4247
4248   } 
4249  
4250
4251 });
4252  
4253
4254 /**
4255  * @class Roo.lib.HSLColor
4256  * @extends Roo.lib.Color
4257  *
4258  * @constructor
4259  * Creates a Color specified in the HSL Color space, with an optional alpha
4260  * component. The parameters are:
4261  *
4262  * @param {Number} h - the hue component, wrapped to the range [0,360)
4263  * @param {Number} s - the saturation component, clipped to the range [0,100]
4264  * @param {Number} l - the lightness component, clipped to the range [0,100]
4265  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4266  *     optional and defaults to 1
4267  */
4268
4269 Roo.lib.HSLColor = function(h, s, l, a){
4270
4271   // store the alpha component after clipping it if necessary
4272   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4273
4274   // store the HSL components after clipping or wrapping them if necessary
4275   this.hsl =
4276       {
4277         'h' : (h % 360 + 360) % 360,
4278         's' : Math.max(0, Math.min(100, s)),
4279         'l' : Math.max(0, Math.min(100, l))
4280       };
4281
4282   // initialise the RGB and HSV components to null
4283 }
4284
4285 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4286
4287   /* Calculates and stores the RGB components of this HSLColor so that they can
4288    * be returned be the getRGB function.
4289    */
4290   calculateRGB: function (){
4291
4292     // check whether the saturation is zero
4293     if (this.hsl.s == 0){
4294
4295       // store the RGB components representing the appropriate shade of grey
4296       this.rgb =
4297           {
4298             'r' : this.hsl.l * 2.55,
4299             'g' : this.hsl.l * 2.55,
4300             'b' : this.hsl.l * 2.55
4301           };
4302
4303     }else{
4304
4305       // set some temporary values
4306       var p = this.hsl.l < 50
4307             ? this.hsl.l * (1 + hsl.s / 100)
4308             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4309       var q = 2 * hsl.l - p;
4310
4311       // initialise the RGB components
4312       this.rgb =
4313           {
4314             'r' : (h + 120) / 60 % 6,
4315             'g' : h / 60,
4316             'b' : (h + 240) / 60 % 6
4317           };
4318
4319       // loop over the RGB components
4320       for (var key in this.rgb){
4321
4322         // ensure that the property is not inherited from the root object
4323         if (this.rgb.hasOwnProperty(key)){
4324
4325           // set the component to its value in the range [0,100]
4326           if (this.rgb[key] < 1){
4327             this.rgb[key] = q + (p - q) * this.rgb[key];
4328           }else if (this.rgb[key] < 3){
4329             this.rgb[key] = p;
4330           }else if (this.rgb[key] < 4){
4331             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4332           }else{
4333             this.rgb[key] = q;
4334           }
4335
4336           // set the component to its value in the range [0,255]
4337           this.rgb[key] *= 2.55;
4338
4339         }
4340
4341       }
4342
4343     }
4344
4345   },
4346
4347   /* Calculates and stores the HSV components of this HSLColor so that they can
4348    * be returned be the getHSL function.
4349    */
4350    calculateHSV : function(){
4351
4352     // set a temporary value
4353     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4354
4355     // store the HSV components
4356     this.hsv =
4357         {
4358           'h' : this.hsl.h,
4359           's' : 200 * t / (this.hsl.l + t),
4360           'v' : t + this.hsl.l
4361         };
4362
4363     // correct a division-by-zero error
4364     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4365
4366   }
4367  
4368
4369 });
4370 /*
4371  * Portions of this file are based on pieces of Yahoo User Interface Library
4372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4373  * YUI licensed under the BSD License:
4374  * http://developer.yahoo.net/yui/license.txt
4375  * <script type="text/javascript">
4376  *
4377  */
4378 (function() {
4379
4380     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4381         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4382     };
4383
4384     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4385
4386     var fly = Roo.lib.AnimBase.fly;
4387     var Y = Roo.lib;
4388     var superclass = Y.ColorAnim.superclass;
4389     var proto = Y.ColorAnim.prototype;
4390
4391     proto.toString = function() {
4392         var el = this.getEl();
4393         var id = el.id || el.tagName;
4394         return ("ColorAnim " + id);
4395     };
4396
4397     proto.patterns.color = /color$/i;
4398     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4399     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4400     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4401     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4402
4403
4404     proto.parseColor = function(s) {
4405         if (s.length == 3) {
4406             return s;
4407         }
4408
4409         var c = this.patterns.hex.exec(s);
4410         if (c && c.length == 4) {
4411             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4412         }
4413
4414         c = this.patterns.rgb.exec(s);
4415         if (c && c.length == 4) {
4416             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4417         }
4418
4419         c = this.patterns.hex3.exec(s);
4420         if (c && c.length == 4) {
4421             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4422         }
4423
4424         return null;
4425     };
4426     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4427     proto.getAttribute = function(attr) {
4428         var el = this.getEl();
4429         if (this.patterns.color.test(attr)) {
4430             var val = fly(el).getStyle(attr);
4431
4432             if (this.patterns.transparent.test(val)) {
4433                 var parent = el.parentNode;
4434                 val = fly(parent).getStyle(attr);
4435
4436                 while (parent && this.patterns.transparent.test(val)) {
4437                     parent = parent.parentNode;
4438                     val = fly(parent).getStyle(attr);
4439                     if (parent.tagName.toUpperCase() == 'HTML') {
4440                         val = '#fff';
4441                     }
4442                 }
4443             }
4444         } else {
4445             val = superclass.getAttribute.call(this, attr);
4446         }
4447
4448         return val;
4449     };
4450     proto.getAttribute = function(attr) {
4451         var el = this.getEl();
4452         if (this.patterns.color.test(attr)) {
4453             var val = fly(el).getStyle(attr);
4454
4455             if (this.patterns.transparent.test(val)) {
4456                 var parent = el.parentNode;
4457                 val = fly(parent).getStyle(attr);
4458
4459                 while (parent && this.patterns.transparent.test(val)) {
4460                     parent = parent.parentNode;
4461                     val = fly(parent).getStyle(attr);
4462                     if (parent.tagName.toUpperCase() == 'HTML') {
4463                         val = '#fff';
4464                     }
4465                 }
4466             }
4467         } else {
4468             val = superclass.getAttribute.call(this, attr);
4469         }
4470
4471         return val;
4472     };
4473
4474     proto.doMethod = function(attr, start, end) {
4475         var val;
4476
4477         if (this.patterns.color.test(attr)) {
4478             val = [];
4479             for (var i = 0, len = start.length; i < len; ++i) {
4480                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4481             }
4482
4483             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4484         }
4485         else {
4486             val = superclass.doMethod.call(this, attr, start, end);
4487         }
4488
4489         return val;
4490     };
4491
4492     proto.setRuntimeAttribute = function(attr) {
4493         superclass.setRuntimeAttribute.call(this, attr);
4494
4495         if (this.patterns.color.test(attr)) {
4496             var attributes = this.attributes;
4497             var start = this.parseColor(this.runtimeAttributes[attr].start);
4498             var end = this.parseColor(this.runtimeAttributes[attr].end);
4499
4500             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4501                 end = this.parseColor(attributes[attr].by);
4502
4503                 for (var i = 0, len = start.length; i < len; ++i) {
4504                     end[i] = start[i] + end[i];
4505                 }
4506             }
4507
4508             this.runtimeAttributes[attr].start = start;
4509             this.runtimeAttributes[attr].end = end;
4510         }
4511     };
4512 })();
4513
4514 /*
4515  * Portions of this file are based on pieces of Yahoo User Interface Library
4516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4517  * YUI licensed under the BSD License:
4518  * http://developer.yahoo.net/yui/license.txt
4519  * <script type="text/javascript">
4520  *
4521  */
4522 Roo.lib.Easing = {
4523
4524
4525     easeNone: function (t, b, c, d) {
4526         return c * t / d + b;
4527     },
4528
4529
4530     easeIn: function (t, b, c, d) {
4531         return c * (t /= d) * t + b;
4532     },
4533
4534
4535     easeOut: function (t, b, c, d) {
4536         return -c * (t /= d) * (t - 2) + b;
4537     },
4538
4539
4540     easeBoth: function (t, b, c, d) {
4541         if ((t /= d / 2) < 1) {
4542             return c / 2 * t * t + b;
4543         }
4544
4545         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4546     },
4547
4548
4549     easeInStrong: function (t, b, c, d) {
4550         return c * (t /= d) * t * t * t + b;
4551     },
4552
4553
4554     easeOutStrong: function (t, b, c, d) {
4555         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4556     },
4557
4558
4559     easeBothStrong: function (t, b, c, d) {
4560         if ((t /= d / 2) < 1) {
4561             return c / 2 * t * t * t * t + b;
4562         }
4563
4564         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4565     },
4566
4567
4568
4569     elasticIn: function (t, b, c, d, a, p) {
4570         if (t == 0) {
4571             return b;
4572         }
4573         if ((t /= d) == 1) {
4574             return b + c;
4575         }
4576         if (!p) {
4577             p = d * .3;
4578         }
4579
4580         if (!a || a < Math.abs(c)) {
4581             a = c;
4582             var s = p / 4;
4583         }
4584         else {
4585             var s = p / (2 * Math.PI) * Math.asin(c / a);
4586         }
4587
4588         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4589     },
4590
4591
4592     elasticOut: function (t, b, c, d, a, p) {
4593         if (t == 0) {
4594             return b;
4595         }
4596         if ((t /= d) == 1) {
4597             return b + c;
4598         }
4599         if (!p) {
4600             p = d * .3;
4601         }
4602
4603         if (!a || a < Math.abs(c)) {
4604             a = c;
4605             var s = p / 4;
4606         }
4607         else {
4608             var s = p / (2 * Math.PI) * Math.asin(c / a);
4609         }
4610
4611         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4612     },
4613
4614
4615     elasticBoth: function (t, b, c, d, a, p) {
4616         if (t == 0) {
4617             return b;
4618         }
4619
4620         if ((t /= d / 2) == 2) {
4621             return b + c;
4622         }
4623
4624         if (!p) {
4625             p = d * (.3 * 1.5);
4626         }
4627
4628         if (!a || a < Math.abs(c)) {
4629             a = c;
4630             var s = p / 4;
4631         }
4632         else {
4633             var s = p / (2 * Math.PI) * Math.asin(c / a);
4634         }
4635
4636         if (t < 1) {
4637             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4638                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4639         }
4640         return a * Math.pow(2, -10 * (t -= 1)) *
4641                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4642     },
4643
4644
4645
4646     backIn: function (t, b, c, d, s) {
4647         if (typeof s == 'undefined') {
4648             s = 1.70158;
4649         }
4650         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4651     },
4652
4653
4654     backOut: function (t, b, c, d, s) {
4655         if (typeof s == 'undefined') {
4656             s = 1.70158;
4657         }
4658         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4659     },
4660
4661
4662     backBoth: function (t, b, c, d, s) {
4663         if (typeof s == 'undefined') {
4664             s = 1.70158;
4665         }
4666
4667         if ((t /= d / 2 ) < 1) {
4668             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4669         }
4670         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4671     },
4672
4673
4674     bounceIn: function (t, b, c, d) {
4675         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4676     },
4677
4678
4679     bounceOut: function (t, b, c, d) {
4680         if ((t /= d) < (1 / 2.75)) {
4681             return c * (7.5625 * t * t) + b;
4682         } else if (t < (2 / 2.75)) {
4683             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4684         } else if (t < (2.5 / 2.75)) {
4685             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4686         }
4687         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4688     },
4689
4690
4691     bounceBoth: function (t, b, c, d) {
4692         if (t < d / 2) {
4693             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4694         }
4695         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4696     }
4697 };/*
4698  * Portions of this file are based on pieces of Yahoo User Interface Library
4699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4700  * YUI licensed under the BSD License:
4701  * http://developer.yahoo.net/yui/license.txt
4702  * <script type="text/javascript">
4703  *
4704  */
4705     (function() {
4706         Roo.lib.Motion = function(el, attributes, duration, method) {
4707             if (el) {
4708                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4709             }
4710         };
4711
4712         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4713
4714
4715         var Y = Roo.lib;
4716         var superclass = Y.Motion.superclass;
4717         var proto = Y.Motion.prototype;
4718
4719         proto.toString = function() {
4720             var el = this.getEl();
4721             var id = el.id || el.tagName;
4722             return ("Motion " + id);
4723         };
4724
4725         proto.patterns.points = /^points$/i;
4726
4727         proto.setAttribute = function(attr, val, unit) {
4728             if (this.patterns.points.test(attr)) {
4729                 unit = unit || 'px';
4730                 superclass.setAttribute.call(this, 'left', val[0], unit);
4731                 superclass.setAttribute.call(this, 'top', val[1], unit);
4732             } else {
4733                 superclass.setAttribute.call(this, attr, val, unit);
4734             }
4735         };
4736
4737         proto.getAttribute = function(attr) {
4738             if (this.patterns.points.test(attr)) {
4739                 var val = [
4740                         superclass.getAttribute.call(this, 'left'),
4741                         superclass.getAttribute.call(this, 'top')
4742                         ];
4743             } else {
4744                 val = superclass.getAttribute.call(this, attr);
4745             }
4746
4747             return val;
4748         };
4749
4750         proto.doMethod = function(attr, start, end) {
4751             var val = null;
4752
4753             if (this.patterns.points.test(attr)) {
4754                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4755                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4756             } else {
4757                 val = superclass.doMethod.call(this, attr, start, end);
4758             }
4759             return val;
4760         };
4761
4762         proto.setRuntimeAttribute = function(attr) {
4763             if (this.patterns.points.test(attr)) {
4764                 var el = this.getEl();
4765                 var attributes = this.attributes;
4766                 var start;
4767                 var control = attributes['points']['control'] || [];
4768                 var end;
4769                 var i, len;
4770
4771                 if (control.length > 0 && !(control[0] instanceof Array)) {
4772                     control = [control];
4773                 } else {
4774                     var tmp = [];
4775                     for (i = 0,len = control.length; i < len; ++i) {
4776                         tmp[i] = control[i];
4777                     }
4778                     control = tmp;
4779                 }
4780
4781                 Roo.fly(el).position();
4782
4783                 if (isset(attributes['points']['from'])) {
4784                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4785                 }
4786                 else {
4787                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4788                 }
4789
4790                 start = this.getAttribute('points');
4791
4792
4793                 if (isset(attributes['points']['to'])) {
4794                     end = translateValues.call(this, attributes['points']['to'], start);
4795
4796                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4797                     for (i = 0,len = control.length; i < len; ++i) {
4798                         control[i] = translateValues.call(this, control[i], start);
4799                     }
4800
4801
4802                 } else if (isset(attributes['points']['by'])) {
4803                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4804
4805                     for (i = 0,len = control.length; i < len; ++i) {
4806                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4807                     }
4808                 }
4809
4810                 this.runtimeAttributes[attr] = [start];
4811
4812                 if (control.length > 0) {
4813                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4814                 }
4815
4816                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4817             }
4818             else {
4819                 superclass.setRuntimeAttribute.call(this, attr);
4820             }
4821         };
4822
4823         var translateValues = function(val, start) {
4824             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4825             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4826
4827             return val;
4828         };
4829
4830         var isset = function(prop) {
4831             return (typeof prop !== 'undefined');
4832         };
4833     })();
4834 /*
4835  * Portions of this file are based on pieces of Yahoo User Interface Library
4836  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4837  * YUI licensed under the BSD License:
4838  * http://developer.yahoo.net/yui/license.txt
4839  * <script type="text/javascript">
4840  *
4841  */
4842     (function() {
4843         Roo.lib.Scroll = function(el, attributes, duration, method) {
4844             if (el) {
4845                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4846             }
4847         };
4848
4849         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4850
4851
4852         var Y = Roo.lib;
4853         var superclass = Y.Scroll.superclass;
4854         var proto = Y.Scroll.prototype;
4855
4856         proto.toString = function() {
4857             var el = this.getEl();
4858             var id = el.id || el.tagName;
4859             return ("Scroll " + id);
4860         };
4861
4862         proto.doMethod = function(attr, start, end) {
4863             var val = null;
4864
4865             if (attr == 'scroll') {
4866                 val = [
4867                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4868                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4869                         ];
4870
4871             } else {
4872                 val = superclass.doMethod.call(this, attr, start, end);
4873             }
4874             return val;
4875         };
4876
4877         proto.getAttribute = function(attr) {
4878             var val = null;
4879             var el = this.getEl();
4880
4881             if (attr == 'scroll') {
4882                 val = [ el.scrollLeft, el.scrollTop ];
4883             } else {
4884                 val = superclass.getAttribute.call(this, attr);
4885             }
4886
4887             return val;
4888         };
4889
4890         proto.setAttribute = function(attr, val, unit) {
4891             var el = this.getEl();
4892
4893             if (attr == 'scroll') {
4894                 el.scrollLeft = val[0];
4895                 el.scrollTop = val[1];
4896             } else {
4897                 superclass.setAttribute.call(this, attr, val, unit);
4898             }
4899         };
4900     })();
4901 /*
4902  * Based on:
4903  * Ext JS Library 1.1.1
4904  * Copyright(c) 2006-2007, Ext JS, LLC.
4905  *
4906  * Originally Released Under LGPL - original licence link has changed is not relivant.
4907  *
4908  * Fork - LGPL
4909  * <script type="text/javascript">
4910  */
4911
4912
4913 // nasty IE9 hack - what a pile of crap that is..
4914
4915  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4916     Range.prototype.createContextualFragment = function (html) {
4917         var doc = window.document;
4918         var container = doc.createElement("div");
4919         container.innerHTML = html;
4920         var frag = doc.createDocumentFragment(), n;
4921         while ((n = container.firstChild)) {
4922             frag.appendChild(n);
4923         }
4924         return frag;
4925     };
4926 }
4927
4928 /**
4929  * @class Roo.DomHelper
4930  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4931  * 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>.
4932  * @static
4933  */
4934 Roo.DomHelper = function(){
4935     var tempTableEl = null;
4936     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4937     var tableRe = /^table|tbody|tr|td$/i;
4938     var xmlns = {};
4939     // build as innerHTML where available
4940     /** @ignore */
4941     var createHtml = function(o){
4942         if(typeof o == 'string'){
4943             return o;
4944         }
4945         var b = "";
4946         if(!o.tag){
4947             o.tag = "div";
4948         }
4949         b += "<" + o.tag;
4950         for(var attr in o){
4951             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4952             if(attr == "style"){
4953                 var s = o["style"];
4954                 if(typeof s == "function"){
4955                     s = s.call();
4956                 }
4957                 if(typeof s == "string"){
4958                     b += ' style="' + s + '"';
4959                 }else if(typeof s == "object"){
4960                     b += ' style="';
4961                     for(var key in s){
4962                         if(typeof s[key] != "function"){
4963                             b += key + ":" + s[key] + ";";
4964                         }
4965                     }
4966                     b += '"';
4967                 }
4968             }else{
4969                 if(attr == "cls"){
4970                     b += ' class="' + o["cls"] + '"';
4971                 }else if(attr == "htmlFor"){
4972                     b += ' for="' + o["htmlFor"] + '"';
4973                 }else{
4974                     b += " " + attr + '="' + o[attr] + '"';
4975                 }
4976             }
4977         }
4978         if(emptyTags.test(o.tag)){
4979             b += "/>";
4980         }else{
4981             b += ">";
4982             var cn = o.children || o.cn;
4983             if(cn){
4984                 //http://bugs.kde.org/show_bug.cgi?id=71506
4985                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4986                     for(var i = 0, len = cn.length; i < len; i++) {
4987                         b += createHtml(cn[i], b);
4988                     }
4989                 }else{
4990                     b += createHtml(cn, b);
4991                 }
4992             }
4993             if(o.html){
4994                 b += o.html;
4995             }
4996             b += "</" + o.tag + ">";
4997         }
4998         return b;
4999     };
5000
5001     // build as dom
5002     /** @ignore */
5003     var createDom = function(o, parentNode){
5004          
5005         // defininition craeted..
5006         var ns = false;
5007         if (o.ns && o.ns != 'html') {
5008                
5009             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5010                 xmlns[o.ns] = o.xmlns;
5011                 ns = o.xmlns;
5012             }
5013             if (typeof(xmlns[o.ns]) == 'undefined') {
5014                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5015             }
5016             ns = xmlns[o.ns];
5017         }
5018         
5019         
5020         if (typeof(o) == 'string') {
5021             return parentNode.appendChild(document.createTextNode(o));
5022         }
5023         o.tag = o.tag || div;
5024         if (o.ns && Roo.isIE) {
5025             ns = false;
5026             o.tag = o.ns + ':' + o.tag;
5027             
5028         }
5029         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5030         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5031         for(var attr in o){
5032             
5033             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5034                     attr == "style" || typeof o[attr] == "function") { continue; }
5035                     
5036             if(attr=="cls" && Roo.isIE){
5037                 el.className = o["cls"];
5038             }else{
5039                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5040                 else { 
5041                     el[attr] = o[attr];
5042                 }
5043             }
5044         }
5045         Roo.DomHelper.applyStyles(el, o.style);
5046         var cn = o.children || o.cn;
5047         if(cn){
5048             //http://bugs.kde.org/show_bug.cgi?id=71506
5049              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5050                 for(var i = 0, len = cn.length; i < len; i++) {
5051                     createDom(cn[i], el);
5052                 }
5053             }else{
5054                 createDom(cn, el);
5055             }
5056         }
5057         if(o.html){
5058             el.innerHTML = o.html;
5059         }
5060         if(parentNode){
5061            parentNode.appendChild(el);
5062         }
5063         return el;
5064     };
5065
5066     var ieTable = function(depth, s, h, e){
5067         tempTableEl.innerHTML = [s, h, e].join('');
5068         var i = -1, el = tempTableEl;
5069         while(++i < depth && el.firstChild){
5070             el = el.firstChild;
5071         }
5072         return el;
5073     };
5074
5075     // kill repeat to save bytes
5076     var ts = '<table>',
5077         te = '</table>',
5078         tbs = ts+'<tbody>',
5079         tbe = '</tbody>'+te,
5080         trs = tbs + '<tr>',
5081         tre = '</tr>'+tbe;
5082
5083     /**
5084      * @ignore
5085      * Nasty code for IE's broken table implementation
5086      */
5087     var insertIntoTable = function(tag, where, el, html){
5088         if(!tempTableEl){
5089             tempTableEl = document.createElement('div');
5090         }
5091         var node;
5092         var before = null;
5093         if(tag == 'td'){
5094             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5095                 return;
5096             }
5097             if(where == 'beforebegin'){
5098                 before = el;
5099                 el = el.parentNode;
5100             } else{
5101                 before = el.nextSibling;
5102                 el = el.parentNode;
5103             }
5104             node = ieTable(4, trs, html, tre);
5105         }
5106         else if(tag == 'tr'){
5107             if(where == 'beforebegin'){
5108                 before = el;
5109                 el = el.parentNode;
5110                 node = ieTable(3, tbs, html, tbe);
5111             } else if(where == 'afterend'){
5112                 before = el.nextSibling;
5113                 el = el.parentNode;
5114                 node = ieTable(3, tbs, html, tbe);
5115             } else{ // INTO a TR
5116                 if(where == 'afterbegin'){
5117                     before = el.firstChild;
5118                 }
5119                 node = ieTable(4, trs, html, tre);
5120             }
5121         } else if(tag == 'tbody'){
5122             if(where == 'beforebegin'){
5123                 before = el;
5124                 el = el.parentNode;
5125                 node = ieTable(2, ts, html, te);
5126             } else if(where == 'afterend'){
5127                 before = el.nextSibling;
5128                 el = el.parentNode;
5129                 node = ieTable(2, ts, html, te);
5130             } else{
5131                 if(where == 'afterbegin'){
5132                     before = el.firstChild;
5133                 }
5134                 node = ieTable(3, tbs, html, tbe);
5135             }
5136         } else{ // TABLE
5137             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5138                 return;
5139             }
5140             if(where == 'afterbegin'){
5141                 before = el.firstChild;
5142             }
5143             node = ieTable(2, ts, html, te);
5144         }
5145         el.insertBefore(node, before);
5146         return node;
5147     };
5148     
5149     // this is a bit like the react update code...
5150     // 
5151     
5152     var updateNode = function(from, to)
5153     {
5154         // should we handle non-standard elements?
5155         
5156         if (from.nodeType != to.nodeType) {
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             from.parentNode.replaceChild(to, from);
5172             return;
5173         }
5174         // compare attributes
5175         var ar = Array.from(from.attributes);
5176         for(var i = 0; i< ar.length;i++) {
5177             if (to.hasAttribute(ar[i].name)) {
5178                 continue;
5179             }
5180             from.removeAttribute(ar[i].name);
5181         }
5182         ar = to.attributes;
5183         for(var i = 0; i< ar.length;i++) {
5184             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5185                 continue;
5186             }
5187             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5188         }
5189         // children
5190         var far = Array.from(from.childNodes);
5191         var tar = Array.from(to.childNodes);
5192         // if the lengths are different.. then it's probably a editable content change, rather than
5193         // a change of the block definition..
5194         if (from.innerHTML == to.innerHTML) {
5195             return;
5196         }
5197         if (far.length != tar.length) {
5198             from.innerHTML = to.innerHTML;
5199             return;
5200         }
5201         
5202         for(var i = 0; i < far.length; i++) {
5203             updateNode(far[i], tar[i]);
5204         }
5205         
5206         
5207     };
5208     
5209     
5210
5211     return {
5212         /** True to force the use of DOM instead of html fragments @type Boolean */
5213         useDom : false,
5214     
5215         /**
5216          * Returns the markup for the passed Element(s) config
5217          * @param {Object} o The Dom object spec (and children)
5218          * @return {String}
5219          */
5220         markup : function(o){
5221             return createHtml(o);
5222         },
5223     
5224         /**
5225          * Applies a style specification to an element
5226          * @param {String/HTMLElement} el The element to apply styles to
5227          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5228          * a function which returns such a specification.
5229          */
5230         applyStyles : function(el, styles){
5231             if(styles){
5232                el = Roo.fly(el);
5233                if(typeof styles == "string"){
5234                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5235                    var matches;
5236                    while ((matches = re.exec(styles)) != null){
5237                        el.setStyle(matches[1], matches[2]);
5238                    }
5239                }else if (typeof styles == "object"){
5240                    for (var style in styles){
5241                       el.setStyle(style, styles[style]);
5242                    }
5243                }else if (typeof styles == "function"){
5244                     Roo.DomHelper.applyStyles(el, styles.call());
5245                }
5246             }
5247         },
5248     
5249         /**
5250          * Inserts an HTML fragment into the Dom
5251          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5252          * @param {HTMLElement} el The context element
5253          * @param {String} html The HTML fragmenet
5254          * @return {HTMLElement} The new node
5255          */
5256         insertHtml : function(where, el, html){
5257             where = where.toLowerCase();
5258             if(el.insertAdjacentHTML){
5259                 if(tableRe.test(el.tagName)){
5260                     var rs;
5261                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5262                         return rs;
5263                     }
5264                 }
5265                 switch(where){
5266                     case "beforebegin":
5267                         el.insertAdjacentHTML('BeforeBegin', html);
5268                         return el.previousSibling;
5269                     case "afterbegin":
5270                         el.insertAdjacentHTML('AfterBegin', html);
5271                         return el.firstChild;
5272                     case "beforeend":
5273                         el.insertAdjacentHTML('BeforeEnd', html);
5274                         return el.lastChild;
5275                     case "afterend":
5276                         el.insertAdjacentHTML('AfterEnd', html);
5277                         return el.nextSibling;
5278                 }
5279                 throw 'Illegal insertion point -> "' + where + '"';
5280             }
5281             var range = el.ownerDocument.createRange();
5282             var frag;
5283             switch(where){
5284                  case "beforebegin":
5285                     range.setStartBefore(el);
5286                     frag = range.createContextualFragment(html);
5287                     el.parentNode.insertBefore(frag, el);
5288                     return el.previousSibling;
5289                  case "afterbegin":
5290                     if(el.firstChild){
5291                         range.setStartBefore(el.firstChild);
5292                         frag = range.createContextualFragment(html);
5293                         el.insertBefore(frag, el.firstChild);
5294                         return el.firstChild;
5295                     }else{
5296                         el.innerHTML = html;
5297                         return el.firstChild;
5298                     }
5299                 case "beforeend":
5300                     if(el.lastChild){
5301                         range.setStartAfter(el.lastChild);
5302                         frag = range.createContextualFragment(html);
5303                         el.appendChild(frag);
5304                         return el.lastChild;
5305                     }else{
5306                         el.innerHTML = html;
5307                         return el.lastChild;
5308                     }
5309                 case "afterend":
5310                     range.setStartAfter(el);
5311                     frag = range.createContextualFragment(html);
5312                     el.parentNode.insertBefore(frag, el.nextSibling);
5313                     return el.nextSibling;
5314                 }
5315                 throw 'Illegal insertion point -> "' + where + '"';
5316         },
5317     
5318         /**
5319          * Creates new Dom element(s) and inserts them before el
5320          * @param {String/HTMLElement/Element} el The context element
5321          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5322          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5323          * @return {HTMLElement/Roo.Element} The new node
5324          */
5325         insertBefore : function(el, o, returnElement){
5326             return this.doInsert(el, o, returnElement, "beforeBegin");
5327         },
5328     
5329         /**
5330          * Creates new Dom element(s) and inserts them after el
5331          * @param {String/HTMLElement/Element} el The context element
5332          * @param {Object} o The Dom object spec (and children)
5333          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5334          * @return {HTMLElement/Roo.Element} The new node
5335          */
5336         insertAfter : function(el, o, returnElement){
5337             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5338         },
5339     
5340         /**
5341          * Creates new Dom element(s) and inserts them as the first child of el
5342          * @param {String/HTMLElement/Element} el The context element
5343          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5344          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5345          * @return {HTMLElement/Roo.Element} The new node
5346          */
5347         insertFirst : function(el, o, returnElement){
5348             return this.doInsert(el, o, returnElement, "afterBegin");
5349         },
5350     
5351         // private
5352         doInsert : function(el, o, returnElement, pos, sibling){
5353             el = Roo.getDom(el);
5354             var newNode;
5355             if(this.useDom || o.ns){
5356                 newNode = createDom(o, null);
5357                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5358             }else{
5359                 var html = createHtml(o);
5360                 newNode = this.insertHtml(pos, el, html);
5361             }
5362             return returnElement ? Roo.get(newNode, true) : newNode;
5363         },
5364     
5365         /**
5366          * Creates new Dom element(s) and appends them to el
5367          * @param {String/HTMLElement/Element} el The context element
5368          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5369          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5370          * @return {HTMLElement/Roo.Element} The new node
5371          */
5372         append : function(el, o, returnElement){
5373             el = Roo.getDom(el);
5374             var newNode;
5375             if(this.useDom || o.ns){
5376                 newNode = createDom(o, null);
5377                 el.appendChild(newNode);
5378             }else{
5379                 var html = createHtml(o);
5380                 newNode = this.insertHtml("beforeEnd", el, html);
5381             }
5382             return returnElement ? Roo.get(newNode, true) : newNode;
5383         },
5384     
5385         /**
5386          * Creates new Dom element(s) and overwrites the contents of el with them
5387          * @param {String/HTMLElement/Element} el The context element
5388          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5389          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5390          * @return {HTMLElement/Roo.Element} The new node
5391          */
5392         overwrite : function(el, o, returnElement)
5393         {
5394             el = Roo.getDom(el);
5395             if (o.ns) {
5396               
5397                 while (el.childNodes.length) {
5398                     el.removeChild(el.firstChild);
5399                 }
5400                 createDom(o, el);
5401             } else {
5402                 el.innerHTML = createHtml(o);   
5403             }
5404             
5405             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5406         },
5407     
5408         /**
5409          * Creates a new Roo.DomHelper.Template from the Dom object spec
5410          * @param {Object} o The Dom object spec (and children)
5411          * @return {Roo.DomHelper.Template} The new template
5412          */
5413         createTemplate : function(o){
5414             var html = createHtml(o);
5415             return new Roo.Template(html);
5416         },
5417          /**
5418          * Updates the first element with the spec from the o (replacing if necessary)
5419          * This iterates through the children, and updates attributes / children etc..
5420          * @param {String/HTMLElement/Element} el The context element
5421          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5422          */
5423         
5424         update : function(el, o)
5425         {
5426             updateNode(Roo.getDom(el), createDom(o));
5427             
5428         }
5429         
5430         
5431     };
5432 }();
5433 /*
5434  * Based on:
5435  * Ext JS Library 1.1.1
5436  * Copyright(c) 2006-2007, Ext JS, LLC.
5437  *
5438  * Originally Released Under LGPL - original licence link has changed is not relivant.
5439  *
5440  * Fork - LGPL
5441  * <script type="text/javascript">
5442  */
5443  
5444 /**
5445 * @class Roo.Template
5446 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5447 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5448 * Usage:
5449 <pre><code>
5450 var t = new Roo.Template({
5451     html :  '&lt;div name="{id}"&gt;' + 
5452         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5453         '&lt;/div&gt;',
5454     myformat: function (value, allValues) {
5455         return 'XX' + value;
5456     }
5457 });
5458 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5459 </code></pre>
5460 * For more information see this blog post with examples:
5461 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5462      - Create Elements using DOM, HTML fragments and Templates</a>. 
5463 * @constructor
5464 * @param {Object} cfg - Configuration object.
5465 */
5466 Roo.Template = function(cfg){
5467     // BC!
5468     if(cfg instanceof Array){
5469         cfg = cfg.join("");
5470     }else if(arguments.length > 1){
5471         cfg = Array.prototype.join.call(arguments, "");
5472     }
5473     
5474     
5475     if (typeof(cfg) == 'object') {
5476         Roo.apply(this,cfg)
5477     } else {
5478         // bc
5479         this.html = cfg;
5480     }
5481     if (this.url) {
5482         this.load();
5483     }
5484     
5485 };
5486 Roo.Template.prototype = {
5487     
5488     /**
5489      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5490      */
5491     onLoad : false,
5492     
5493     
5494     /**
5495      * @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..
5496      *                    it should be fixed so that template is observable...
5497      */
5498     url : false,
5499     /**
5500      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5501      */
5502     html : '',
5503     
5504     
5505     compiled : false,
5506     loaded : false,
5507     /**
5508      * Returns an HTML fragment of this template with the specified values applied.
5509      * @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'})
5510      * @return {String} The HTML fragment
5511      */
5512     
5513    
5514     
5515     applyTemplate : function(values){
5516         //Roo.log(["applyTemplate", values]);
5517         try {
5518            
5519             if(this.compiled){
5520                 return this.compiled(values);
5521             }
5522             var useF = this.disableFormats !== true;
5523             var fm = Roo.util.Format, tpl = this;
5524             var fn = function(m, name, format, args){
5525                 if(format && useF){
5526                     if(format.substr(0, 5) == "this."){
5527                         return tpl.call(format.substr(5), values[name], values);
5528                     }else{
5529                         if(args){
5530                             // quoted values are required for strings in compiled templates, 
5531                             // but for non compiled we need to strip them
5532                             // quoted reversed for jsmin
5533                             var re = /^\s*['"](.*)["']\s*$/;
5534                             args = args.split(',');
5535                             for(var i = 0, len = args.length; i < len; i++){
5536                                 args[i] = args[i].replace(re, "$1");
5537                             }
5538                             args = [values[name]].concat(args);
5539                         }else{
5540                             args = [values[name]];
5541                         }
5542                         return fm[format].apply(fm, args);
5543                     }
5544                 }else{
5545                     return values[name] !== undefined ? values[name] : "";
5546                 }
5547             };
5548             return this.html.replace(this.re, fn);
5549         } catch (e) {
5550             Roo.log(e);
5551             throw e;
5552         }
5553          
5554     },
5555     
5556     loading : false,
5557       
5558     load : function ()
5559     {
5560          
5561         if (this.loading) {
5562             return;
5563         }
5564         var _t = this;
5565         
5566         this.loading = true;
5567         this.compiled = false;
5568         
5569         var cx = new Roo.data.Connection();
5570         cx.request({
5571             url : this.url,
5572             method : 'GET',
5573             success : function (response) {
5574                 _t.loading = false;
5575                 _t.url = false;
5576                 
5577                 _t.set(response.responseText,true);
5578                 _t.loaded = true;
5579                 if (_t.onLoad) {
5580                     _t.onLoad();
5581                 }
5582              },
5583             failure : function(response) {
5584                 Roo.log("Template failed to load from " + _t.url);
5585                 _t.loading = false;
5586             }
5587         });
5588     },
5589
5590     /**
5591      * Sets the HTML used as the template and optionally compiles it.
5592      * @param {String} html
5593      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5594      * @return {Roo.Template} this
5595      */
5596     set : function(html, compile){
5597         this.html = html;
5598         this.compiled = false;
5599         if(compile){
5600             this.compile();
5601         }
5602         return this;
5603     },
5604     
5605     /**
5606      * True to disable format functions (defaults to false)
5607      * @type Boolean
5608      */
5609     disableFormats : false,
5610     
5611     /**
5612     * The regular expression used to match template variables 
5613     * @type RegExp
5614     * @property 
5615     */
5616     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5617     
5618     /**
5619      * Compiles the template into an internal function, eliminating the RegEx overhead.
5620      * @return {Roo.Template} this
5621      */
5622     compile : function(){
5623         var fm = Roo.util.Format;
5624         var useF = this.disableFormats !== true;
5625         var sep = Roo.isGecko ? "+" : ",";
5626         var fn = function(m, name, format, args){
5627             if(format && useF){
5628                 args = args ? ',' + args : "";
5629                 if(format.substr(0, 5) != "this."){
5630                     format = "fm." + format + '(';
5631                 }else{
5632                     format = 'this.call("'+ format.substr(5) + '", ';
5633                     args = ", values";
5634                 }
5635             }else{
5636                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5637             }
5638             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5639         };
5640         var body;
5641         // branched to use + in gecko and [].join() in others
5642         if(Roo.isGecko){
5643             body = "this.compiled = function(values){ return '" +
5644                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5645                     "';};";
5646         }else{
5647             body = ["this.compiled = function(values){ return ['"];
5648             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5649             body.push("'].join('');};");
5650             body = body.join('');
5651         }
5652         /**
5653          * eval:var:values
5654          * eval:var:fm
5655          */
5656         eval(body);
5657         return this;
5658     },
5659     
5660     // private function used to call members
5661     call : function(fnName, value, allValues){
5662         return this[fnName](value, allValues);
5663     },
5664     
5665     /**
5666      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5667      * @param {String/HTMLElement/Roo.Element} el The context element
5668      * @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'})
5669      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5670      * @return {HTMLElement/Roo.Element} The new node or Element
5671      */
5672     insertFirst: function(el, values, returnElement){
5673         return this.doInsert('afterBegin', el, values, returnElement);
5674     },
5675
5676     /**
5677      * Applies the supplied values to the template and inserts the new node(s) before el.
5678      * @param {String/HTMLElement/Roo.Element} el The context element
5679      * @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'})
5680      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5681      * @return {HTMLElement/Roo.Element} The new node or Element
5682      */
5683     insertBefore: function(el, values, returnElement){
5684         return this.doInsert('beforeBegin', el, values, returnElement);
5685     },
5686
5687     /**
5688      * Applies the supplied values to the template and inserts the new node(s) after el.
5689      * @param {String/HTMLElement/Roo.Element} el The context element
5690      * @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'})
5691      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5692      * @return {HTMLElement/Roo.Element} The new node or Element
5693      */
5694     insertAfter : function(el, values, returnElement){
5695         return this.doInsert('afterEnd', el, values, returnElement);
5696     },
5697     
5698     /**
5699      * Applies the supplied values to the template and appends the new node(s) to el.
5700      * @param {String/HTMLElement/Roo.Element} el The context element
5701      * @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'})
5702      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5703      * @return {HTMLElement/Roo.Element} The new node or Element
5704      */
5705     append : function(el, values, returnElement){
5706         return this.doInsert('beforeEnd', el, values, returnElement);
5707     },
5708
5709     doInsert : function(where, el, values, returnEl){
5710         el = Roo.getDom(el);
5711         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5712         return returnEl ? Roo.get(newNode, true) : newNode;
5713     },
5714
5715     /**
5716      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5717      * @param {String/HTMLElement/Roo.Element} el The context element
5718      * @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'})
5719      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5720      * @return {HTMLElement/Roo.Element} The new node or Element
5721      */
5722     overwrite : function(el, values, returnElement){
5723         el = Roo.getDom(el);
5724         el.innerHTML = this.applyTemplate(values);
5725         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5726     }
5727 };
5728 /**
5729  * Alias for {@link #applyTemplate}
5730  * @method
5731  */
5732 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5733
5734 // backwards compat
5735 Roo.DomHelper.Template = Roo.Template;
5736
5737 /**
5738  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5739  * @param {String/HTMLElement} el A DOM element or its id
5740  * @returns {Roo.Template} The created template
5741  * @static
5742  */
5743 Roo.Template.from = function(el){
5744     el = Roo.getDom(el);
5745     return new Roo.Template(el.value || el.innerHTML);
5746 };/*
5747  * Based on:
5748  * Ext JS Library 1.1.1
5749  * Copyright(c) 2006-2007, Ext JS, LLC.
5750  *
5751  * Originally Released Under LGPL - original licence link has changed is not relivant.
5752  *
5753  * Fork - LGPL
5754  * <script type="text/javascript">
5755  */
5756  
5757
5758 /*
5759  * This is code is also distributed under MIT license for use
5760  * with jQuery and prototype JavaScript libraries.
5761  */
5762 /**
5763  * @class Roo.DomQuery
5764 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).
5765 <p>
5766 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>
5767
5768 <p>
5769 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.
5770 </p>
5771 <h4>Element Selectors:</h4>
5772 <ul class="list">
5773     <li> <b>*</b> any element</li>
5774     <li> <b>E</b> an element with the tag E</li>
5775     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5776     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5777     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5778     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5779 </ul>
5780 <h4>Attribute Selectors:</h4>
5781 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5782 <ul class="list">
5783     <li> <b>E[foo]</b> has an attribute "foo"</li>
5784     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5785     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5786     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5787     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5788     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5789     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5790 </ul>
5791 <h4>Pseudo Classes:</h4>
5792 <ul class="list">
5793     <li> <b>E:first-child</b> E is the first child of its parent</li>
5794     <li> <b>E:last-child</b> E is the last child of its parent</li>
5795     <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>
5796     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5797     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5798     <li> <b>E:only-child</b> E is the only child of its parent</li>
5799     <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>
5800     <li> <b>E:first</b> the first E in the resultset</li>
5801     <li> <b>E:last</b> the last E in the resultset</li>
5802     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5803     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5804     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5805     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5806     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5807     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5808     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5809     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5810     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5811 </ul>
5812 <h4>CSS Value Selectors:</h4>
5813 <ul class="list">
5814     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5815     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5816     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5817     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5818     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5819     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5820 </ul>
5821  * @static
5822  */
5823 Roo.DomQuery = function(){
5824     var cache = {}, simpleCache = {}, valueCache = {};
5825     var nonSpace = /\S/;
5826     var trimRe = /^\s+|\s+$/g;
5827     var tplRe = /\{(\d+)\}/g;
5828     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5829     var tagTokenRe = /^(#)?([\w-\*]+)/;
5830     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5831
5832     function child(p, index){
5833         var i = 0;
5834         var n = p.firstChild;
5835         while(n){
5836             if(n.nodeType == 1){
5837                if(++i == index){
5838                    return n;
5839                }
5840             }
5841             n = n.nextSibling;
5842         }
5843         return null;
5844     };
5845
5846     function next(n){
5847         while((n = n.nextSibling) && n.nodeType != 1);
5848         return n;
5849     };
5850
5851     function prev(n){
5852         while((n = n.previousSibling) && n.nodeType != 1);
5853         return n;
5854     };
5855
5856     function children(d){
5857         var n = d.firstChild, ni = -1;
5858             while(n){
5859                 var nx = n.nextSibling;
5860                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5861                     d.removeChild(n);
5862                 }else{
5863                     n.nodeIndex = ++ni;
5864                 }
5865                 n = nx;
5866             }
5867             return this;
5868         };
5869
5870     function byClassName(c, a, v){
5871         if(!v){
5872             return c;
5873         }
5874         var r = [], ri = -1, cn;
5875         for(var i = 0, ci; ci = c[i]; i++){
5876             
5877             
5878             if((' '+
5879                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5880                  +' ').indexOf(v) != -1){
5881                 r[++ri] = ci;
5882             }
5883         }
5884         return r;
5885     };
5886
5887     function attrValue(n, attr){
5888         if(!n.tagName && typeof n.length != "undefined"){
5889             n = n[0];
5890         }
5891         if(!n){
5892             return null;
5893         }
5894         if(attr == "for"){
5895             return n.htmlFor;
5896         }
5897         if(attr == "class" || attr == "className"){
5898             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5899         }
5900         return n.getAttribute(attr) || n[attr];
5901
5902     };
5903
5904     function getNodes(ns, mode, tagName){
5905         var result = [], ri = -1, cs;
5906         if(!ns){
5907             return result;
5908         }
5909         tagName = tagName || "*";
5910         if(typeof ns.getElementsByTagName != "undefined"){
5911             ns = [ns];
5912         }
5913         if(!mode){
5914             for(var i = 0, ni; ni = ns[i]; i++){
5915                 cs = ni.getElementsByTagName(tagName);
5916                 for(var j = 0, ci; ci = cs[j]; j++){
5917                     result[++ri] = ci;
5918                 }
5919             }
5920         }else if(mode == "/" || mode == ">"){
5921             var utag = tagName.toUpperCase();
5922             for(var i = 0, ni, cn; ni = ns[i]; i++){
5923                 cn = ni.children || ni.childNodes;
5924                 for(var j = 0, cj; cj = cn[j]; j++){
5925                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5926                         result[++ri] = cj;
5927                     }
5928                 }
5929             }
5930         }else if(mode == "+"){
5931             var utag = tagName.toUpperCase();
5932             for(var i = 0, n; n = ns[i]; i++){
5933                 while((n = n.nextSibling) && n.nodeType != 1);
5934                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5935                     result[++ri] = n;
5936                 }
5937             }
5938         }else if(mode == "~"){
5939             for(var i = 0, n; n = ns[i]; i++){
5940                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5941                 if(n){
5942                     result[++ri] = n;
5943                 }
5944             }
5945         }
5946         return result;
5947     };
5948
5949     function concat(a, b){
5950         if(b.slice){
5951             return a.concat(b);
5952         }
5953         for(var i = 0, l = b.length; i < l; i++){
5954             a[a.length] = b[i];
5955         }
5956         return a;
5957     }
5958
5959     function byTag(cs, tagName){
5960         if(cs.tagName || cs == document){
5961             cs = [cs];
5962         }
5963         if(!tagName){
5964             return cs;
5965         }
5966         var r = [], ri = -1;
5967         tagName = tagName.toLowerCase();
5968         for(var i = 0, ci; ci = cs[i]; i++){
5969             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5970                 r[++ri] = ci;
5971             }
5972         }
5973         return r;
5974     };
5975
5976     function byId(cs, attr, id){
5977         if(cs.tagName || cs == document){
5978             cs = [cs];
5979         }
5980         if(!id){
5981             return cs;
5982         }
5983         var r = [], ri = -1;
5984         for(var i = 0,ci; ci = cs[i]; i++){
5985             if(ci && ci.id == id){
5986                 r[++ri] = ci;
5987                 return r;
5988             }
5989         }
5990         return r;
5991     };
5992
5993     function byAttribute(cs, attr, value, op, custom){
5994         var r = [], ri = -1, st = custom=="{";
5995         var f = Roo.DomQuery.operators[op];
5996         for(var i = 0, ci; ci = cs[i]; i++){
5997             var a;
5998             if(st){
5999                 a = Roo.DomQuery.getStyle(ci, attr);
6000             }
6001             else if(attr == "class" || attr == "className"){
6002                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6003             }else if(attr == "for"){
6004                 a = ci.htmlFor;
6005             }else if(attr == "href"){
6006                 a = ci.getAttribute("href", 2);
6007             }else{
6008                 a = ci.getAttribute(attr);
6009             }
6010             if((f && f(a, value)) || (!f && a)){
6011                 r[++ri] = ci;
6012             }
6013         }
6014         return r;
6015     };
6016
6017     function byPseudo(cs, name, value){
6018         return Roo.DomQuery.pseudos[name](cs, value);
6019     };
6020
6021     // This is for IE MSXML which does not support expandos.
6022     // IE runs the same speed using setAttribute, however FF slows way down
6023     // and Safari completely fails so they need to continue to use expandos.
6024     var isIE = window.ActiveXObject ? true : false;
6025
6026     // this eval is stop the compressor from
6027     // renaming the variable to something shorter
6028     
6029     /** eval:var:batch */
6030     var batch = 30803; 
6031
6032     var key = 30803;
6033
6034     function nodupIEXml(cs){
6035         var d = ++key;
6036         cs[0].setAttribute("_nodup", d);
6037         var r = [cs[0]];
6038         for(var i = 1, len = cs.length; i < len; i++){
6039             var c = cs[i];
6040             if(!c.getAttribute("_nodup") != d){
6041                 c.setAttribute("_nodup", d);
6042                 r[r.length] = c;
6043             }
6044         }
6045         for(var i = 0, len = cs.length; i < len; i++){
6046             cs[i].removeAttribute("_nodup");
6047         }
6048         return r;
6049     }
6050
6051     function nodup(cs){
6052         if(!cs){
6053             return [];
6054         }
6055         var len = cs.length, c, i, r = cs, cj, ri = -1;
6056         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6057             return cs;
6058         }
6059         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6060             return nodupIEXml(cs);
6061         }
6062         var d = ++key;
6063         cs[0]._nodup = d;
6064         for(i = 1; c = cs[i]; i++){
6065             if(c._nodup != d){
6066                 c._nodup = d;
6067             }else{
6068                 r = [];
6069                 for(var j = 0; j < i; j++){
6070                     r[++ri] = cs[j];
6071                 }
6072                 for(j = i+1; cj = cs[j]; j++){
6073                     if(cj._nodup != d){
6074                         cj._nodup = d;
6075                         r[++ri] = cj;
6076                     }
6077                 }
6078                 return r;
6079             }
6080         }
6081         return r;
6082     }
6083
6084     function quickDiffIEXml(c1, c2){
6085         var d = ++key;
6086         for(var i = 0, len = c1.length; i < len; i++){
6087             c1[i].setAttribute("_qdiff", d);
6088         }
6089         var r = [];
6090         for(var i = 0, len = c2.length; i < len; i++){
6091             if(c2[i].getAttribute("_qdiff") != d){
6092                 r[r.length] = c2[i];
6093             }
6094         }
6095         for(var i = 0, len = c1.length; i < len; i++){
6096            c1[i].removeAttribute("_qdiff");
6097         }
6098         return r;
6099     }
6100
6101     function quickDiff(c1, c2){
6102         var len1 = c1.length;
6103         if(!len1){
6104             return c2;
6105         }
6106         if(isIE && c1[0].selectSingleNode){
6107             return quickDiffIEXml(c1, c2);
6108         }
6109         var d = ++key;
6110         for(var i = 0; i < len1; i++){
6111             c1[i]._qdiff = d;
6112         }
6113         var r = [];
6114         for(var i = 0, len = c2.length; i < len; i++){
6115             if(c2[i]._qdiff != d){
6116                 r[r.length] = c2[i];
6117             }
6118         }
6119         return r;
6120     }
6121
6122     function quickId(ns, mode, root, id){
6123         if(ns == root){
6124            var d = root.ownerDocument || root;
6125            return d.getElementById(id);
6126         }
6127         ns = getNodes(ns, mode, "*");
6128         return byId(ns, null, id);
6129     }
6130
6131     return {
6132         getStyle : function(el, name){
6133             return Roo.fly(el).getStyle(name);
6134         },
6135         /**
6136          * Compiles a selector/xpath query into a reusable function. The returned function
6137          * takes one parameter "root" (optional), which is the context node from where the query should start.
6138          * @param {String} selector The selector/xpath query
6139          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6140          * @return {Function}
6141          */
6142         compile : function(path, type){
6143             type = type || "select";
6144             
6145             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6146             var q = path, mode, lq;
6147             var tk = Roo.DomQuery.matchers;
6148             var tklen = tk.length;
6149             var mm;
6150
6151             // accept leading mode switch
6152             var lmode = q.match(modeRe);
6153             if(lmode && lmode[1]){
6154                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6155                 q = q.replace(lmode[1], "");
6156             }
6157             // strip leading slashes
6158             while(path.substr(0, 1)=="/"){
6159                 path = path.substr(1);
6160             }
6161
6162             while(q && lq != q){
6163                 lq = q;
6164                 var tm = q.match(tagTokenRe);
6165                 if(type == "select"){
6166                     if(tm){
6167                         if(tm[1] == "#"){
6168                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6169                         }else{
6170                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6171                         }
6172                         q = q.replace(tm[0], "");
6173                     }else if(q.substr(0, 1) != '@'){
6174                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6175                     }
6176                 }else{
6177                     if(tm){
6178                         if(tm[1] == "#"){
6179                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6180                         }else{
6181                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6182                         }
6183                         q = q.replace(tm[0], "");
6184                     }
6185                 }
6186                 while(!(mm = q.match(modeRe))){
6187                     var matched = false;
6188                     for(var j = 0; j < tklen; j++){
6189                         var t = tk[j];
6190                         var m = q.match(t.re);
6191                         if(m){
6192                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6193                                                     return m[i];
6194                                                 });
6195                             q = q.replace(m[0], "");
6196                             matched = true;
6197                             break;
6198                         }
6199                     }
6200                     // prevent infinite loop on bad selector
6201                     if(!matched){
6202                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6203                     }
6204                 }
6205                 if(mm[1]){
6206                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6207                     q = q.replace(mm[1], "");
6208                 }
6209             }
6210             fn[fn.length] = "return nodup(n);\n}";
6211             
6212              /** 
6213               * list of variables that need from compression as they are used by eval.
6214              *  eval:var:batch 
6215              *  eval:var:nodup
6216              *  eval:var:byTag
6217              *  eval:var:ById
6218              *  eval:var:getNodes
6219              *  eval:var:quickId
6220              *  eval:var:mode
6221              *  eval:var:root
6222              *  eval:var:n
6223              *  eval:var:byClassName
6224              *  eval:var:byPseudo
6225              *  eval:var:byAttribute
6226              *  eval:var:attrValue
6227              * 
6228              **/ 
6229             eval(fn.join(""));
6230             return f;
6231         },
6232
6233         /**
6234          * Selects a group of elements.
6235          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6236          * @param {Node} root (optional) The start of the query (defaults to document).
6237          * @return {Array}
6238          */
6239         select : function(path, root, type){
6240             if(!root || root == document){
6241                 root = document;
6242             }
6243             if(typeof root == "string"){
6244                 root = document.getElementById(root);
6245             }
6246             var paths = path.split(",");
6247             var results = [];
6248             for(var i = 0, len = paths.length; i < len; i++){
6249                 var p = paths[i].replace(trimRe, "");
6250                 if(!cache[p]){
6251                     cache[p] = Roo.DomQuery.compile(p);
6252                     if(!cache[p]){
6253                         throw p + " is not a valid selector";
6254                     }
6255                 }
6256                 var result = cache[p](root);
6257                 if(result && result != document){
6258                     results = results.concat(result);
6259                 }
6260             }
6261             if(paths.length > 1){
6262                 return nodup(results);
6263             }
6264             return results;
6265         },
6266
6267         /**
6268          * Selects a single element.
6269          * @param {String} selector The selector/xpath query
6270          * @param {Node} root (optional) The start of the query (defaults to document).
6271          * @return {Element}
6272          */
6273         selectNode : function(path, root){
6274             return Roo.DomQuery.select(path, root)[0];
6275         },
6276
6277         /**
6278          * Selects the value of a node, optionally replacing null with the defaultValue.
6279          * @param {String} selector The selector/xpath query
6280          * @param {Node} root (optional) The start of the query (defaults to document).
6281          * @param {String} defaultValue
6282          */
6283         selectValue : function(path, root, defaultValue){
6284             path = path.replace(trimRe, "");
6285             if(!valueCache[path]){
6286                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6287             }
6288             var n = valueCache[path](root);
6289             n = n[0] ? n[0] : n;
6290             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6291             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6292         },
6293
6294         /**
6295          * Selects the value of a node, parsing integers and floats.
6296          * @param {String} selector The selector/xpath query
6297          * @param {Node} root (optional) The start of the query (defaults to document).
6298          * @param {Number} defaultValue
6299          * @return {Number}
6300          */
6301         selectNumber : function(path, root, defaultValue){
6302             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6303             return parseFloat(v);
6304         },
6305
6306         /**
6307          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6308          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6309          * @param {String} selector The simple selector to test
6310          * @return {Boolean}
6311          */
6312         is : function(el, ss){
6313             if(typeof el == "string"){
6314                 el = document.getElementById(el);
6315             }
6316             var isArray = (el instanceof Array);
6317             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6318             return isArray ? (result.length == el.length) : (result.length > 0);
6319         },
6320
6321         /**
6322          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6323          * @param {Array} el An array of elements to filter
6324          * @param {String} selector The simple selector to test
6325          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6326          * the selector instead of the ones that match
6327          * @return {Array}
6328          */
6329         filter : function(els, ss, nonMatches){
6330             ss = ss.replace(trimRe, "");
6331             if(!simpleCache[ss]){
6332                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6333             }
6334             var result = simpleCache[ss](els);
6335             return nonMatches ? quickDiff(result, els) : result;
6336         },
6337
6338         /**
6339          * Collection of matching regular expressions and code snippets.
6340          */
6341         matchers : [{
6342                 re: /^\.([\w-]+)/,
6343                 select: 'n = byClassName(n, null, " {1} ");'
6344             }, {
6345                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6346                 select: 'n = byPseudo(n, "{1}", "{2}");'
6347             },{
6348                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6349                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6350             }, {
6351                 re: /^#([\w-]+)/,
6352                 select: 'n = byId(n, null, "{1}");'
6353             },{
6354                 re: /^@([\w-]+)/,
6355                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6356             }
6357         ],
6358
6359         /**
6360          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6361          * 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;.
6362          */
6363         operators : {
6364             "=" : function(a, v){
6365                 return a == v;
6366             },
6367             "!=" : function(a, v){
6368                 return a != v;
6369             },
6370             "^=" : function(a, v){
6371                 return a && a.substr(0, v.length) == v;
6372             },
6373             "$=" : function(a, v){
6374                 return a && a.substr(a.length-v.length) == v;
6375             },
6376             "*=" : function(a, v){
6377                 return a && a.indexOf(v) !== -1;
6378             },
6379             "%=" : function(a, v){
6380                 return (a % v) == 0;
6381             },
6382             "|=" : function(a, v){
6383                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6384             },
6385             "~=" : function(a, v){
6386                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6387             }
6388         },
6389
6390         /**
6391          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6392          * and the argument (if any) supplied in the selector.
6393          */
6394         pseudos : {
6395             "first-child" : function(c){
6396                 var r = [], ri = -1, n;
6397                 for(var i = 0, ci; ci = n = c[i]; i++){
6398                     while((n = n.previousSibling) && n.nodeType != 1);
6399                     if(!n){
6400                         r[++ri] = ci;
6401                     }
6402                 }
6403                 return r;
6404             },
6405
6406             "last-child" : function(c){
6407                 var r = [], ri = -1, n;
6408                 for(var i = 0, ci; ci = n = c[i]; i++){
6409                     while((n = n.nextSibling) && n.nodeType != 1);
6410                     if(!n){
6411                         r[++ri] = ci;
6412                     }
6413                 }
6414                 return r;
6415             },
6416
6417             "nth-child" : function(c, a) {
6418                 var r = [], ri = -1;
6419                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6420                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6421                 for(var i = 0, n; n = c[i]; i++){
6422                     var pn = n.parentNode;
6423                     if (batch != pn._batch) {
6424                         var j = 0;
6425                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6426                             if(cn.nodeType == 1){
6427                                cn.nodeIndex = ++j;
6428                             }
6429                         }
6430                         pn._batch = batch;
6431                     }
6432                     if (f == 1) {
6433                         if (l == 0 || n.nodeIndex == l){
6434                             r[++ri] = n;
6435                         }
6436                     } else if ((n.nodeIndex + l) % f == 0){
6437                         r[++ri] = n;
6438                     }
6439                 }
6440
6441                 return r;
6442             },
6443
6444             "only-child" : function(c){
6445                 var r = [], ri = -1;;
6446                 for(var i = 0, ci; ci = c[i]; i++){
6447                     if(!prev(ci) && !next(ci)){
6448                         r[++ri] = ci;
6449                     }
6450                 }
6451                 return r;
6452             },
6453
6454             "empty" : function(c){
6455                 var r = [], ri = -1;
6456                 for(var i = 0, ci; ci = c[i]; i++){
6457                     var cns = ci.childNodes, j = 0, cn, empty = true;
6458                     while(cn = cns[j]){
6459                         ++j;
6460                         if(cn.nodeType == 1 || cn.nodeType == 3){
6461                             empty = false;
6462                             break;
6463                         }
6464                     }
6465                     if(empty){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "contains" : function(c, v){
6473                 var r = [], ri = -1;
6474                 for(var i = 0, ci; ci = c[i]; i++){
6475                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6476                         r[++ri] = ci;
6477                     }
6478                 }
6479                 return r;
6480             },
6481
6482             "nodeValue" : function(c, v){
6483                 var r = [], ri = -1;
6484                 for(var i = 0, ci; ci = c[i]; i++){
6485                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6486                         r[++ri] = ci;
6487                     }
6488                 }
6489                 return r;
6490             },
6491
6492             "checked" : function(c){
6493                 var r = [], ri = -1;
6494                 for(var i = 0, ci; ci = c[i]; i++){
6495                     if(ci.checked == true){
6496                         r[++ri] = ci;
6497                     }
6498                 }
6499                 return r;
6500             },
6501
6502             "not" : function(c, ss){
6503                 return Roo.DomQuery.filter(c, ss, true);
6504             },
6505
6506             "odd" : function(c){
6507                 return this["nth-child"](c, "odd");
6508             },
6509
6510             "even" : function(c){
6511                 return this["nth-child"](c, "even");
6512             },
6513
6514             "nth" : function(c, a){
6515                 return c[a-1] || [];
6516             },
6517
6518             "first" : function(c){
6519                 return c[0] || [];
6520             },
6521
6522             "last" : function(c){
6523                 return c[c.length-1] || [];
6524             },
6525
6526             "has" : function(c, ss){
6527                 var s = Roo.DomQuery.select;
6528                 var r = [], ri = -1;
6529                 for(var i = 0, ci; ci = c[i]; i++){
6530                     if(s(ss, ci).length > 0){
6531                         r[++ri] = ci;
6532                     }
6533                 }
6534                 return r;
6535             },
6536
6537             "next" : function(c, ss){
6538                 var is = Roo.DomQuery.is;
6539                 var r = [], ri = -1;
6540                 for(var i = 0, ci; ci = c[i]; i++){
6541                     var n = next(ci);
6542                     if(n && is(n, ss)){
6543                         r[++ri] = ci;
6544                     }
6545                 }
6546                 return r;
6547             },
6548
6549             "prev" : function(c, ss){
6550                 var is = Roo.DomQuery.is;
6551                 var r = [], ri = -1;
6552                 for(var i = 0, ci; ci = c[i]; i++){
6553                     var n = prev(ci);
6554                     if(n && is(n, ss)){
6555                         r[++ri] = ci;
6556                     }
6557                 }
6558                 return r;
6559             }
6560         }
6561     };
6562 }();
6563
6564 /**
6565  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6566  * @param {String} path The selector/xpath query
6567  * @param {Node} root (optional) The start of the query (defaults to document).
6568  * @return {Array}
6569  * @member Roo
6570  * @method query
6571  */
6572 Roo.query = Roo.DomQuery.select;
6573 /*
6574  * Based on:
6575  * Ext JS Library 1.1.1
6576  * Copyright(c) 2006-2007, Ext JS, LLC.
6577  *
6578  * Originally Released Under LGPL - original licence link has changed is not relivant.
6579  *
6580  * Fork - LGPL
6581  * <script type="text/javascript">
6582  */
6583
6584 /**
6585  * @class Roo.util.Observable
6586  * Base class that provides a common interface for publishing events. Subclasses are expected to
6587  * to have a property "events" with all the events defined.<br>
6588  * For example:
6589  * <pre><code>
6590  Employee = function(name){
6591     this.name = name;
6592     this.addEvents({
6593         "fired" : true,
6594         "quit" : true
6595     });
6596  }
6597  Roo.extend(Employee, Roo.util.Observable);
6598 </code></pre>
6599  * @param {Object} config properties to use (incuding events / listeners)
6600  */
6601
6602 Roo.util.Observable = function(cfg){
6603     
6604     cfg = cfg|| {};
6605     this.addEvents(cfg.events || {});
6606     if (cfg.events) {
6607         delete cfg.events; // make sure
6608     }
6609      
6610     Roo.apply(this, cfg);
6611     
6612     if(this.listeners){
6613         this.on(this.listeners);
6614         delete this.listeners;
6615     }
6616 };
6617 Roo.util.Observable.prototype = {
6618     /** 
6619  * @cfg {Object} listeners  list of events and functions to call for this object, 
6620  * For example :
6621  * <pre><code>
6622     listeners :  { 
6623        'click' : function(e) {
6624            ..... 
6625         } ,
6626         .... 
6627     } 
6628   </code></pre>
6629  */
6630     
6631     
6632     /**
6633      * Fires the specified event with the passed parameters (minus the event name).
6634      * @param {String} eventName
6635      * @param {Object...} args Variable number of parameters are passed to handlers
6636      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6637      */
6638     fireEvent : function(){
6639         var ce = this.events[arguments[0].toLowerCase()];
6640         if(typeof ce == "object"){
6641             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6642         }else{
6643             return true;
6644         }
6645     },
6646
6647     // private
6648     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6649
6650     /**
6651      * Appends an event handler to this component
6652      * @param {String}   eventName The type of event to listen for
6653      * @param {Function} handler The method the event invokes
6654      * @param {Object}   scope (optional) The scope in which to execute the handler
6655      * function. The handler function's "this" context.
6656      * @param {Object}   options (optional) An object containing handler configuration
6657      * properties. This may contain any of the following properties:<ul>
6658      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6659      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6660      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6661      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6662      * by the specified number of milliseconds. If the event fires again within that time, the original
6663      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6664      * </ul><br>
6665      * <p>
6666      * <b>Combining Options</b><br>
6667      * Using the options argument, it is possible to combine different types of listeners:<br>
6668      * <br>
6669      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6670                 <pre><code>
6671                 el.on('click', this.onClick, this, {
6672                         single: true,
6673                 delay: 100,
6674                 forumId: 4
6675                 });
6676                 </code></pre>
6677      * <p>
6678      * <b>Attaching multiple handlers in 1 call</b><br>
6679      * The method also allows for a single argument to be passed which is a config object containing properties
6680      * which specify multiple handlers.
6681      * <pre><code>
6682                 el.on({
6683                         'click': {
6684                         fn: this.onClick,
6685                         scope: this,
6686                         delay: 100
6687                 }, 
6688                 'mouseover': {
6689                         fn: this.onMouseOver,
6690                         scope: this
6691                 },
6692                 'mouseout': {
6693                         fn: this.onMouseOut,
6694                         scope: this
6695                 }
6696                 });
6697                 </code></pre>
6698      * <p>
6699      * Or a shorthand syntax which passes the same scope object to all handlers:
6700         <pre><code>
6701                 el.on({
6702                         'click': this.onClick,
6703                 'mouseover': this.onMouseOver,
6704                 'mouseout': this.onMouseOut,
6705                 scope: this
6706                 });
6707                 </code></pre>
6708      */
6709     addListener : function(eventName, fn, scope, o){
6710         if(typeof eventName == "object"){
6711             o = eventName;
6712             for(var e in o){
6713                 if(this.filterOptRe.test(e)){
6714                     continue;
6715                 }
6716                 if(typeof o[e] == "function"){
6717                     // shared options
6718                     this.addListener(e, o[e], o.scope,  o);
6719                 }else{
6720                     // individual options
6721                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6722                 }
6723             }
6724             return;
6725         }
6726         o = (!o || typeof o == "boolean") ? {} : o;
6727         eventName = eventName.toLowerCase();
6728         var ce = this.events[eventName] || true;
6729         if(typeof ce == "boolean"){
6730             ce = new Roo.util.Event(this, eventName);
6731             this.events[eventName] = ce;
6732         }
6733         ce.addListener(fn, scope, o);
6734     },
6735
6736     /**
6737      * Removes a listener
6738      * @param {String}   eventName     The type of event to listen for
6739      * @param {Function} handler        The handler to remove
6740      * @param {Object}   scope  (optional) The scope (this object) for the handler
6741      */
6742     removeListener : function(eventName, fn, scope){
6743         var ce = this.events[eventName.toLowerCase()];
6744         if(typeof ce == "object"){
6745             ce.removeListener(fn, scope);
6746         }
6747     },
6748
6749     /**
6750      * Removes all listeners for this object
6751      */
6752     purgeListeners : function(){
6753         for(var evt in this.events){
6754             if(typeof this.events[evt] == "object"){
6755                  this.events[evt].clearListeners();
6756             }
6757         }
6758     },
6759
6760     relayEvents : function(o, events){
6761         var createHandler = function(ename){
6762             return function(){
6763                  
6764                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6765             };
6766         };
6767         for(var i = 0, len = events.length; i < len; i++){
6768             var ename = events[i];
6769             if(!this.events[ename]){
6770                 this.events[ename] = true;
6771             };
6772             o.on(ename, createHandler(ename), this);
6773         }
6774     },
6775
6776     /**
6777      * Used to define events on this Observable
6778      * @param {Object} object The object with the events defined
6779      */
6780     addEvents : function(o){
6781         if(!this.events){
6782             this.events = {};
6783         }
6784         Roo.applyIf(this.events, o);
6785     },
6786
6787     /**
6788      * Checks to see if this object has any listeners for a specified event
6789      * @param {String} eventName The name of the event to check for
6790      * @return {Boolean} True if the event is being listened for, else false
6791      */
6792     hasListener : function(eventName){
6793         var e = this.events[eventName];
6794         return typeof e == "object" && e.listeners.length > 0;
6795     }
6796 };
6797 /**
6798  * Appends an event handler to this element (shorthand for addListener)
6799  * @param {String}   eventName     The type of event to listen for
6800  * @param {Function} handler        The method the event invokes
6801  * @param {Object}   scope (optional) The scope in which to execute the handler
6802  * function. The handler function's "this" context.
6803  * @param {Object}   options  (optional)
6804  * @method
6805  */
6806 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6807 /**
6808  * Removes a listener (shorthand for removeListener)
6809  * @param {String}   eventName     The type of event to listen for
6810  * @param {Function} handler        The handler to remove
6811  * @param {Object}   scope  (optional) The scope (this object) for the handler
6812  * @method
6813  */
6814 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6815
6816 /**
6817  * Starts capture on the specified Observable. All events will be passed
6818  * to the supplied function with the event name + standard signature of the event
6819  * <b>before</b> the event is fired. If the supplied function returns false,
6820  * the event will not fire.
6821  * @param {Observable} o The Observable to capture
6822  * @param {Function} fn The function to call
6823  * @param {Object} scope (optional) The scope (this object) for the fn
6824  * @static
6825  */
6826 Roo.util.Observable.capture = function(o, fn, scope){
6827     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6828 };
6829
6830 /**
6831  * Removes <b>all</b> added captures from the Observable.
6832  * @param {Observable} o The Observable to release
6833  * @static
6834  */
6835 Roo.util.Observable.releaseCapture = function(o){
6836     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6837 };
6838
6839 (function(){
6840
6841     var createBuffered = function(h, o, scope){
6842         var task = new Roo.util.DelayedTask();
6843         return function(){
6844             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6845         };
6846     };
6847
6848     var createSingle = function(h, e, fn, scope){
6849         return function(){
6850             e.removeListener(fn, scope);
6851             return h.apply(scope, arguments);
6852         };
6853     };
6854
6855     var createDelayed = function(h, o, scope){
6856         return function(){
6857             var args = Array.prototype.slice.call(arguments, 0);
6858             setTimeout(function(){
6859                 h.apply(scope, args);
6860             }, o.delay || 10);
6861         };
6862     };
6863
6864     Roo.util.Event = function(obj, name){
6865         this.name = name;
6866         this.obj = obj;
6867         this.listeners = [];
6868     };
6869
6870     Roo.util.Event.prototype = {
6871         addListener : function(fn, scope, options){
6872             var o = options || {};
6873             scope = scope || this.obj;
6874             if(!this.isListening(fn, scope)){
6875                 var l = {fn: fn, scope: scope, options: o};
6876                 var h = fn;
6877                 if(o.delay){
6878                     h = createDelayed(h, o, scope);
6879                 }
6880                 if(o.single){
6881                     h = createSingle(h, this, fn, scope);
6882                 }
6883                 if(o.buffer){
6884                     h = createBuffered(h, o, scope);
6885                 }
6886                 l.fireFn = h;
6887                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6888                     this.listeners.push(l);
6889                 }else{
6890                     this.listeners = this.listeners.slice(0);
6891                     this.listeners.push(l);
6892                 }
6893             }
6894         },
6895
6896         findListener : function(fn, scope){
6897             scope = scope || this.obj;
6898             var ls = this.listeners;
6899             for(var i = 0, len = ls.length; i < len; i++){
6900                 var l = ls[i];
6901                 if(l.fn == fn && l.scope == scope){
6902                     return i;
6903                 }
6904             }
6905             return -1;
6906         },
6907
6908         isListening : function(fn, scope){
6909             return this.findListener(fn, scope) != -1;
6910         },
6911
6912         removeListener : function(fn, scope){
6913             var index;
6914             if((index = this.findListener(fn, scope)) != -1){
6915                 if(!this.firing){
6916                     this.listeners.splice(index, 1);
6917                 }else{
6918                     this.listeners = this.listeners.slice(0);
6919                     this.listeners.splice(index, 1);
6920                 }
6921                 return true;
6922             }
6923             return false;
6924         },
6925
6926         clearListeners : function(){
6927             this.listeners = [];
6928         },
6929
6930         fire : function(){
6931             var ls = this.listeners, scope, len = ls.length;
6932             if(len > 0){
6933                 this.firing = true;
6934                 var args = Array.prototype.slice.call(arguments, 0);                
6935                 for(var i = 0; i < len; i++){
6936                     var l = ls[i];
6937                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6938                         this.firing = false;
6939                         return false;
6940                     }
6941                 }
6942                 this.firing = false;
6943             }
6944             return true;
6945         }
6946     };
6947 })();/*
6948  * RooJS Library 
6949  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6950  *
6951  * Licence LGPL 
6952  *
6953  */
6954  
6955 /**
6956  * @class Roo.Document
6957  * @extends Roo.util.Observable
6958  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6959  * 
6960  * @param {Object} config the methods and properties of the 'base' class for the application.
6961  * 
6962  *  Generic Page handler - implement this to start your app..
6963  * 
6964  * eg.
6965  *  MyProject = new Roo.Document({
6966         events : {
6967             'load' : true // your events..
6968         },
6969         listeners : {
6970             'ready' : function() {
6971                 // fired on Roo.onReady()
6972             }
6973         }
6974  * 
6975  */
6976 Roo.Document = function(cfg) {
6977      
6978     this.addEvents({ 
6979         'ready' : true
6980     });
6981     Roo.util.Observable.call(this,cfg);
6982     
6983     var _this = this;
6984     
6985     Roo.onReady(function() {
6986         _this.fireEvent('ready');
6987     },null,false);
6988     
6989     
6990 }
6991
6992 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6993  * Based on:
6994  * Ext JS Library 1.1.1
6995  * Copyright(c) 2006-2007, Ext JS, LLC.
6996  *
6997  * Originally Released Under LGPL - original licence link has changed is not relivant.
6998  *
6999  * Fork - LGPL
7000  * <script type="text/javascript">
7001  */
7002
7003 /**
7004  * @class Roo.EventManager
7005  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7006  * several useful events directly.
7007  * See {@link Roo.EventObject} for more details on normalized event objects.
7008  * @static
7009  */
7010 Roo.EventManager = function(){
7011     var docReadyEvent, docReadyProcId, docReadyState = false;
7012     var resizeEvent, resizeTask, textEvent, textSize;
7013     var E = Roo.lib.Event;
7014     var D = Roo.lib.Dom;
7015
7016     
7017     
7018
7019     var fireDocReady = function(){
7020         if(!docReadyState){
7021             docReadyState = true;
7022             Roo.isReady = true;
7023             if(docReadyProcId){
7024                 clearInterval(docReadyProcId);
7025             }
7026             if(Roo.isGecko || Roo.isOpera) {
7027                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7028             }
7029             if(Roo.isIE){
7030                 var defer = document.getElementById("ie-deferred-loader");
7031                 if(defer){
7032                     defer.onreadystatechange = null;
7033                     defer.parentNode.removeChild(defer);
7034                 }
7035             }
7036             if(docReadyEvent){
7037                 docReadyEvent.fire();
7038                 docReadyEvent.clearListeners();
7039             }
7040         }
7041     };
7042     
7043     var initDocReady = function(){
7044         docReadyEvent = new Roo.util.Event();
7045         if(Roo.isGecko || Roo.isOpera) {
7046             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7047         }else if(Roo.isIE){
7048             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7049             var defer = document.getElementById("ie-deferred-loader");
7050             defer.onreadystatechange = function(){
7051                 if(this.readyState == "complete"){
7052                     fireDocReady();
7053                 }
7054             };
7055         }else if(Roo.isSafari){ 
7056             docReadyProcId = setInterval(function(){
7057                 var rs = document.readyState;
7058                 if(rs == "complete") {
7059                     fireDocReady();     
7060                  }
7061             }, 10);
7062         }
7063         // no matter what, make sure it fires on load
7064         E.on(window, "load", fireDocReady);
7065     };
7066
7067     var createBuffered = function(h, o){
7068         var task = new Roo.util.DelayedTask(h);
7069         return function(e){
7070             // create new event object impl so new events don't wipe out properties
7071             e = new Roo.EventObjectImpl(e);
7072             task.delay(o.buffer, h, null, [e]);
7073         };
7074     };
7075
7076     var createSingle = function(h, el, ename, fn){
7077         return function(e){
7078             Roo.EventManager.removeListener(el, ename, fn);
7079             h(e);
7080         };
7081     };
7082
7083     var createDelayed = function(h, o){
7084         return function(e){
7085             // create new event object impl so new events don't wipe out properties
7086             e = new Roo.EventObjectImpl(e);
7087             setTimeout(function(){
7088                 h(e);
7089             }, o.delay || 10);
7090         };
7091     };
7092     var transitionEndVal = false;
7093     
7094     var transitionEnd = function()
7095     {
7096         if (transitionEndVal) {
7097             return transitionEndVal;
7098         }
7099         var el = document.createElement('div');
7100
7101         var transEndEventNames = {
7102             WebkitTransition : 'webkitTransitionEnd',
7103             MozTransition    : 'transitionend',
7104             OTransition      : 'oTransitionEnd otransitionend',
7105             transition       : 'transitionend'
7106         };
7107     
7108         for (var name in transEndEventNames) {
7109             if (el.style[name] !== undefined) {
7110                 transitionEndVal = transEndEventNames[name];
7111                 return  transitionEndVal ;
7112             }
7113         }
7114     }
7115     
7116   
7117
7118     var listen = function(element, ename, opt, fn, scope)
7119     {
7120         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7121         fn = fn || o.fn; scope = scope || o.scope;
7122         var el = Roo.getDom(element);
7123         
7124         
7125         if(!el){
7126             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7127         }
7128         
7129         if (ename == 'transitionend') {
7130             ename = transitionEnd();
7131         }
7132         var h = function(e){
7133             e = Roo.EventObject.setEvent(e);
7134             var t;
7135             if(o.delegate){
7136                 t = e.getTarget(o.delegate, el);
7137                 if(!t){
7138                     return;
7139                 }
7140             }else{
7141                 t = e.target;
7142             }
7143             if(o.stopEvent === true){
7144                 e.stopEvent();
7145             }
7146             if(o.preventDefault === true){
7147                e.preventDefault();
7148             }
7149             if(o.stopPropagation === true){
7150                 e.stopPropagation();
7151             }
7152
7153             if(o.normalized === false){
7154                 e = e.browserEvent;
7155             }
7156
7157             fn.call(scope || el, e, t, o);
7158         };
7159         if(o.delay){
7160             h = createDelayed(h, o);
7161         }
7162         if(o.single){
7163             h = createSingle(h, el, ename, fn);
7164         }
7165         if(o.buffer){
7166             h = createBuffered(h, o);
7167         }
7168         
7169         fn._handlers = fn._handlers || [];
7170         
7171         
7172         fn._handlers.push([Roo.id(el), ename, h]);
7173         
7174         
7175          
7176         E.on(el, ename, h); // this adds the actuall listener to the object..
7177         
7178         
7179         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7180             el.addEventListener("DOMMouseScroll", h, false);
7181             E.on(window, 'unload', function(){
7182                 el.removeEventListener("DOMMouseScroll", h, false);
7183             });
7184         }
7185         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7186             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7187         }
7188         return h;
7189     };
7190
7191     var stopListening = function(el, ename, fn){
7192         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7193         if(hds){
7194             for(var i = 0, len = hds.length; i < len; i++){
7195                 var h = hds[i];
7196                 if(h[0] == id && h[1] == ename){
7197                     hd = h[2];
7198                     hds.splice(i, 1);
7199                     break;
7200                 }
7201             }
7202         }
7203         E.un(el, ename, hd);
7204         el = Roo.getDom(el);
7205         if(ename == "mousewheel" && el.addEventListener){
7206             el.removeEventListener("DOMMouseScroll", hd, false);
7207         }
7208         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7209             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7210         }
7211     };
7212
7213     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7214     
7215     var pub = {
7216         
7217         
7218         /** 
7219          * Fix for doc tools
7220          * @scope Roo.EventManager
7221          */
7222         
7223         
7224         /** 
7225          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7226          * object with a Roo.EventObject
7227          * @param {Function} fn        The method the event invokes
7228          * @param {Object}   scope    An object that becomes the scope of the handler
7229          * @param {boolean}  override If true, the obj passed in becomes
7230          *                             the execution scope of the listener
7231          * @return {Function} The wrapped function
7232          * @deprecated
7233          */
7234         wrap : function(fn, scope, override){
7235             return function(e){
7236                 Roo.EventObject.setEvent(e);
7237                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7238             };
7239         },
7240         
7241         /**
7242      * Appends an event handler to an element (shorthand for addListener)
7243      * @param {String/HTMLElement}   element        The html element or id to assign the
7244      * @param {String}   eventName The type of event to listen for
7245      * @param {Function} handler The method the event invokes
7246      * @param {Object}   scope (optional) The scope in which to execute the handler
7247      * function. The handler function's "this" context.
7248      * @param {Object}   options (optional) An object containing handler configuration
7249      * properties. This may contain any of the following properties:<ul>
7250      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7251      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7252      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7253      * <li>preventDefault {Boolean} True to prevent the default action</li>
7254      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7255      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7256      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7257      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7258      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7259      * by the specified number of milliseconds. If the event fires again within that time, the original
7260      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7261      * </ul><br>
7262      * <p>
7263      * <b>Combining Options</b><br>
7264      * Using the options argument, it is possible to combine different types of listeners:<br>
7265      * <br>
7266      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7267      * Code:<pre><code>
7268 el.on('click', this.onClick, this, {
7269     single: true,
7270     delay: 100,
7271     stopEvent : true,
7272     forumId: 4
7273 });</code></pre>
7274      * <p>
7275      * <b>Attaching multiple handlers in 1 call</b><br>
7276       * The method also allows for a single argument to be passed which is a config object containing properties
7277      * which specify multiple handlers.
7278      * <p>
7279      * Code:<pre><code>
7280 el.on({
7281     'click' : {
7282         fn: this.onClick
7283         scope: this,
7284         delay: 100
7285     },
7286     'mouseover' : {
7287         fn: this.onMouseOver
7288         scope: this
7289     },
7290     'mouseout' : {
7291         fn: this.onMouseOut
7292         scope: this
7293     }
7294 });</code></pre>
7295      * <p>
7296      * Or a shorthand syntax:<br>
7297      * Code:<pre><code>
7298 el.on({
7299     'click' : this.onClick,
7300     'mouseover' : this.onMouseOver,
7301     'mouseout' : this.onMouseOut
7302     scope: this
7303 });</code></pre>
7304      */
7305         addListener : function(element, eventName, fn, scope, options){
7306             if(typeof eventName == "object"){
7307                 var o = eventName;
7308                 for(var e in o){
7309                     if(propRe.test(e)){
7310                         continue;
7311                     }
7312                     if(typeof o[e] == "function"){
7313                         // shared options
7314                         listen(element, e, o, o[e], o.scope);
7315                     }else{
7316                         // individual options
7317                         listen(element, e, o[e]);
7318                     }
7319                 }
7320                 return;
7321             }
7322             return listen(element, eventName, options, fn, scope);
7323         },
7324         
7325         /**
7326          * Removes an event handler
7327          *
7328          * @param {String/HTMLElement}   element        The id or html element to remove the 
7329          *                             event from
7330          * @param {String}   eventName     The type of event
7331          * @param {Function} fn
7332          * @return {Boolean} True if a listener was actually removed
7333          */
7334         removeListener : function(element, eventName, fn){
7335             return stopListening(element, eventName, fn);
7336         },
7337         
7338         /**
7339          * Fires when the document is ready (before onload and before images are loaded). Can be 
7340          * accessed shorthanded Roo.onReady().
7341          * @param {Function} fn        The method the event invokes
7342          * @param {Object}   scope    An  object that becomes the scope of the handler
7343          * @param {boolean}  options
7344          */
7345         onDocumentReady : function(fn, scope, options){
7346             if(docReadyState){ // if it already fired
7347                 docReadyEvent.addListener(fn, scope, options);
7348                 docReadyEvent.fire();
7349                 docReadyEvent.clearListeners();
7350                 return;
7351             }
7352             if(!docReadyEvent){
7353                 initDocReady();
7354             }
7355             docReadyEvent.addListener(fn, scope, options);
7356         },
7357         
7358         /**
7359          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onWindowResize : function(fn, scope, options)
7365         {
7366             if(!resizeEvent){
7367                 resizeEvent = new Roo.util.Event();
7368                 resizeTask = new Roo.util.DelayedTask(function(){
7369                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7370                 });
7371                 E.on(window, "resize", function()
7372                 {
7373                     if (Roo.isIE) {
7374                         resizeTask.delay(50);
7375                     } else {
7376                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7377                     }
7378                 });
7379             }
7380             resizeEvent.addListener(fn, scope, options);
7381         },
7382
7383         /**
7384          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7385          * @param {Function} fn        The method the event invokes
7386          * @param {Object}   scope    An object that becomes the scope of the handler
7387          * @param {boolean}  options
7388          */
7389         onTextResize : function(fn, scope, options){
7390             if(!textEvent){
7391                 textEvent = new Roo.util.Event();
7392                 var textEl = new Roo.Element(document.createElement('div'));
7393                 textEl.dom.className = 'x-text-resize';
7394                 textEl.dom.innerHTML = 'X';
7395                 textEl.appendTo(document.body);
7396                 textSize = textEl.dom.offsetHeight;
7397                 setInterval(function(){
7398                     if(textEl.dom.offsetHeight != textSize){
7399                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7400                     }
7401                 }, this.textResizeInterval);
7402             }
7403             textEvent.addListener(fn, scope, options);
7404         },
7405
7406         /**
7407          * Removes the passed window resize listener.
7408          * @param {Function} fn        The method the event invokes
7409          * @param {Object}   scope    The scope of handler
7410          */
7411         removeResizeListener : function(fn, scope){
7412             if(resizeEvent){
7413                 resizeEvent.removeListener(fn, scope);
7414             }
7415         },
7416
7417         // private
7418         fireResize : function(){
7419             if(resizeEvent){
7420                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7421             }   
7422         },
7423         /**
7424          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7425          */
7426         ieDeferSrc : false,
7427         /**
7428          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7429          */
7430         textResizeInterval : 50
7431     };
7432     
7433     /**
7434      * Fix for doc tools
7435      * @scopeAlias pub=Roo.EventManager
7436      */
7437     
7438      /**
7439      * Appends an event handler to an element (shorthand for addListener)
7440      * @param {String/HTMLElement}   element        The html element or id to assign the
7441      * @param {String}   eventName The type of event to listen for
7442      * @param {Function} handler The method the event invokes
7443      * @param {Object}   scope (optional) The scope in which to execute the handler
7444      * function. The handler function's "this" context.
7445      * @param {Object}   options (optional) An object containing handler configuration
7446      * properties. This may contain any of the following properties:<ul>
7447      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7448      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7449      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7450      * <li>preventDefault {Boolean} True to prevent the default action</li>
7451      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7452      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7453      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7454      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7455      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7456      * by the specified number of milliseconds. If the event fires again within that time, the original
7457      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7458      * </ul><br>
7459      * <p>
7460      * <b>Combining Options</b><br>
7461      * Using the options argument, it is possible to combine different types of listeners:<br>
7462      * <br>
7463      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7464      * Code:<pre><code>
7465 el.on('click', this.onClick, this, {
7466     single: true,
7467     delay: 100,
7468     stopEvent : true,
7469     forumId: 4
7470 });</code></pre>
7471      * <p>
7472      * <b>Attaching multiple handlers in 1 call</b><br>
7473       * The method also allows for a single argument to be passed which is a config object containing properties
7474      * which specify multiple handlers.
7475      * <p>
7476      * Code:<pre><code>
7477 el.on({
7478     'click' : {
7479         fn: this.onClick
7480         scope: this,
7481         delay: 100
7482     },
7483     'mouseover' : {
7484         fn: this.onMouseOver
7485         scope: this
7486     },
7487     'mouseout' : {
7488         fn: this.onMouseOut
7489         scope: this
7490     }
7491 });</code></pre>
7492      * <p>
7493      * Or a shorthand syntax:<br>
7494      * Code:<pre><code>
7495 el.on({
7496     'click' : this.onClick,
7497     'mouseover' : this.onMouseOver,
7498     'mouseout' : this.onMouseOut
7499     scope: this
7500 });</code></pre>
7501      */
7502     pub.on = pub.addListener;
7503     pub.un = pub.removeListener;
7504
7505     pub.stoppedMouseDownEvent = new Roo.util.Event();
7506     return pub;
7507 }();
7508 /**
7509   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7510   * @param {Function} fn        The method the event invokes
7511   * @param {Object}   scope    An  object that becomes the scope of the handler
7512   * @param {boolean}  override If true, the obj passed in becomes
7513   *                             the execution scope of the listener
7514   * @member Roo
7515   * @method onReady
7516  */
7517 Roo.onReady = Roo.EventManager.onDocumentReady;
7518
7519 Roo.onReady(function(){
7520     var bd = Roo.get(document.body);
7521     if(!bd){ return; }
7522
7523     var cls = [
7524             Roo.isIE ? "roo-ie"
7525             : Roo.isIE11 ? "roo-ie11"
7526             : Roo.isEdge ? "roo-edge"
7527             : Roo.isGecko ? "roo-gecko"
7528             : Roo.isOpera ? "roo-opera"
7529             : Roo.isSafari ? "roo-safari" : ""];
7530
7531     if(Roo.isMac){
7532         cls.push("roo-mac");
7533     }
7534     if(Roo.isLinux){
7535         cls.push("roo-linux");
7536     }
7537     if(Roo.isIOS){
7538         cls.push("roo-ios");
7539     }
7540     if(Roo.isTouch){
7541         cls.push("roo-touch");
7542     }
7543     if(Roo.isBorderBox){
7544         cls.push('roo-border-box');
7545     }
7546     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7547         var p = bd.dom.parentNode;
7548         if(p){
7549             p.className += ' roo-strict';
7550         }
7551     }
7552     bd.addClass(cls.join(' '));
7553 });
7554
7555 /**
7556  * @class Roo.EventObject
7557  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7558  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7559  * Example:
7560  * <pre><code>
7561  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7562     e.preventDefault();
7563     var target = e.getTarget();
7564     ...
7565  }
7566  var myDiv = Roo.get("myDiv");
7567  myDiv.on("click", handleClick);
7568  //or
7569  Roo.EventManager.on("myDiv", 'click', handleClick);
7570  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7571  </code></pre>
7572  * @static
7573  */
7574 Roo.EventObject = function(){
7575     
7576     var E = Roo.lib.Event;
7577     
7578     // safari keypress events for special keys return bad keycodes
7579     var safariKeys = {
7580         63234 : 37, // left
7581         63235 : 39, // right
7582         63232 : 38, // up
7583         63233 : 40, // down
7584         63276 : 33, // page up
7585         63277 : 34, // page down
7586         63272 : 46, // delete
7587         63273 : 36, // home
7588         63275 : 35  // end
7589     };
7590
7591     // normalize button clicks
7592     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7593                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7594
7595     Roo.EventObjectImpl = function(e){
7596         if(e){
7597             this.setEvent(e.browserEvent || e);
7598         }
7599     };
7600     Roo.EventObjectImpl.prototype = {
7601         /**
7602          * Used to fix doc tools.
7603          * @scope Roo.EventObject.prototype
7604          */
7605             
7606
7607         
7608         
7609         /** The normal browser event */
7610         browserEvent : null,
7611         /** The button pressed in a mouse event */
7612         button : -1,
7613         /** True if the shift key was down during the event */
7614         shiftKey : false,
7615         /** True if the control key was down during the event */
7616         ctrlKey : false,
7617         /** True if the alt key was down during the event */
7618         altKey : false,
7619
7620         /** Key constant 
7621         * @type Number */
7622         BACKSPACE : 8,
7623         /** Key constant 
7624         * @type Number */
7625         TAB : 9,
7626         /** Key constant 
7627         * @type Number */
7628         RETURN : 13,
7629         /** Key constant 
7630         * @type Number */
7631         ENTER : 13,
7632         /** Key constant 
7633         * @type Number */
7634         SHIFT : 16,
7635         /** Key constant 
7636         * @type Number */
7637         CONTROL : 17,
7638         /** Key constant 
7639         * @type Number */
7640         ESC : 27,
7641         /** Key constant 
7642         * @type Number */
7643         SPACE : 32,
7644         /** Key constant 
7645         * @type Number */
7646         PAGEUP : 33,
7647         /** Key constant 
7648         * @type Number */
7649         PAGEDOWN : 34,
7650         /** Key constant 
7651         * @type Number */
7652         END : 35,
7653         /** Key constant 
7654         * @type Number */
7655         HOME : 36,
7656         /** Key constant 
7657         * @type Number */
7658         LEFT : 37,
7659         /** Key constant 
7660         * @type Number */
7661         UP : 38,
7662         /** Key constant 
7663         * @type Number */
7664         RIGHT : 39,
7665         /** Key constant 
7666         * @type Number */
7667         DOWN : 40,
7668         /** Key constant 
7669         * @type Number */
7670         DELETE : 46,
7671         /** Key constant 
7672         * @type Number */
7673         F5 : 116,
7674
7675            /** @private */
7676         setEvent : function(e){
7677             if(e == this || (e && e.browserEvent)){ // already wrapped
7678                 return e;
7679             }
7680             this.browserEvent = e;
7681             if(e){
7682                 // normalize buttons
7683                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7684                 if(e.type == 'click' && this.button == -1){
7685                     this.button = 0;
7686                 }
7687                 this.type = e.type;
7688                 this.shiftKey = e.shiftKey;
7689                 // mac metaKey behaves like ctrlKey
7690                 this.ctrlKey = e.ctrlKey || e.metaKey;
7691                 this.altKey = e.altKey;
7692                 // in getKey these will be normalized for the mac
7693                 this.keyCode = e.keyCode;
7694                 // keyup warnings on firefox.
7695                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7696                 // cache the target for the delayed and or buffered events
7697                 this.target = E.getTarget(e);
7698                 // same for XY
7699                 this.xy = E.getXY(e);
7700             }else{
7701                 this.button = -1;
7702                 this.shiftKey = false;
7703                 this.ctrlKey = false;
7704                 this.altKey = false;
7705                 this.keyCode = 0;
7706                 this.charCode =0;
7707                 this.target = null;
7708                 this.xy = [0, 0];
7709             }
7710             return this;
7711         },
7712
7713         /**
7714          * Stop the event (preventDefault and stopPropagation)
7715          */
7716         stopEvent : function(){
7717             if(this.browserEvent){
7718                 if(this.browserEvent.type == 'mousedown'){
7719                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7720                 }
7721                 E.stopEvent(this.browserEvent);
7722             }
7723         },
7724
7725         /**
7726          * Prevents the browsers default handling of the event.
7727          */
7728         preventDefault : function(){
7729             if(this.browserEvent){
7730                 E.preventDefault(this.browserEvent);
7731             }
7732         },
7733
7734         /** @private */
7735         isNavKeyPress : function(){
7736             var k = this.keyCode;
7737             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7738             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7739         },
7740
7741         isSpecialKey : function(){
7742             var k = this.keyCode;
7743             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7744             (k == 16) || (k == 17) ||
7745             (k >= 18 && k <= 20) ||
7746             (k >= 33 && k <= 35) ||
7747             (k >= 36 && k <= 39) ||
7748             (k >= 44 && k <= 45);
7749         },
7750         /**
7751          * Cancels bubbling of the event.
7752          */
7753         stopPropagation : function(){
7754             if(this.browserEvent){
7755                 if(this.type == 'mousedown'){
7756                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7757                 }
7758                 E.stopPropagation(this.browserEvent);
7759             }
7760         },
7761
7762         /**
7763          * Gets the key code for the event.
7764          * @return {Number}
7765          */
7766         getCharCode : function(){
7767             return this.charCode || this.keyCode;
7768         },
7769
7770         /**
7771          * Returns a normalized keyCode for the event.
7772          * @return {Number} The key code
7773          */
7774         getKey : function(){
7775             var k = this.keyCode || this.charCode;
7776             return Roo.isSafari ? (safariKeys[k] || k) : k;
7777         },
7778
7779         /**
7780          * Gets the x coordinate of the event.
7781          * @return {Number}
7782          */
7783         getPageX : function(){
7784             return this.xy[0];
7785         },
7786
7787         /**
7788          * Gets the y coordinate of the event.
7789          * @return {Number}
7790          */
7791         getPageY : function(){
7792             return this.xy[1];
7793         },
7794
7795         /**
7796          * Gets the time of the event.
7797          * @return {Number}
7798          */
7799         getTime : function(){
7800             if(this.browserEvent){
7801                 return E.getTime(this.browserEvent);
7802             }
7803             return null;
7804         },
7805
7806         /**
7807          * Gets the page coordinates of the event.
7808          * @return {Array} The xy values like [x, y]
7809          */
7810         getXY : function(){
7811             return this.xy;
7812         },
7813
7814         /**
7815          * Gets the target for the event.
7816          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7817          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7818                 search as a number or element (defaults to 10 || document.body)
7819          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7820          * @return {HTMLelement}
7821          */
7822         getTarget : function(selector, maxDepth, returnEl){
7823             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7824         },
7825         /**
7826          * Gets the related target.
7827          * @return {HTMLElement}
7828          */
7829         getRelatedTarget : function(){
7830             if(this.browserEvent){
7831                 return E.getRelatedTarget(this.browserEvent);
7832             }
7833             return null;
7834         },
7835
7836         /**
7837          * Normalizes mouse wheel delta across browsers
7838          * @return {Number} The delta
7839          */
7840         getWheelDelta : function(){
7841             var e = this.browserEvent;
7842             var delta = 0;
7843             if(e.wheelDelta){ /* IE/Opera. */
7844                 delta = e.wheelDelta/120;
7845             }else if(e.detail){ /* Mozilla case. */
7846                 delta = -e.detail/3;
7847             }
7848             return delta;
7849         },
7850
7851         /**
7852          * Returns true if the control, meta, shift or alt key was pressed during this event.
7853          * @return {Boolean}
7854          */
7855         hasModifier : function(){
7856             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7857         },
7858
7859         /**
7860          * Returns true if the target of this event equals el or is a child of el
7861          * @param {String/HTMLElement/Element} el
7862          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7863          * @return {Boolean}
7864          */
7865         within : function(el, related){
7866             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7867             return t && Roo.fly(el).contains(t);
7868         },
7869
7870         getPoint : function(){
7871             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7872         }
7873     };
7874
7875     return new Roo.EventObjectImpl();
7876 }();
7877             
7878     /*
7879  * Based on:
7880  * Ext JS Library 1.1.1
7881  * Copyright(c) 2006-2007, Ext JS, LLC.
7882  *
7883  * Originally Released Under LGPL - original licence link has changed is not relivant.
7884  *
7885  * Fork - LGPL
7886  * <script type="text/javascript">
7887  */
7888
7889  
7890 // was in Composite Element!??!?!
7891  
7892 (function(){
7893     var D = Roo.lib.Dom;
7894     var E = Roo.lib.Event;
7895     var A = Roo.lib.Anim;
7896
7897     // local style camelizing for speed
7898     var propCache = {};
7899     var camelRe = /(-[a-z])/gi;
7900     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7901     var view = document.defaultView;
7902
7903 /**
7904  * @class Roo.Element
7905  * Represents an Element in the DOM.<br><br>
7906  * Usage:<br>
7907 <pre><code>
7908 var el = Roo.get("my-div");
7909
7910 // or with getEl
7911 var el = getEl("my-div");
7912
7913 // or with a DOM element
7914 var el = Roo.get(myDivElement);
7915 </code></pre>
7916  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7917  * each call instead of constructing a new one.<br><br>
7918  * <b>Animations</b><br />
7919  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7920  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7921 <pre>
7922 Option    Default   Description
7923 --------- --------  ---------------------------------------------
7924 duration  .35       The duration of the animation in seconds
7925 easing    easeOut   The YUI easing method
7926 callback  none      A function to execute when the anim completes
7927 scope     this      The scope (this) of the callback function
7928 </pre>
7929 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7930 * manipulate the animation. Here's an example:
7931 <pre><code>
7932 var el = Roo.get("my-div");
7933
7934 // no animation
7935 el.setWidth(100);
7936
7937 // default animation
7938 el.setWidth(100, true);
7939
7940 // animation with some options set
7941 el.setWidth(100, {
7942     duration: 1,
7943     callback: this.foo,
7944     scope: this
7945 });
7946
7947 // using the "anim" property to get the Anim object
7948 var opt = {
7949     duration: 1,
7950     callback: this.foo,
7951     scope: this
7952 };
7953 el.setWidth(100, opt);
7954 ...
7955 if(opt.anim.isAnimated()){
7956     opt.anim.stop();
7957 }
7958 </code></pre>
7959 * <b> Composite (Collections of) Elements</b><br />
7960  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7961  * @constructor Create a new Element directly.
7962  * @param {String/HTMLElement} element
7963  * @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).
7964  */
7965     Roo.Element = function(element, forceNew)
7966     {
7967         var dom = typeof element == "string" ?
7968                 document.getElementById(element) : element;
7969         
7970         this.listeners = {};
7971         
7972         if(!dom){ // invalid id/element
7973             return null;
7974         }
7975         var id = dom.id;
7976         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7977             return Roo.Element.cache[id];
7978         }
7979
7980         /**
7981          * The DOM element
7982          * @type HTMLElement
7983          */
7984         this.dom = dom;
7985
7986         /**
7987          * The DOM element ID
7988          * @type String
7989          */
7990         this.id = id || Roo.id(dom);
7991         
7992         return this; // assumed for cctor?
7993     };
7994
7995     var El = Roo.Element;
7996
7997     El.prototype = {
7998         /**
7999          * The element's default display mode  (defaults to "") 
8000          * @type String
8001          */
8002         originalDisplay : "",
8003
8004         
8005         // note this is overridden in BS version..
8006         visibilityMode : 1, 
8007         /**
8008          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8009          * @type String
8010          */
8011         defaultUnit : "px",
8012         
8013         /**
8014          * Sets the element's visibility mode. When setVisible() is called it
8015          * will use this to determine whether to set the visibility or the display property.
8016          * @param visMode Element.VISIBILITY or Element.DISPLAY
8017          * @return {Roo.Element} this
8018          */
8019         setVisibilityMode : function(visMode){
8020             this.visibilityMode = visMode;
8021             return this;
8022         },
8023         /**
8024          * Convenience method for setVisibilityMode(Element.DISPLAY)
8025          * @param {String} display (optional) What to set display to when visible
8026          * @return {Roo.Element} this
8027          */
8028         enableDisplayMode : function(display){
8029             this.setVisibilityMode(El.DISPLAY);
8030             if(typeof display != "undefined") { this.originalDisplay = display; }
8031             return this;
8032         },
8033
8034         /**
8035          * 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)
8036          * @param {String} selector The simple selector to test
8037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8038                 search as a number or element (defaults to 10 || document.body)
8039          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8040          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8041          */
8042         findParent : function(simpleSelector, maxDepth, returnEl){
8043             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8044             maxDepth = maxDepth || 50;
8045             if(typeof maxDepth != "number"){
8046                 stopEl = Roo.getDom(maxDepth);
8047                 maxDepth = 10;
8048             }
8049             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8050                 if(dq.is(p, simpleSelector)){
8051                     return returnEl ? Roo.get(p) : p;
8052                 }
8053                 depth++;
8054                 p = p.parentNode;
8055             }
8056             return null;
8057         },
8058
8059
8060         /**
8061          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8062          * @param {String} selector The simple selector to test
8063          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8064                 search as a number or element (defaults to 10 || document.body)
8065          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8066          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8067          */
8068         findParentNode : function(simpleSelector, maxDepth, returnEl){
8069             var p = Roo.fly(this.dom.parentNode, '_internal');
8070             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8071         },
8072         
8073         /**
8074          * Looks at  the scrollable parent element
8075          */
8076         findScrollableParent : function()
8077         {
8078             var overflowRegex = /(auto|scroll)/;
8079             
8080             if(this.getStyle('position') === 'fixed'){
8081                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8082             }
8083             
8084             var excludeStaticParent = this.getStyle('position') === "absolute";
8085             
8086             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8087                 
8088                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8089                     continue;
8090                 }
8091                 
8092                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8093                     return parent;
8094                 }
8095                 
8096                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8097                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8098                 }
8099             }
8100             
8101             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8102         },
8103
8104         /**
8105          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8106          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8107          * @param {String} selector The simple selector to test
8108          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8109                 search as a number or element (defaults to 10 || document.body)
8110          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8111          */
8112         up : function(simpleSelector, maxDepth){
8113             return this.findParentNode(simpleSelector, maxDepth, true);
8114         },
8115
8116
8117
8118         /**
8119          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8120          * @param {String} selector The simple selector to test
8121          * @return {Boolean} True if this element matches the selector, else false
8122          */
8123         is : function(simpleSelector){
8124             return Roo.DomQuery.is(this.dom, simpleSelector);
8125         },
8126
8127         /**
8128          * Perform animation on this element.
8129          * @param {Object} args The YUI animation control args
8130          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8131          * @param {Function} onComplete (optional) Function to call when animation completes
8132          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8133          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8134          * @return {Roo.Element} this
8135          */
8136         animate : function(args, duration, onComplete, easing, animType){
8137             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8138             return this;
8139         },
8140
8141         /*
8142          * @private Internal animation call
8143          */
8144         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8145             animType = animType || 'run';
8146             opt = opt || {};
8147             var anim = Roo.lib.Anim[animType](
8148                 this.dom, args,
8149                 (opt.duration || defaultDur) || .35,
8150                 (opt.easing || defaultEase) || 'easeOut',
8151                 function(){
8152                     Roo.callback(cb, this);
8153                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8154                 },
8155                 this
8156             );
8157             opt.anim = anim;
8158             return anim;
8159         },
8160
8161         // private legacy anim prep
8162         preanim : function(a, i){
8163             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8164         },
8165
8166         /**
8167          * Removes worthless text nodes
8168          * @param {Boolean} forceReclean (optional) By default the element
8169          * keeps track if it has been cleaned already so
8170          * you can call this over and over. However, if you update the element and
8171          * need to force a reclean, you can pass true.
8172          */
8173         clean : function(forceReclean){
8174             if(this.isCleaned && forceReclean !== true){
8175                 return this;
8176             }
8177             var ns = /\S/;
8178             var d = this.dom, n = d.firstChild, ni = -1;
8179             while(n){
8180                 var nx = n.nextSibling;
8181                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8182                     d.removeChild(n);
8183                 }else{
8184                     n.nodeIndex = ++ni;
8185                 }
8186                 n = nx;
8187             }
8188             this.isCleaned = true;
8189             return this;
8190         },
8191
8192         // private
8193         calcOffsetsTo : function(el){
8194             el = Roo.get(el);
8195             var d = el.dom;
8196             var restorePos = false;
8197             if(el.getStyle('position') == 'static'){
8198                 el.position('relative');
8199                 restorePos = true;
8200             }
8201             var x = 0, y =0;
8202             var op = this.dom;
8203             while(op && op != d && op.tagName != 'HTML'){
8204                 x+= op.offsetLeft;
8205                 y+= op.offsetTop;
8206                 op = op.offsetParent;
8207             }
8208             if(restorePos){
8209                 el.position('static');
8210             }
8211             return [x, y];
8212         },
8213
8214         /**
8215          * Scrolls this element into view within the passed container.
8216          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8217          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8218          * @return {Roo.Element} this
8219          */
8220         scrollIntoView : function(container, hscroll){
8221             var c = Roo.getDom(container) || document.body;
8222             var el = this.dom;
8223
8224             var o = this.calcOffsetsTo(c),
8225                 l = o[0],
8226                 t = o[1],
8227                 b = t+el.offsetHeight,
8228                 r = l+el.offsetWidth;
8229
8230             var ch = c.clientHeight;
8231             var ct = parseInt(c.scrollTop, 10);
8232             var cl = parseInt(c.scrollLeft, 10);
8233             var cb = ct + ch;
8234             var cr = cl + c.clientWidth;
8235
8236             if(t < ct){
8237                 c.scrollTop = t;
8238             }else if(b > cb){
8239                 c.scrollTop = b-ch;
8240             }
8241
8242             if(hscroll !== false){
8243                 if(l < cl){
8244                     c.scrollLeft = l;
8245                 }else if(r > cr){
8246                     c.scrollLeft = r-c.clientWidth;
8247                 }
8248             }
8249             return this;
8250         },
8251
8252         // private
8253         scrollChildIntoView : function(child, hscroll){
8254             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8255         },
8256
8257         /**
8258          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8259          * the new height may not be available immediately.
8260          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8261          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8262          * @param {Function} onComplete (optional) Function to call when animation completes
8263          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8264          * @return {Roo.Element} this
8265          */
8266         autoHeight : function(animate, duration, onComplete, easing){
8267             var oldHeight = this.getHeight();
8268             this.clip();
8269             this.setHeight(1); // force clipping
8270             setTimeout(function(){
8271                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8272                 if(!animate){
8273                     this.setHeight(height);
8274                     this.unclip();
8275                     if(typeof onComplete == "function"){
8276                         onComplete();
8277                     }
8278                 }else{
8279                     this.setHeight(oldHeight); // restore original height
8280                     this.setHeight(height, animate, duration, function(){
8281                         this.unclip();
8282                         if(typeof onComplete == "function") { onComplete(); }
8283                     }.createDelegate(this), easing);
8284                 }
8285             }.createDelegate(this), 0);
8286             return this;
8287         },
8288
8289         /**
8290          * Returns true if this element is an ancestor of the passed element
8291          * @param {HTMLElement/String} el The element to check
8292          * @return {Boolean} True if this element is an ancestor of el, else false
8293          */
8294         contains : function(el){
8295             if(!el){return false;}
8296             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8297         },
8298
8299         /**
8300          * Checks whether the element is currently visible using both visibility and display properties.
8301          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8302          * @return {Boolean} True if the element is currently visible, else false
8303          */
8304         isVisible : function(deep) {
8305             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8306             if(deep !== true || !vis){
8307                 return vis;
8308             }
8309             var p = this.dom.parentNode;
8310             while(p && p.tagName.toLowerCase() != "body"){
8311                 if(!Roo.fly(p, '_isVisible').isVisible()){
8312                     return false;
8313                 }
8314                 p = p.parentNode;
8315             }
8316             return true;
8317         },
8318
8319         /**
8320          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8321          * @param {String} selector The CSS selector
8322          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8323          * @return {CompositeElement/CompositeElementLite} The composite element
8324          */
8325         select : function(selector, unique){
8326             return El.select(selector, unique, this.dom);
8327         },
8328
8329         /**
8330          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8331          * @param {String} selector The CSS selector
8332          * @return {Array} An array of the matched nodes
8333          */
8334         query : function(selector, unique){
8335             return Roo.DomQuery.select(selector, this.dom);
8336         },
8337
8338         /**
8339          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8342          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8343          */
8344         child : function(selector, returnDom){
8345             var n = Roo.DomQuery.selectNode(selector, this.dom);
8346             return returnDom ? n : Roo.get(n);
8347         },
8348
8349         /**
8350          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8351          * @param {String} selector The CSS selector
8352          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8353          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8354          */
8355         down : function(selector, returnDom){
8356             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8357             return returnDom ? n : Roo.get(n);
8358         },
8359
8360         /**
8361          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8362          * @param {String} group The group the DD object is member of
8363          * @param {Object} config The DD config object
8364          * @param {Object} overrides An object containing methods to override/implement on the DD object
8365          * @return {Roo.dd.DD} The DD object
8366          */
8367         initDD : function(group, config, overrides){
8368             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8369             return Roo.apply(dd, overrides);
8370         },
8371
8372         /**
8373          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8374          * @param {String} group The group the DDProxy object is member of
8375          * @param {Object} config The DDProxy config object
8376          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8377          * @return {Roo.dd.DDProxy} The DDProxy object
8378          */
8379         initDDProxy : function(group, config, overrides){
8380             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8381             return Roo.apply(dd, overrides);
8382         },
8383
8384         /**
8385          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8386          * @param {String} group The group the DDTarget object is member of
8387          * @param {Object} config The DDTarget config object
8388          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8389          * @return {Roo.dd.DDTarget} The DDTarget object
8390          */
8391         initDDTarget : function(group, config, overrides){
8392             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8393             return Roo.apply(dd, overrides);
8394         },
8395
8396         /**
8397          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8398          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8399          * @param {Boolean} visible Whether the element is visible
8400          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8401          * @return {Roo.Element} this
8402          */
8403          setVisible : function(visible, animate){
8404             if(!animate || !A){
8405                 if(this.visibilityMode == El.DISPLAY){
8406                     this.setDisplayed(visible);
8407                 }else{
8408                     this.fixDisplay();
8409                     this.dom.style.visibility = visible ? "visible" : "hidden";
8410                 }
8411             }else{
8412                 // closure for composites
8413                 var dom = this.dom;
8414                 var visMode = this.visibilityMode;
8415                 if(visible){
8416                     this.setOpacity(.01);
8417                     this.setVisible(true);
8418                 }
8419                 this.anim({opacity: { to: (visible?1:0) }},
8420                       this.preanim(arguments, 1),
8421                       null, .35, 'easeIn', function(){
8422                          if(!visible){
8423                              if(visMode == El.DISPLAY){
8424                                  dom.style.display = "none";
8425                              }else{
8426                                  dom.style.visibility = "hidden";
8427                              }
8428                              Roo.get(dom).setOpacity(1);
8429                          }
8430                      });
8431             }
8432             return this;
8433         },
8434
8435         /**
8436          * Returns true if display is not "none"
8437          * @return {Boolean}
8438          */
8439         isDisplayed : function() {
8440             return this.getStyle("display") != "none";
8441         },
8442
8443         /**
8444          * Toggles the element's visibility or display, depending on visibility mode.
8445          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8446          * @return {Roo.Element} this
8447          */
8448         toggle : function(animate){
8449             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8450             return this;
8451         },
8452
8453         /**
8454          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8455          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8456          * @return {Roo.Element} this
8457          */
8458         setDisplayed : function(value) {
8459             if(typeof value == "boolean"){
8460                value = value ? this.originalDisplay : "none";
8461             }
8462             this.setStyle("display", value);
8463             return this;
8464         },
8465
8466         /**
8467          * Tries to focus the element. Any exceptions are caught and ignored.
8468          * @return {Roo.Element} this
8469          */
8470         focus : function() {
8471             try{
8472                 this.dom.focus();
8473             }catch(e){}
8474             return this;
8475         },
8476
8477         /**
8478          * Tries to blur the element. Any exceptions are caught and ignored.
8479          * @return {Roo.Element} this
8480          */
8481         blur : function() {
8482             try{
8483                 this.dom.blur();
8484             }catch(e){}
8485             return this;
8486         },
8487
8488         /**
8489          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8490          * @param {String/Array} className The CSS class to add, or an array of classes
8491          * @return {Roo.Element} this
8492          */
8493         addClass : function(className){
8494             if(className instanceof Array){
8495                 for(var i = 0, len = className.length; i < len; i++) {
8496                     this.addClass(className[i]);
8497                 }
8498             }else{
8499                 if(className && !this.hasClass(className)){
8500                     if (this.dom instanceof SVGElement) {
8501                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8502                     } else {
8503                         this.dom.className = this.dom.className + " " + className;
8504                     }
8505                 }
8506             }
8507             return this;
8508         },
8509
8510         /**
8511          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8512          * @param {String/Array} className The CSS class to add, or an array of classes
8513          * @return {Roo.Element} this
8514          */
8515         radioClass : function(className){
8516             var siblings = this.dom.parentNode.childNodes;
8517             for(var i = 0; i < siblings.length; i++) {
8518                 var s = siblings[i];
8519                 if(s.nodeType == 1){
8520                     Roo.get(s).removeClass(className);
8521                 }
8522             }
8523             this.addClass(className);
8524             return this;
8525         },
8526
8527         /**
8528          * Removes one or more CSS classes from the element.
8529          * @param {String/Array} className The CSS class to remove, or an array of classes
8530          * @return {Roo.Element} this
8531          */
8532         removeClass : function(className){
8533             
8534             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8535             if(!className || !cn){
8536                 return this;
8537             }
8538             if(className instanceof Array){
8539                 for(var i = 0, len = className.length; i < len; i++) {
8540                     this.removeClass(className[i]);
8541                 }
8542             }else{
8543                 if(this.hasClass(className)){
8544                     var re = this.classReCache[className];
8545                     if (!re) {
8546                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8547                        this.classReCache[className] = re;
8548                     }
8549                     if (this.dom instanceof SVGElement) {
8550                         this.dom.className.baseVal = cn.replace(re, " ");
8551                     } else {
8552                         this.dom.className = cn.replace(re, " ");
8553                     }
8554                 }
8555             }
8556             return this;
8557         },
8558
8559         // private
8560         classReCache: {},
8561
8562         /**
8563          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8564          * @param {String} className The CSS class to toggle
8565          * @return {Roo.Element} this
8566          */
8567         toggleClass : function(className){
8568             if(this.hasClass(className)){
8569                 this.removeClass(className);
8570             }else{
8571                 this.addClass(className);
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Checks if the specified CSS class exists on this element's DOM node.
8578          * @param {String} className The CSS class to check for
8579          * @return {Boolean} True if the class exists, else false
8580          */
8581         hasClass : function(className){
8582             if (this.dom instanceof SVGElement) {
8583                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8584             } 
8585             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8586         },
8587
8588         /**
8589          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8590          * @param {String} oldClassName The CSS class to replace
8591          * @param {String} newClassName The replacement CSS class
8592          * @return {Roo.Element} this
8593          */
8594         replaceClass : function(oldClassName, newClassName){
8595             this.removeClass(oldClassName);
8596             this.addClass(newClassName);
8597             return this;
8598         },
8599
8600         /**
8601          * Returns an object with properties matching the styles requested.
8602          * For example, el.getStyles('color', 'font-size', 'width') might return
8603          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8604          * @param {String} style1 A style name
8605          * @param {String} style2 A style name
8606          * @param {String} etc.
8607          * @return {Object} The style object
8608          */
8609         getStyles : function(){
8610             var a = arguments, len = a.length, r = {};
8611             for(var i = 0; i < len; i++){
8612                 r[a[i]] = this.getStyle(a[i]);
8613             }
8614             return r;
8615         },
8616
8617         /**
8618          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8619          * @param {String} property The style property whose value is returned.
8620          * @return {String} The current value of the style property for this element.
8621          */
8622         getStyle : function(){
8623             return view && view.getComputedStyle ?
8624                 function(prop){
8625                     var el = this.dom, v, cs, camel;
8626                     if(prop == 'float'){
8627                         prop = "cssFloat";
8628                     }
8629                     if(el.style && (v = el.style[prop])){
8630                         return v;
8631                     }
8632                     if(cs = view.getComputedStyle(el, "")){
8633                         if(!(camel = propCache[prop])){
8634                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8635                         }
8636                         return cs[camel];
8637                     }
8638                     return null;
8639                 } :
8640                 function(prop){
8641                     var el = this.dom, v, cs, camel;
8642                     if(prop == 'opacity'){
8643                         if(typeof el.style.filter == 'string'){
8644                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8645                             if(m){
8646                                 var fv = parseFloat(m[1]);
8647                                 if(!isNaN(fv)){
8648                                     return fv ? fv / 100 : 0;
8649                                 }
8650                             }
8651                         }
8652                         return 1;
8653                     }else if(prop == 'float'){
8654                         prop = "styleFloat";
8655                     }
8656                     if(!(camel = propCache[prop])){
8657                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8658                     }
8659                     if(v = el.style[camel]){
8660                         return v;
8661                     }
8662                     if(cs = el.currentStyle){
8663                         return cs[camel];
8664                     }
8665                     return null;
8666                 };
8667         }(),
8668
8669         /**
8670          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8671          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8672          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8673          * @return {Roo.Element} this
8674          */
8675         setStyle : function(prop, value){
8676             if(typeof prop == "string"){
8677                 
8678                 if (prop == 'float') {
8679                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8680                     return this;
8681                 }
8682                 
8683                 var camel;
8684                 if(!(camel = propCache[prop])){
8685                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8686                 }
8687                 
8688                 if(camel == 'opacity') {
8689                     this.setOpacity(value);
8690                 }else{
8691                     this.dom.style[camel] = value;
8692                 }
8693             }else{
8694                 for(var style in prop){
8695                     if(typeof prop[style] != "function"){
8696                        this.setStyle(style, prop[style]);
8697                     }
8698                 }
8699             }
8700             return this;
8701         },
8702
8703         /**
8704          * More flexible version of {@link #setStyle} for setting style properties.
8705          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8706          * a function which returns such a specification.
8707          * @return {Roo.Element} this
8708          */
8709         applyStyles : function(style){
8710             Roo.DomHelper.applyStyles(this.dom, style);
8711             return this;
8712         },
8713
8714         /**
8715           * 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).
8716           * @return {Number} The X position of the element
8717           */
8718         getX : function(){
8719             return D.getX(this.dom);
8720         },
8721
8722         /**
8723           * 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).
8724           * @return {Number} The Y position of the element
8725           */
8726         getY : function(){
8727             return D.getY(this.dom);
8728         },
8729
8730         /**
8731           * 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).
8732           * @return {Array} The XY position of the element
8733           */
8734         getXY : function(){
8735             return D.getXY(this.dom);
8736         },
8737
8738         /**
8739          * 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).
8740          * @param {Number} The X position of the element
8741          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         setX : function(x, animate){
8745             if(!animate || !A){
8746                 D.setX(this.dom, x);
8747             }else{
8748                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8749             }
8750             return this;
8751         },
8752
8753         /**
8754          * 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).
8755          * @param {Number} The Y position of the element
8756          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8757          * @return {Roo.Element} this
8758          */
8759         setY : function(y, animate){
8760             if(!animate || !A){
8761                 D.setY(this.dom, y);
8762             }else{
8763                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8764             }
8765             return this;
8766         },
8767
8768         /**
8769          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8770          * @param {String} left The left CSS property value
8771          * @return {Roo.Element} this
8772          */
8773         setLeft : function(left){
8774             this.setStyle("left", this.addUnits(left));
8775             return this;
8776         },
8777
8778         /**
8779          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8780          * @param {String} top The top CSS property value
8781          * @return {Roo.Element} this
8782          */
8783         setTop : function(top){
8784             this.setStyle("top", this.addUnits(top));
8785             return this;
8786         },
8787
8788         /**
8789          * Sets the element's CSS right style.
8790          * @param {String} right The right CSS property value
8791          * @return {Roo.Element} this
8792          */
8793         setRight : function(right){
8794             this.setStyle("right", this.addUnits(right));
8795             return this;
8796         },
8797
8798         /**
8799          * Sets the element's CSS bottom style.
8800          * @param {String} bottom The bottom CSS property value
8801          * @return {Roo.Element} this
8802          */
8803         setBottom : function(bottom){
8804             this.setStyle("bottom", this.addUnits(bottom));
8805             return this;
8806         },
8807
8808         /**
8809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8811          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8812          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8813          * @return {Roo.Element} this
8814          */
8815         setXY : function(pos, animate){
8816             if(!animate || !A){
8817                 D.setXY(this.dom, pos);
8818             }else{
8819                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8820             }
8821             return this;
8822         },
8823
8824         /**
8825          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8826          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8827          * @param {Number} x X value for new position (coordinates are page-based)
8828          * @param {Number} y Y value for new position (coordinates are page-based)
8829          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8830          * @return {Roo.Element} this
8831          */
8832         setLocation : function(x, y, animate){
8833             this.setXY([x, y], this.preanim(arguments, 2));
8834             return this;
8835         },
8836
8837         /**
8838          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8839          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8840          * @param {Number} x X value for new position (coordinates are page-based)
8841          * @param {Number} y Y value for new position (coordinates are page-based)
8842          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8843          * @return {Roo.Element} this
8844          */
8845         moveTo : function(x, y, animate){
8846             this.setXY([x, y], this.preanim(arguments, 2));
8847             return this;
8848         },
8849
8850         /**
8851          * Returns the region of the given element.
8852          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8853          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8854          */
8855         getRegion : function(){
8856             return D.getRegion(this.dom);
8857         },
8858
8859         /**
8860          * Returns the offset height of the element
8861          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8862          * @return {Number} The element's height
8863          */
8864         getHeight : function(contentHeight){
8865             var h = this.dom.offsetHeight || 0;
8866             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8867         },
8868
8869         /**
8870          * Returns the offset width of the element
8871          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8872          * @return {Number} The element's width
8873          */
8874         getWidth : function(contentWidth){
8875             var w = this.dom.offsetWidth || 0;
8876             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8877         },
8878
8879         /**
8880          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8881          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8882          * if a height has not been set using CSS.
8883          * @return {Number}
8884          */
8885         getComputedHeight : function(){
8886             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8887             if(!h){
8888                 h = parseInt(this.getStyle('height'), 10) || 0;
8889                 if(!this.isBorderBox()){
8890                     h += this.getFrameWidth('tb');
8891                 }
8892             }
8893             return h;
8894         },
8895
8896         /**
8897          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8898          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8899          * if a width has not been set using CSS.
8900          * @return {Number}
8901          */
8902         getComputedWidth : function(){
8903             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8904             if(!w){
8905                 w = parseInt(this.getStyle('width'), 10) || 0;
8906                 if(!this.isBorderBox()){
8907                     w += this.getFrameWidth('lr');
8908                 }
8909             }
8910             return w;
8911         },
8912
8913         /**
8914          * Returns the size of the element.
8915          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8916          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8917          */
8918         getSize : function(contentSize){
8919             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8920         },
8921
8922         /**
8923          * Returns the width and height of the viewport.
8924          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8925          */
8926         getViewSize : function(){
8927             var d = this.dom, doc = document, aw = 0, ah = 0;
8928             if(d == doc || d == doc.body){
8929                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8930             }else{
8931                 return {
8932                     width : d.clientWidth,
8933                     height: d.clientHeight
8934                 };
8935             }
8936         },
8937
8938         /**
8939          * Returns the value of the "value" attribute
8940          * @param {Boolean} asNumber true to parse the value as a number
8941          * @return {String/Number}
8942          */
8943         getValue : function(asNumber){
8944             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8945         },
8946
8947         // private
8948         adjustWidth : function(width){
8949             if(typeof width == "number"){
8950                 if(this.autoBoxAdjust && !this.isBorderBox()){
8951                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8952                 }
8953                 if(width < 0){
8954                     width = 0;
8955                 }
8956             }
8957             return width;
8958         },
8959
8960         // private
8961         adjustHeight : function(height){
8962             if(typeof height == "number"){
8963                if(this.autoBoxAdjust && !this.isBorderBox()){
8964                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8965                }
8966                if(height < 0){
8967                    height = 0;
8968                }
8969             }
8970             return height;
8971         },
8972
8973         /**
8974          * Set the width of the element
8975          * @param {Number} width The new width
8976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8977          * @return {Roo.Element} this
8978          */
8979         setWidth : function(width, animate){
8980             width = this.adjustWidth(width);
8981             if(!animate || !A){
8982                 this.dom.style.width = this.addUnits(width);
8983             }else{
8984                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8985             }
8986             return this;
8987         },
8988
8989         /**
8990          * Set the height of the element
8991          * @param {Number} height The new height
8992          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8993          * @return {Roo.Element} this
8994          */
8995          setHeight : function(height, animate){
8996             height = this.adjustHeight(height);
8997             if(!animate || !A){
8998                 this.dom.style.height = this.addUnits(height);
8999             }else{
9000                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9001             }
9002             return this;
9003         },
9004
9005         /**
9006          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9007          * @param {Number} width The new width
9008          * @param {Number} height The new height
9009          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9010          * @return {Roo.Element} this
9011          */
9012          setSize : function(width, height, animate){
9013             if(typeof width == "object"){ // in case of object from getSize()
9014                 height = width.height; width = width.width;
9015             }
9016             width = this.adjustWidth(width); height = this.adjustHeight(height);
9017             if(!animate || !A){
9018                 this.dom.style.width = this.addUnits(width);
9019                 this.dom.style.height = this.addUnits(height);
9020             }else{
9021                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9022             }
9023             return this;
9024         },
9025
9026         /**
9027          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9028          * @param {Number} x X value for new position (coordinates are page-based)
9029          * @param {Number} y Y value for new position (coordinates are page-based)
9030          * @param {Number} width The new width
9031          * @param {Number} height The new height
9032          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9033          * @return {Roo.Element} this
9034          */
9035         setBounds : function(x, y, width, height, animate){
9036             if(!animate || !A){
9037                 this.setSize(width, height);
9038                 this.setLocation(x, y);
9039             }else{
9040                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9041                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9042                               this.preanim(arguments, 4), 'motion');
9043             }
9044             return this;
9045         },
9046
9047         /**
9048          * 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.
9049          * @param {Roo.lib.Region} region The region to fill
9050          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9051          * @return {Roo.Element} this
9052          */
9053         setRegion : function(region, animate){
9054             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9055             return this;
9056         },
9057
9058         /**
9059          * Appends an event handler
9060          *
9061          * @param {String}   eventName     The type of event to append
9062          * @param {Function} fn        The method the event invokes
9063          * @param {Object} scope       (optional) The scope (this object) of the fn
9064          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9065          */
9066         addListener : function(eventName, fn, scope, options)
9067         {
9068             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9069                 this.addListener('touchstart', this.onTapHandler, this);
9070             }
9071             
9072             // we need to handle a special case where dom element is a svg element.
9073             // in this case we do not actua
9074             if (!this.dom) {
9075                 return;
9076             }
9077             
9078             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9079                 if (typeof(this.listeners[eventName]) == 'undefined') {
9080                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9081                 }
9082                 this.listeners[eventName].addListener(fn, scope, options);
9083                 return;
9084             }
9085             
9086                 
9087             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9088             
9089             
9090         },
9091         tapedTwice : false,
9092         onTapHandler : function(event)
9093         {
9094             if(!this.tapedTwice) {
9095                 this.tapedTwice = true;
9096                 var s = this;
9097                 setTimeout( function() {
9098                     s.tapedTwice = false;
9099                 }, 300 );
9100                 return;
9101             }
9102             event.preventDefault();
9103             var revent = new MouseEvent('dblclick',  {
9104                 view: window,
9105                 bubbles: true,
9106                 cancelable: true
9107             });
9108              
9109             this.dom.dispatchEvent(revent);
9110             //action on double tap goes below
9111              
9112         }, 
9113  
9114         /**
9115          * Removes an event handler from this element
9116          * @param {String} eventName the type of event to remove
9117          * @param {Function} fn the method the event invokes
9118          * @param {Function} scope (needed for svg fake listeners)
9119          * @return {Roo.Element} this
9120          */
9121         removeListener : function(eventName, fn, scope){
9122             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9123             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9124                 return this;
9125             }
9126             this.listeners[eventName].removeListener(fn, scope);
9127             return this;
9128         },
9129
9130         /**
9131          * Removes all previous added listeners from this element
9132          * @return {Roo.Element} this
9133          */
9134         removeAllListeners : function(){
9135             E.purgeElement(this.dom);
9136             this.listeners = {};
9137             return this;
9138         },
9139
9140         relayEvent : function(eventName, observable){
9141             this.on(eventName, function(e){
9142                 observable.fireEvent(eventName, e);
9143             });
9144         },
9145
9146         
9147         /**
9148          * Set the opacity of the element
9149          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9150          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9151          * @return {Roo.Element} this
9152          */
9153          setOpacity : function(opacity, animate){
9154             if(!animate || !A){
9155                 var s = this.dom.style;
9156                 if(Roo.isIE){
9157                     s.zoom = 1;
9158                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9159                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9160                 }else{
9161                     s.opacity = opacity;
9162                 }
9163             }else{
9164                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9165             }
9166             return this;
9167         },
9168
9169         /**
9170          * Gets the left X coordinate
9171          * @param {Boolean} local True to get the local css position instead of page coordinate
9172          * @return {Number}
9173          */
9174         getLeft : function(local){
9175             if(!local){
9176                 return this.getX();
9177             }else{
9178                 return parseInt(this.getStyle("left"), 10) || 0;
9179             }
9180         },
9181
9182         /**
9183          * Gets the right X coordinate of the element (element X position + element width)
9184          * @param {Boolean} local True to get the local css position instead of page coordinate
9185          * @return {Number}
9186          */
9187         getRight : function(local){
9188             if(!local){
9189                 return this.getX() + this.getWidth();
9190             }else{
9191                 return (this.getLeft(true) + this.getWidth()) || 0;
9192             }
9193         },
9194
9195         /**
9196          * Gets the top Y coordinate
9197          * @param {Boolean} local True to get the local css position instead of page coordinate
9198          * @return {Number}
9199          */
9200         getTop : function(local) {
9201             if(!local){
9202                 return this.getY();
9203             }else{
9204                 return parseInt(this.getStyle("top"), 10) || 0;
9205             }
9206         },
9207
9208         /**
9209          * Gets the bottom Y coordinate of the element (element Y position + element height)
9210          * @param {Boolean} local True to get the local css position instead of page coordinate
9211          * @return {Number}
9212          */
9213         getBottom : function(local){
9214             if(!local){
9215                 return this.getY() + this.getHeight();
9216             }else{
9217                 return (this.getTop(true) + this.getHeight()) || 0;
9218             }
9219         },
9220
9221         /**
9222         * Initializes positioning on this element. If a desired position is not passed, it will make the
9223         * the element positioned relative IF it is not already positioned.
9224         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9225         * @param {Number} zIndex (optional) The zIndex to apply
9226         * @param {Number} x (optional) Set the page X position
9227         * @param {Number} y (optional) Set the page Y position
9228         */
9229         position : function(pos, zIndex, x, y){
9230             if(!pos){
9231                if(this.getStyle('position') == 'static'){
9232                    this.setStyle('position', 'relative');
9233                }
9234             }else{
9235                 this.setStyle("position", pos);
9236             }
9237             if(zIndex){
9238                 this.setStyle("z-index", zIndex);
9239             }
9240             if(x !== undefined && y !== undefined){
9241                 this.setXY([x, y]);
9242             }else if(x !== undefined){
9243                 this.setX(x);
9244             }else if(y !== undefined){
9245                 this.setY(y);
9246             }
9247         },
9248
9249         /**
9250         * Clear positioning back to the default when the document was loaded
9251         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9252         * @return {Roo.Element} this
9253          */
9254         clearPositioning : function(value){
9255             value = value ||'';
9256             this.setStyle({
9257                 "left": value,
9258                 "right": value,
9259                 "top": value,
9260                 "bottom": value,
9261                 "z-index": "",
9262                 "position" : "static"
9263             });
9264             return this;
9265         },
9266
9267         /**
9268         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9269         * snapshot before performing an update and then restoring the element.
9270         * @return {Object}
9271         */
9272         getPositioning : function(){
9273             var l = this.getStyle("left");
9274             var t = this.getStyle("top");
9275             return {
9276                 "position" : this.getStyle("position"),
9277                 "left" : l,
9278                 "right" : l ? "" : this.getStyle("right"),
9279                 "top" : t,
9280                 "bottom" : t ? "" : this.getStyle("bottom"),
9281                 "z-index" : this.getStyle("z-index")
9282             };
9283         },
9284
9285         /**
9286          * Gets the width of the border(s) for the specified side(s)
9287          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9288          * passing lr would get the border (l)eft width + the border (r)ight width.
9289          * @return {Number} The width of the sides passed added together
9290          */
9291         getBorderWidth : function(side){
9292             return this.addStyles(side, El.borders);
9293         },
9294
9295         /**
9296          * Gets the width of the padding(s) for the specified side(s)
9297          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9298          * passing lr would get the padding (l)eft + the padding (r)ight.
9299          * @return {Number} The padding of the sides passed added together
9300          */
9301         getPadding : function(side){
9302             return this.addStyles(side, El.paddings);
9303         },
9304
9305         /**
9306         * Set positioning with an object returned by getPositioning().
9307         * @param {Object} posCfg
9308         * @return {Roo.Element} this
9309          */
9310         setPositioning : function(pc){
9311             this.applyStyles(pc);
9312             if(pc.right == "auto"){
9313                 this.dom.style.right = "";
9314             }
9315             if(pc.bottom == "auto"){
9316                 this.dom.style.bottom = "";
9317             }
9318             return this;
9319         },
9320
9321         // private
9322         fixDisplay : function(){
9323             if(this.getStyle("display") == "none"){
9324                 this.setStyle("visibility", "hidden");
9325                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9326                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9327                     this.setStyle("display", "block");
9328                 }
9329             }
9330         },
9331
9332         /**
9333          * Quick set left and top adding default units
9334          * @param {String} left The left CSS property value
9335          * @param {String} top The top CSS property value
9336          * @return {Roo.Element} this
9337          */
9338          setLeftTop : function(left, top){
9339             this.dom.style.left = this.addUnits(left);
9340             this.dom.style.top = this.addUnits(top);
9341             return this;
9342         },
9343
9344         /**
9345          * Move this element relative to its current position.
9346          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9347          * @param {Number} distance How far to move the element in pixels
9348          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9349          * @return {Roo.Element} this
9350          */
9351          move : function(direction, distance, animate){
9352             var xy = this.getXY();
9353             direction = direction.toLowerCase();
9354             switch(direction){
9355                 case "l":
9356                 case "left":
9357                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9358                     break;
9359                case "r":
9360                case "right":
9361                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9362                     break;
9363                case "t":
9364                case "top":
9365                case "up":
9366                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9367                     break;
9368                case "b":
9369                case "bottom":
9370                case "down":
9371                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9372                     break;
9373             }
9374             return this;
9375         },
9376
9377         /**
9378          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9379          * @return {Roo.Element} this
9380          */
9381         clip : function(){
9382             if(!this.isClipped){
9383                this.isClipped = true;
9384                this.originalClip = {
9385                    "o": this.getStyle("overflow"),
9386                    "x": this.getStyle("overflow-x"),
9387                    "y": this.getStyle("overflow-y")
9388                };
9389                this.setStyle("overflow", "hidden");
9390                this.setStyle("overflow-x", "hidden");
9391                this.setStyle("overflow-y", "hidden");
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Return clipping (overflow) to original clipping before clip() was called
9398          * @return {Roo.Element} this
9399          */
9400         unclip : function(){
9401             if(this.isClipped){
9402                 this.isClipped = false;
9403                 var o = this.originalClip;
9404                 if(o.o){this.setStyle("overflow", o.o);}
9405                 if(o.x){this.setStyle("overflow-x", o.x);}
9406                 if(o.y){this.setStyle("overflow-y", o.y);}
9407             }
9408             return this;
9409         },
9410
9411
9412         /**
9413          * Gets the x,y coordinates specified by the anchor position on the element.
9414          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9415          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9416          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9417          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9418          * @return {Array} [x, y] An array containing the element's x and y coordinates
9419          */
9420         getAnchorXY : function(anchor, local, s){
9421             //Passing a different size is useful for pre-calculating anchors,
9422             //especially for anchored animations that change the el size.
9423
9424             var w, h, vp = false;
9425             if(!s){
9426                 var d = this.dom;
9427                 if(d == document.body || d == document){
9428                     vp = true;
9429                     w = D.getViewWidth(); h = D.getViewHeight();
9430                 }else{
9431                     w = this.getWidth(); h = this.getHeight();
9432                 }
9433             }else{
9434                 w = s.width;  h = s.height;
9435             }
9436             var x = 0, y = 0, r = Math.round;
9437             switch((anchor || "tl").toLowerCase()){
9438                 case "c":
9439                     x = r(w*.5);
9440                     y = r(h*.5);
9441                 break;
9442                 case "t":
9443                     x = r(w*.5);
9444                     y = 0;
9445                 break;
9446                 case "l":
9447                     x = 0;
9448                     y = r(h*.5);
9449                 break;
9450                 case "r":
9451                     x = w;
9452                     y = r(h*.5);
9453                 break;
9454                 case "b":
9455                     x = r(w*.5);
9456                     y = h;
9457                 break;
9458                 case "tl":
9459                     x = 0;
9460                     y = 0;
9461                 break;
9462                 case "bl":
9463                     x = 0;
9464                     y = h;
9465                 break;
9466                 case "br":
9467                     x = w;
9468                     y = h;
9469                 break;
9470                 case "tr":
9471                     x = w;
9472                     y = 0;
9473                 break;
9474             }
9475             if(local === true){
9476                 return [x, y];
9477             }
9478             if(vp){
9479                 var sc = this.getScroll();
9480                 return [x + sc.left, y + sc.top];
9481             }
9482             //Add the element's offset xy
9483             var o = this.getXY();
9484             return [x+o[0], y+o[1]];
9485         },
9486
9487         /**
9488          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9489          * supported position values.
9490          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9491          * @param {String} position The position to align to.
9492          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9493          * @return {Array} [x, y]
9494          */
9495         getAlignToXY : function(el, p, o)
9496         {
9497             el = Roo.get(el);
9498             var d = this.dom;
9499             if(!el.dom){
9500                 throw "Element.alignTo with an element that doesn't exist";
9501             }
9502             var c = false; //constrain to viewport
9503             var p1 = "", p2 = "";
9504             o = o || [0,0];
9505
9506             if(!p){
9507                 p = "tl-bl";
9508             }else if(p == "?"){
9509                 p = "tl-bl?";
9510             }else if(p.indexOf("-") == -1){
9511                 p = "tl-" + p;
9512             }
9513             p = p.toLowerCase();
9514             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9515             if(!m){
9516                throw "Element.alignTo with an invalid alignment " + p;
9517             }
9518             p1 = m[1]; p2 = m[2]; c = !!m[3];
9519
9520             //Subtract the aligned el's internal xy from the target's offset xy
9521             //plus custom offset to get the aligned el's new offset xy
9522             var a1 = this.getAnchorXY(p1, true);
9523             var a2 = el.getAnchorXY(p2, false);
9524             var x = a2[0] - a1[0] + o[0];
9525             var y = a2[1] - a1[1] + o[1];
9526             if(c){
9527                 //constrain the aligned el to viewport if necessary
9528                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9529                 // 5px of margin for ie
9530                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9531
9532                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9533                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9534                 //otherwise swap the aligned el to the opposite border of the target.
9535                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9536                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9537                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9538                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9539
9540                var doc = document;
9541                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9542                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9543
9544                if((x+w) > dw + scrollX){
9545                     x = swapX ? r.left-w : dw+scrollX-w;
9546                 }
9547                if(x < scrollX){
9548                    x = swapX ? r.right : scrollX;
9549                }
9550                if((y+h) > dh + scrollY){
9551                     y = swapY ? r.top-h : dh+scrollY-h;
9552                 }
9553                if (y < scrollY){
9554                    y = swapY ? r.bottom : scrollY;
9555                }
9556             }
9557             return [x,y];
9558         },
9559
9560         // private
9561         getConstrainToXY : function(){
9562             var os = {top:0, left:0, bottom:0, right: 0};
9563
9564             return function(el, local, offsets, proposedXY){
9565                 el = Roo.get(el);
9566                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9567
9568                 var vw, vh, vx = 0, vy = 0;
9569                 if(el.dom == document.body || el.dom == document){
9570                     vw = Roo.lib.Dom.getViewWidth();
9571                     vh = Roo.lib.Dom.getViewHeight();
9572                 }else{
9573                     vw = el.dom.clientWidth;
9574                     vh = el.dom.clientHeight;
9575                     if(!local){
9576                         var vxy = el.getXY();
9577                         vx = vxy[0];
9578                         vy = vxy[1];
9579                     }
9580                 }
9581
9582                 var s = el.getScroll();
9583
9584                 vx += offsets.left + s.left;
9585                 vy += offsets.top + s.top;
9586
9587                 vw -= offsets.right;
9588                 vh -= offsets.bottom;
9589
9590                 var vr = vx+vw;
9591                 var vb = vy+vh;
9592
9593                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9594                 var x = xy[0], y = xy[1];
9595                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9596
9597                 // only move it if it needs it
9598                 var moved = false;
9599
9600                 // first validate right/bottom
9601                 if((x + w) > vr){
9602                     x = vr - w;
9603                     moved = true;
9604                 }
9605                 if((y + h) > vb){
9606                     y = vb - h;
9607                     moved = true;
9608                 }
9609                 // then make sure top/left isn't negative
9610                 if(x < vx){
9611                     x = vx;
9612                     moved = true;
9613                 }
9614                 if(y < vy){
9615                     y = vy;
9616                     moved = true;
9617                 }
9618                 return moved ? [x, y] : false;
9619             };
9620         }(),
9621
9622         // private
9623         adjustForConstraints : function(xy, parent, offsets){
9624             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9625         },
9626
9627         /**
9628          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9629          * document it aligns it to the viewport.
9630          * The position parameter is optional, and can be specified in any one of the following formats:
9631          * <ul>
9632          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9633          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9634          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9635          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9636          *   <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
9637          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9638          * </ul>
9639          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9640          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9641          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9642          * that specified in order to enforce the viewport constraints.
9643          * Following are all of the supported anchor positions:
9644     <pre>
9645     Value  Description
9646     -----  -----------------------------
9647     tl     The top left corner (default)
9648     t      The center of the top edge
9649     tr     The top right corner
9650     l      The center of the left edge
9651     c      In the center of the element
9652     r      The center of the right edge
9653     bl     The bottom left corner
9654     b      The center of the bottom edge
9655     br     The bottom right corner
9656     </pre>
9657     Example Usage:
9658     <pre><code>
9659     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9660     el.alignTo("other-el");
9661
9662     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9663     el.alignTo("other-el", "tr?");
9664
9665     // align the bottom right corner of el with the center left edge of other-el
9666     el.alignTo("other-el", "br-l?");
9667
9668     // align the center of el with the bottom left corner of other-el and
9669     // adjust the x position by -6 pixels (and the y position by 0)
9670     el.alignTo("other-el", "c-bl", [-6, 0]);
9671     </code></pre>
9672          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9673          * @param {String} position The position to align to.
9674          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676          * @return {Roo.Element} this
9677          */
9678         alignTo : function(element, position, offsets, animate){
9679             var xy = this.getAlignToXY(element, position, offsets);
9680             this.setXY(xy, this.preanim(arguments, 3));
9681             return this;
9682         },
9683
9684         /**
9685          * Anchors an element to another element and realigns it when the window is resized.
9686          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9687          * @param {String} position The position to align to.
9688          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9689          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9690          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9691          * is a number, it is used as the buffer delay (defaults to 50ms).
9692          * @param {Function} callback The function to call after the animation finishes
9693          * @return {Roo.Element} this
9694          */
9695         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9696             var action = function(){
9697                 this.alignTo(el, alignment, offsets, animate);
9698                 Roo.callback(callback, this);
9699             };
9700             Roo.EventManager.onWindowResize(action, this);
9701             var tm = typeof monitorScroll;
9702             if(tm != 'undefined'){
9703                 Roo.EventManager.on(window, 'scroll', action, this,
9704                     {buffer: tm == 'number' ? monitorScroll : 50});
9705             }
9706             action.call(this); // align immediately
9707             return this;
9708         },
9709         /**
9710          * Clears any opacity settings from this element. Required in some cases for IE.
9711          * @return {Roo.Element} this
9712          */
9713         clearOpacity : function(){
9714             if (window.ActiveXObject) {
9715                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9716                     this.dom.style.filter = "";
9717                 }
9718             } else {
9719                 this.dom.style.opacity = "";
9720                 this.dom.style["-moz-opacity"] = "";
9721                 this.dom.style["-khtml-opacity"] = "";
9722             }
9723             return this;
9724         },
9725
9726         /**
9727          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9729          * @return {Roo.Element} this
9730          */
9731         hide : function(animate){
9732             this.setVisible(false, this.preanim(arguments, 0));
9733             return this;
9734         },
9735
9736         /**
9737         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9738         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9739          * @return {Roo.Element} this
9740          */
9741         show : function(animate){
9742             this.setVisible(true, this.preanim(arguments, 0));
9743             return this;
9744         },
9745
9746         /**
9747          * @private Test if size has a unit, otherwise appends the default
9748          */
9749         addUnits : function(size){
9750             return Roo.Element.addUnits(size, this.defaultUnit);
9751         },
9752
9753         /**
9754          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9755          * @return {Roo.Element} this
9756          */
9757         beginMeasure : function(){
9758             var el = this.dom;
9759             if(el.offsetWidth || el.offsetHeight){
9760                 return this; // offsets work already
9761             }
9762             var changed = [];
9763             var p = this.dom, b = document.body; // start with this element
9764             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9765                 var pe = Roo.get(p);
9766                 if(pe.getStyle('display') == 'none'){
9767                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9768                     p.style.visibility = "hidden";
9769                     p.style.display = "block";
9770                 }
9771                 p = p.parentNode;
9772             }
9773             this._measureChanged = changed;
9774             return this;
9775
9776         },
9777
9778         /**
9779          * Restores displays to before beginMeasure was called
9780          * @return {Roo.Element} this
9781          */
9782         endMeasure : function(){
9783             var changed = this._measureChanged;
9784             if(changed){
9785                 for(var i = 0, len = changed.length; i < len; i++) {
9786                     var r = changed[i];
9787                     r.el.style.visibility = r.visibility;
9788                     r.el.style.display = "none";
9789                 }
9790                 this._measureChanged = null;
9791             }
9792             return this;
9793         },
9794
9795         /**
9796         * Update the innerHTML of this element, optionally searching for and processing scripts
9797         * @param {String} html The new HTML
9798         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9799         * @param {Function} callback For async script loading you can be noticed when the update completes
9800         * @return {Roo.Element} this
9801          */
9802         update : function(html, loadScripts, callback){
9803             if(typeof html == "undefined"){
9804                 html = "";
9805             }
9806             if(loadScripts !== true){
9807                 this.dom.innerHTML = html;
9808                 if(typeof callback == "function"){
9809                     callback();
9810                 }
9811                 return this;
9812             }
9813             var id = Roo.id();
9814             var dom = this.dom;
9815
9816             html += '<span id="' + id + '"></span>';
9817
9818             E.onAvailable(id, function(){
9819                 var hd = document.getElementsByTagName("head")[0];
9820                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9821                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9822                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9823
9824                 var match;
9825                 while(match = re.exec(html)){
9826                     var attrs = match[1];
9827                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9828                     if(srcMatch && srcMatch[2]){
9829                        var s = document.createElement("script");
9830                        s.src = srcMatch[2];
9831                        var typeMatch = attrs.match(typeRe);
9832                        if(typeMatch && typeMatch[2]){
9833                            s.type = typeMatch[2];
9834                        }
9835                        hd.appendChild(s);
9836                     }else if(match[2] && match[2].length > 0){
9837                         if(window.execScript) {
9838                            window.execScript(match[2]);
9839                         } else {
9840                             /**
9841                              * eval:var:id
9842                              * eval:var:dom
9843                              * eval:var:html
9844                              * 
9845                              */
9846                            window.eval(match[2]);
9847                         }
9848                     }
9849                 }
9850                 var el = document.getElementById(id);
9851                 if(el){el.parentNode.removeChild(el);}
9852                 if(typeof callback == "function"){
9853                     callback();
9854                 }
9855             });
9856             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9857             return this;
9858         },
9859
9860         /**
9861          * Direct access to the UpdateManager update() method (takes the same parameters).
9862          * @param {String/Function} url The url for this request or a function to call to get the url
9863          * @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}
9864          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9865          * @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.
9866          * @return {Roo.Element} this
9867          */
9868         load : function(){
9869             var um = this.getUpdateManager();
9870             um.update.apply(um, arguments);
9871             return this;
9872         },
9873
9874         /**
9875         * Gets this element's UpdateManager
9876         * @return {Roo.UpdateManager} The UpdateManager
9877         */
9878         getUpdateManager : function(){
9879             if(!this.updateManager){
9880                 this.updateManager = new Roo.UpdateManager(this);
9881             }
9882             return this.updateManager;
9883         },
9884
9885         /**
9886          * Disables text selection for this element (normalized across browsers)
9887          * @return {Roo.Element} this
9888          */
9889         unselectable : function(){
9890             this.dom.unselectable = "on";
9891             this.swallowEvent("selectstart", true);
9892             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9893             this.addClass("x-unselectable");
9894             return this;
9895         },
9896
9897         /**
9898         * Calculates the x, y to center this element on the screen
9899         * @return {Array} The x, y values [x, y]
9900         */
9901         getCenterXY : function(){
9902             return this.getAlignToXY(document, 'c-c');
9903         },
9904
9905         /**
9906         * Centers the Element in either the viewport, or another Element.
9907         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9908         */
9909         center : function(centerIn){
9910             this.alignTo(centerIn || document, 'c-c');
9911             return this;
9912         },
9913
9914         /**
9915          * Tests various css rules/browsers to determine if this element uses a border box
9916          * @return {Boolean}
9917          */
9918         isBorderBox : function(){
9919             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9920         },
9921
9922         /**
9923          * Return a box {x, y, width, height} that can be used to set another elements
9924          * size/location to match this element.
9925          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9926          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9927          * @return {Object} box An object in the format {x, y, width, height}
9928          */
9929         getBox : function(contentBox, local){
9930             var xy;
9931             if(!local){
9932                 xy = this.getXY();
9933             }else{
9934                 var left = parseInt(this.getStyle("left"), 10) || 0;
9935                 var top = parseInt(this.getStyle("top"), 10) || 0;
9936                 xy = [left, top];
9937             }
9938             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9939             if(!contentBox){
9940                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9941             }else{
9942                 var l = this.getBorderWidth("l")+this.getPadding("l");
9943                 var r = this.getBorderWidth("r")+this.getPadding("r");
9944                 var t = this.getBorderWidth("t")+this.getPadding("t");
9945                 var b = this.getBorderWidth("b")+this.getPadding("b");
9946                 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)};
9947             }
9948             bx.right = bx.x + bx.width;
9949             bx.bottom = bx.y + bx.height;
9950             return bx;
9951         },
9952
9953         /**
9954          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9955          for more information about the sides.
9956          * @param {String} sides
9957          * @return {Number}
9958          */
9959         getFrameWidth : function(sides, onlyContentBox){
9960             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9961         },
9962
9963         /**
9964          * 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.
9965          * @param {Object} box The box to fill {x, y, width, height}
9966          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9968          * @return {Roo.Element} this
9969          */
9970         setBox : function(box, adjust, animate){
9971             var w = box.width, h = box.height;
9972             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9973                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9974                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9975             }
9976             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9977             return this;
9978         },
9979
9980         /**
9981          * Forces the browser to repaint this element
9982          * @return {Roo.Element} this
9983          */
9984          repaint : function(){
9985             var dom = this.dom;
9986             this.addClass("x-repaint");
9987             setTimeout(function(){
9988                 Roo.get(dom).removeClass("x-repaint");
9989             }, 1);
9990             return this;
9991         },
9992
9993         /**
9994          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9995          * then it returns the calculated width of the sides (see getPadding)
9996          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9997          * @return {Object/Number}
9998          */
9999         getMargins : function(side){
10000             if(!side){
10001                 return {
10002                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10003                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10004                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10005                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10006                 };
10007             }else{
10008                 return this.addStyles(side, El.margins);
10009              }
10010         },
10011
10012         // private
10013         addStyles : function(sides, styles){
10014             var val = 0, v, w;
10015             for(var i = 0, len = sides.length; i < len; i++){
10016                 v = this.getStyle(styles[sides.charAt(i)]);
10017                 if(v){
10018                      w = parseInt(v, 10);
10019                      if(w){ val += w; }
10020                 }
10021             }
10022             return val;
10023         },
10024
10025         /**
10026          * Creates a proxy element of this element
10027          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10028          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10029          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10030          * @return {Roo.Element} The new proxy element
10031          */
10032         createProxy : function(config, renderTo, matchBox){
10033             if(renderTo){
10034                 renderTo = Roo.getDom(renderTo);
10035             }else{
10036                 renderTo = document.body;
10037             }
10038             config = typeof config == "object" ?
10039                 config : {tag : "div", cls: config};
10040             var proxy = Roo.DomHelper.append(renderTo, config, true);
10041             if(matchBox){
10042                proxy.setBox(this.getBox());
10043             }
10044             return proxy;
10045         },
10046
10047         /**
10048          * Puts a mask over this element to disable user interaction. Requires core.css.
10049          * This method can only be applied to elements which accept child nodes.
10050          * @param {String} msg (optional) A message to display in the mask
10051          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10052          * @return {Element} The mask  element
10053          */
10054         mask : function(msg, msgCls)
10055         {
10056             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10057                 this.setStyle("position", "relative");
10058             }
10059             if(!this._mask){
10060                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10061             }
10062             
10063             this.addClass("x-masked");
10064             this._mask.setDisplayed(true);
10065             
10066             // we wander
10067             var z = 0;
10068             var dom = this.dom;
10069             while (dom && dom.style) {
10070                 if (!isNaN(parseInt(dom.style.zIndex))) {
10071                     z = Math.max(z, parseInt(dom.style.zIndex));
10072                 }
10073                 dom = dom.parentNode;
10074             }
10075             // if we are masking the body - then it hides everything..
10076             if (this.dom == document.body) {
10077                 z = 1000000;
10078                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10079                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10080             }
10081            
10082             if(typeof msg == 'string'){
10083                 if(!this._maskMsg){
10084                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10085                         cls: "roo-el-mask-msg", 
10086                         cn: [
10087                             {
10088                                 tag: 'i',
10089                                 cls: 'fa fa-spinner fa-spin'
10090                             },
10091                             {
10092                                 tag: 'div'
10093                             }   
10094                         ]
10095                     }, true);
10096                 }
10097                 var mm = this._maskMsg;
10098                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10099                 if (mm.dom.lastChild) { // weird IE issue?
10100                     mm.dom.lastChild.innerHTML = msg;
10101                 }
10102                 mm.setDisplayed(true);
10103                 mm.center(this);
10104                 mm.setStyle('z-index', z + 102);
10105             }
10106             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10107                 this._mask.setHeight(this.getHeight());
10108             }
10109             this._mask.setStyle('z-index', z + 100);
10110             
10111             return this._mask;
10112         },
10113
10114         /**
10115          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10116          * it is cached for reuse.
10117          */
10118         unmask : function(removeEl){
10119             if(this._mask){
10120                 if(removeEl === true){
10121                     this._mask.remove();
10122                     delete this._mask;
10123                     if(this._maskMsg){
10124                         this._maskMsg.remove();
10125                         delete this._maskMsg;
10126                     }
10127                 }else{
10128                     this._mask.setDisplayed(false);
10129                     if(this._maskMsg){
10130                         this._maskMsg.setDisplayed(false);
10131                     }
10132                 }
10133             }
10134             this.removeClass("x-masked");
10135         },
10136
10137         /**
10138          * Returns true if this element is masked
10139          * @return {Boolean}
10140          */
10141         isMasked : function(){
10142             return this._mask && this._mask.isVisible();
10143         },
10144
10145         /**
10146          * Creates an iframe shim for this element to keep selects and other windowed objects from
10147          * showing through.
10148          * @return {Roo.Element} The new shim element
10149          */
10150         createShim : function(){
10151             var el = document.createElement('iframe');
10152             el.frameBorder = 'no';
10153             el.className = 'roo-shim';
10154             if(Roo.isIE && Roo.isSecure){
10155                 el.src = Roo.SSL_SECURE_URL;
10156             }
10157             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10158             shim.autoBoxAdjust = false;
10159             return shim;
10160         },
10161
10162         /**
10163          * Removes this element from the DOM and deletes it from the cache
10164          */
10165         remove : function(){
10166             if(this.dom.parentNode){
10167                 this.dom.parentNode.removeChild(this.dom);
10168             }
10169             delete El.cache[this.dom.id];
10170         },
10171
10172         /**
10173          * Sets up event handlers to add and remove a css class when the mouse is over this element
10174          * @param {String} className
10175          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10176          * mouseout events for children elements
10177          * @return {Roo.Element} this
10178          */
10179         addClassOnOver : function(className, preventFlicker){
10180             this.on("mouseover", function(){
10181                 Roo.fly(this, '_internal').addClass(className);
10182             }, this.dom);
10183             var removeFn = function(e){
10184                 if(preventFlicker !== true || !e.within(this, true)){
10185                     Roo.fly(this, '_internal').removeClass(className);
10186                 }
10187             };
10188             this.on("mouseout", removeFn, this.dom);
10189             return this;
10190         },
10191
10192         /**
10193          * Sets up event handlers to add and remove a css class when this element has the focus
10194          * @param {String} className
10195          * @return {Roo.Element} this
10196          */
10197         addClassOnFocus : function(className){
10198             this.on("focus", function(){
10199                 Roo.fly(this, '_internal').addClass(className);
10200             }, this.dom);
10201             this.on("blur", function(){
10202                 Roo.fly(this, '_internal').removeClass(className);
10203             }, this.dom);
10204             return this;
10205         },
10206         /**
10207          * 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)
10208          * @param {String} className
10209          * @return {Roo.Element} this
10210          */
10211         addClassOnClick : function(className){
10212             var dom = this.dom;
10213             this.on("mousedown", function(){
10214                 Roo.fly(dom, '_internal').addClass(className);
10215                 var d = Roo.get(document);
10216                 var fn = function(){
10217                     Roo.fly(dom, '_internal').removeClass(className);
10218                     d.removeListener("mouseup", fn);
10219                 };
10220                 d.on("mouseup", fn);
10221             });
10222             return this;
10223         },
10224
10225         /**
10226          * Stops the specified event from bubbling and optionally prevents the default action
10227          * @param {String} eventName
10228          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10229          * @return {Roo.Element} this
10230          */
10231         swallowEvent : function(eventName, preventDefault){
10232             var fn = function(e){
10233                 e.stopPropagation();
10234                 if(preventDefault){
10235                     e.preventDefault();
10236                 }
10237             };
10238             if(eventName instanceof Array){
10239                 for(var i = 0, len = eventName.length; i < len; i++){
10240                      this.on(eventName[i], fn);
10241                 }
10242                 return this;
10243             }
10244             this.on(eventName, fn);
10245             return this;
10246         },
10247
10248         /**
10249          * @private
10250          */
10251         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10252
10253         /**
10254          * Sizes this element to its parent element's dimensions performing
10255          * neccessary box adjustments.
10256          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10257          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10258          * @return {Roo.Element} this
10259          */
10260         fitToParent : function(monitorResize, targetParent) {
10261           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10262           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10263           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10264             return this;
10265           }
10266           var p = Roo.get(targetParent || this.dom.parentNode);
10267           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10268           if (monitorResize === true) {
10269             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10270             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10271           }
10272           return this;
10273         },
10274
10275         /**
10276          * Gets the next sibling, skipping text nodes
10277          * @return {HTMLElement} The next sibling or null
10278          */
10279         getNextSibling : function(){
10280             var n = this.dom.nextSibling;
10281             while(n && n.nodeType != 1){
10282                 n = n.nextSibling;
10283             }
10284             return n;
10285         },
10286
10287         /**
10288          * Gets the previous sibling, skipping text nodes
10289          * @return {HTMLElement} The previous sibling or null
10290          */
10291         getPrevSibling : function(){
10292             var n = this.dom.previousSibling;
10293             while(n && n.nodeType != 1){
10294                 n = n.previousSibling;
10295             }
10296             return n;
10297         },
10298
10299
10300         /**
10301          * Appends the passed element(s) to this element
10302          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10303          * @return {Roo.Element} this
10304          */
10305         appendChild: function(el){
10306             el = Roo.get(el);
10307             el.appendTo(this);
10308             return this;
10309         },
10310
10311         /**
10312          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10313          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10314          * automatically generated with the specified attributes.
10315          * @param {HTMLElement} insertBefore (optional) a child element of this element
10316          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10317          * @return {Roo.Element} The new child element
10318          */
10319         createChild: function(config, insertBefore, returnDom){
10320             config = config || {tag:'div'};
10321             if(insertBefore){
10322                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10323             }
10324             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10325         },
10326
10327         /**
10328          * Appends this element to the passed element
10329          * @param {String/HTMLElement/Element} el The new parent element
10330          * @return {Roo.Element} this
10331          */
10332         appendTo: function(el){
10333             el = Roo.getDom(el);
10334             el.appendChild(this.dom);
10335             return this;
10336         },
10337
10338         /**
10339          * Inserts this element before the passed element in the DOM
10340          * @param {String/HTMLElement/Element} el The element to insert before
10341          * @return {Roo.Element} this
10342          */
10343         insertBefore: function(el){
10344             el = Roo.getDom(el);
10345             el.parentNode.insertBefore(this.dom, el);
10346             return this;
10347         },
10348
10349         /**
10350          * Inserts this element after the passed element in the DOM
10351          * @param {String/HTMLElement/Element} el The element to insert after
10352          * @return {Roo.Element} this
10353          */
10354         insertAfter: function(el){
10355             el = Roo.getDom(el);
10356             el.parentNode.insertBefore(this.dom, el.nextSibling);
10357             return this;
10358         },
10359
10360         /**
10361          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10362          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10363          * @return {Roo.Element} The new child
10364          */
10365         insertFirst: function(el, returnDom){
10366             el = el || {};
10367             if(typeof el == 'object' && !el.nodeType){ // dh config
10368                 return this.createChild(el, this.dom.firstChild, returnDom);
10369             }else{
10370                 el = Roo.getDom(el);
10371                 this.dom.insertBefore(el, this.dom.firstChild);
10372                 return !returnDom ? Roo.get(el) : el;
10373             }
10374         },
10375
10376         /**
10377          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10378          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10379          * @param {String} where (optional) 'before' or 'after' defaults to before
10380          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10381          * @return {Roo.Element} the inserted Element
10382          */
10383         insertSibling: function(el, where, returnDom){
10384             where = where ? where.toLowerCase() : 'before';
10385             el = el || {};
10386             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10387
10388             if(typeof el == 'object' && !el.nodeType){ // dh config
10389                 if(where == 'after' && !this.dom.nextSibling){
10390                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10391                 }else{
10392                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10393                 }
10394
10395             }else{
10396                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10397                             where == 'before' ? this.dom : this.dom.nextSibling);
10398                 if(!returnDom){
10399                     rt = Roo.get(rt);
10400                 }
10401             }
10402             return rt;
10403         },
10404
10405         /**
10406          * Creates and wraps this element with another element
10407          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10408          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10409          * @return {HTMLElement/Element} The newly created wrapper element
10410          */
10411         wrap: function(config, returnDom){
10412             if(!config){
10413                 config = {tag: "div"};
10414             }
10415             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10416             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10417             return newEl;
10418         },
10419
10420         /**
10421          * Replaces the passed element with this element
10422          * @param {String/HTMLElement/Element} el The element to replace
10423          * @return {Roo.Element} this
10424          */
10425         replace: function(el){
10426             el = Roo.get(el);
10427             this.insertBefore(el);
10428             el.remove();
10429             return this;
10430         },
10431
10432         /**
10433          * Inserts an html fragment into this element
10434          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10435          * @param {String} html The HTML fragment
10436          * @param {Boolean} returnEl True to return an Roo.Element
10437          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10438          */
10439         insertHtml : function(where, html, returnEl){
10440             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10441             return returnEl ? Roo.get(el) : el;
10442         },
10443
10444         /**
10445          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10446          * @param {Object} o The object with the attributes
10447          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10448          * @return {Roo.Element} this
10449          */
10450         set : function(o, useSet){
10451             var el = this.dom;
10452             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10453             for(var attr in o){
10454                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10455                 if(attr=="cls"){
10456                     el.className = o["cls"];
10457                 }else{
10458                     if(useSet) {
10459                         el.setAttribute(attr, o[attr]);
10460                     } else {
10461                         el[attr] = o[attr];
10462                     }
10463                 }
10464             }
10465             if(o.style){
10466                 Roo.DomHelper.applyStyles(el, o.style);
10467             }
10468             return this;
10469         },
10470
10471         /**
10472          * Convenience method for constructing a KeyMap
10473          * @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:
10474          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10475          * @param {Function} fn The function to call
10476          * @param {Object} scope (optional) The scope of the function
10477          * @return {Roo.KeyMap} The KeyMap created
10478          */
10479         addKeyListener : function(key, fn, scope){
10480             var config;
10481             if(typeof key != "object" || key instanceof Array){
10482                 config = {
10483                     key: key,
10484                     fn: fn,
10485                     scope: scope
10486                 };
10487             }else{
10488                 config = {
10489                     key : key.key,
10490                     shift : key.shift,
10491                     ctrl : key.ctrl,
10492                     alt : key.alt,
10493                     fn: fn,
10494                     scope: scope
10495                 };
10496             }
10497             return new Roo.KeyMap(this, config);
10498         },
10499
10500         /**
10501          * Creates a KeyMap for this element
10502          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10503          * @return {Roo.KeyMap} The KeyMap created
10504          */
10505         addKeyMap : function(config){
10506             return new Roo.KeyMap(this, config);
10507         },
10508
10509         /**
10510          * Returns true if this element is scrollable.
10511          * @return {Boolean}
10512          */
10513          isScrollable : function(){
10514             var dom = this.dom;
10515             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10516         },
10517
10518         /**
10519          * 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().
10520          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10521          * @param {Number} value The new scroll value
10522          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10523          * @return {Element} this
10524          */
10525
10526         scrollTo : function(side, value, animate){
10527             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10528             if(!animate || !A){
10529                 this.dom[prop] = value;
10530             }else{
10531                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10532                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10533             }
10534             return this;
10535         },
10536
10537         /**
10538          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10539          * within this element's scrollable range.
10540          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10541          * @param {Number} distance How far to scroll the element in pixels
10542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10543          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10544          * was scrolled as far as it could go.
10545          */
10546          scroll : function(direction, distance, animate){
10547              if(!this.isScrollable()){
10548                  return;
10549              }
10550              var el = this.dom;
10551              var l = el.scrollLeft, t = el.scrollTop;
10552              var w = el.scrollWidth, h = el.scrollHeight;
10553              var cw = el.clientWidth, ch = el.clientHeight;
10554              direction = direction.toLowerCase();
10555              var scrolled = false;
10556              var a = this.preanim(arguments, 2);
10557              switch(direction){
10558                  case "l":
10559                  case "left":
10560                      if(w - l > cw){
10561                          var v = Math.min(l + distance, w-cw);
10562                          this.scrollTo("left", v, a);
10563                          scrolled = true;
10564                      }
10565                      break;
10566                 case "r":
10567                 case "right":
10568                      if(l > 0){
10569                          var v = Math.max(l - distance, 0);
10570                          this.scrollTo("left", v, a);
10571                          scrolled = true;
10572                      }
10573                      break;
10574                 case "t":
10575                 case "top":
10576                 case "up":
10577                      if(t > 0){
10578                          var v = Math.max(t - distance, 0);
10579                          this.scrollTo("top", v, a);
10580                          scrolled = true;
10581                      }
10582                      break;
10583                 case "b":
10584                 case "bottom":
10585                 case "down":
10586                      if(h - t > ch){
10587                          var v = Math.min(t + distance, h-ch);
10588                          this.scrollTo("top", v, a);
10589                          scrolled = true;
10590                      }
10591                      break;
10592              }
10593              return scrolled;
10594         },
10595
10596         /**
10597          * Translates the passed page coordinates into left/top css values for this element
10598          * @param {Number/Array} x The page x or an array containing [x, y]
10599          * @param {Number} y The page y
10600          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10601          */
10602         translatePoints : function(x, y){
10603             if(typeof x == 'object' || x instanceof Array){
10604                 y = x[1]; x = x[0];
10605             }
10606             var p = this.getStyle('position');
10607             var o = this.getXY();
10608
10609             var l = parseInt(this.getStyle('left'), 10);
10610             var t = parseInt(this.getStyle('top'), 10);
10611
10612             if(isNaN(l)){
10613                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10614             }
10615             if(isNaN(t)){
10616                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10617             }
10618
10619             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10620         },
10621
10622         /**
10623          * Returns the current scroll position of the element.
10624          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10625          */
10626         getScroll : function(){
10627             var d = this.dom, doc = document;
10628             if(d == doc || d == doc.body){
10629                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10630                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10631                 return {left: l, top: t};
10632             }else{
10633                 return {left: d.scrollLeft, top: d.scrollTop};
10634             }
10635         },
10636
10637         /**
10638          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10639          * are convert to standard 6 digit hex color.
10640          * @param {String} attr The css attribute
10641          * @param {String} defaultValue The default value to use when a valid color isn't found
10642          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10643          * YUI color anims.
10644          */
10645         getColor : function(attr, defaultValue, prefix){
10646             var v = this.getStyle(attr);
10647             if(!v || v == "transparent" || v == "inherit") {
10648                 return defaultValue;
10649             }
10650             var color = typeof prefix == "undefined" ? "#" : prefix;
10651             if(v.substr(0, 4) == "rgb("){
10652                 var rvs = v.slice(4, v.length -1).split(",");
10653                 for(var i = 0; i < 3; i++){
10654                     var h = parseInt(rvs[i]).toString(16);
10655                     if(h < 16){
10656                         h = "0" + h;
10657                     }
10658                     color += h;
10659                 }
10660             } else {
10661                 if(v.substr(0, 1) == "#"){
10662                     if(v.length == 4) {
10663                         for(var i = 1; i < 4; i++){
10664                             var c = v.charAt(i);
10665                             color +=  c + c;
10666                         }
10667                     }else if(v.length == 7){
10668                         color += v.substr(1);
10669                     }
10670                 }
10671             }
10672             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10673         },
10674
10675         /**
10676          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10677          * gradient background, rounded corners and a 4-way shadow.
10678          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10679          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10680          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10681          * @return {Roo.Element} this
10682          */
10683         boxWrap : function(cls){
10684             cls = cls || 'x-box';
10685             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10686             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10687             return el;
10688         },
10689
10690         /**
10691          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10692          * @param {String} namespace The namespace in which to look for the attribute
10693          * @param {String} name The attribute name
10694          * @return {String} The attribute value
10695          */
10696         getAttributeNS : Roo.isIE ? function(ns, name){
10697             var d = this.dom;
10698             var type = typeof d[ns+":"+name];
10699             if(type != 'undefined' && type != 'unknown'){
10700                 return d[ns+":"+name];
10701             }
10702             return d[name];
10703         } : function(ns, name){
10704             var d = this.dom;
10705             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10706         },
10707         
10708         
10709         /**
10710          * Sets or Returns the value the dom attribute value
10711          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10712          * @param {String} value (optional) The value to set the attribute to
10713          * @return {String} The attribute value
10714          */
10715         attr : function(name){
10716             if (arguments.length > 1) {
10717                 this.dom.setAttribute(name, arguments[1]);
10718                 return arguments[1];
10719             }
10720             if (typeof(name) == 'object') {
10721                 for(var i in name) {
10722                     this.attr(i, name[i]);
10723                 }
10724                 return name;
10725             }
10726             
10727             
10728             if (!this.dom.hasAttribute(name)) {
10729                 return undefined;
10730             }
10731             return this.dom.getAttribute(name);
10732         }
10733         
10734         
10735         
10736     };
10737
10738     var ep = El.prototype;
10739
10740     /**
10741      * Appends an event handler (Shorthand for addListener)
10742      * @param {String}   eventName     The type of event to append
10743      * @param {Function} fn        The method the event invokes
10744      * @param {Object} scope       (optional) The scope (this object) of the fn
10745      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10746      * @method
10747      */
10748     ep.on = ep.addListener;
10749         // backwards compat
10750     ep.mon = ep.addListener;
10751
10752     /**
10753      * Removes an event handler from this element (shorthand for removeListener)
10754      * @param {String} eventName the type of event to remove
10755      * @param {Function} fn the method the event invokes
10756      * @return {Roo.Element} this
10757      * @method
10758      */
10759     ep.un = ep.removeListener;
10760
10761     /**
10762      * true to automatically adjust width and height settings for box-model issues (default to true)
10763      */
10764     ep.autoBoxAdjust = true;
10765
10766     // private
10767     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10768
10769     // private
10770     El.addUnits = function(v, defaultUnit){
10771         if(v === "" || v == "auto"){
10772             return v;
10773         }
10774         if(v === undefined){
10775             return '';
10776         }
10777         if(typeof v == "number" || !El.unitPattern.test(v)){
10778             return v + (defaultUnit || 'px');
10779         }
10780         return v;
10781     };
10782
10783     // special markup used throughout Roo when box wrapping elements
10784     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>';
10785     /**
10786      * Visibility mode constant - Use visibility to hide element
10787      * @static
10788      * @type Number
10789      */
10790     El.VISIBILITY = 1;
10791     /**
10792      * Visibility mode constant - Use display to hide element
10793      * @static
10794      * @type Number
10795      */
10796     El.DISPLAY = 2;
10797
10798     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10799     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10800     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10801
10802
10803
10804     /**
10805      * @private
10806      */
10807     El.cache = {};
10808
10809     var docEl;
10810
10811     /**
10812      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10813      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10814      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10815      * @return {Element} The Element object
10816      * @static
10817      */
10818     El.get = function(el){
10819         var ex, elm, id;
10820         if(!el){ return null; }
10821         if(typeof el == "string"){ // element id
10822             if(!(elm = document.getElementById(el))){
10823                 return null;
10824             }
10825             if(ex = El.cache[el]){
10826                 ex.dom = elm;
10827             }else{
10828                 ex = El.cache[el] = new El(elm);
10829             }
10830             return ex;
10831         }else if(el.tagName){ // dom element
10832             if(!(id = el.id)){
10833                 id = Roo.id(el);
10834             }
10835             if(ex = El.cache[id]){
10836                 ex.dom = el;
10837             }else{
10838                 ex = El.cache[id] = new El(el);
10839             }
10840             return ex;
10841         }else if(el instanceof El){
10842             if(el != docEl){
10843                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10844                                                               // catch case where it hasn't been appended
10845                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10846             }
10847             return el;
10848         }else if(el.isComposite){
10849             return el;
10850         }else if(el instanceof Array){
10851             return El.select(el);
10852         }else if(el == document){
10853             // create a bogus element object representing the document object
10854             if(!docEl){
10855                 var f = function(){};
10856                 f.prototype = El.prototype;
10857                 docEl = new f();
10858                 docEl.dom = document;
10859             }
10860             return docEl;
10861         }
10862         return null;
10863     };
10864
10865     // private
10866     El.uncache = function(el){
10867         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10868             if(a[i]){
10869                 delete El.cache[a[i].id || a[i]];
10870             }
10871         }
10872     };
10873
10874     // private
10875     // Garbage collection - uncache elements/purge listeners on orphaned elements
10876     // so we don't hold a reference and cause the browser to retain them
10877     El.garbageCollect = function(){
10878         if(!Roo.enableGarbageCollector){
10879             clearInterval(El.collectorThread);
10880             return;
10881         }
10882         for(var eid in El.cache){
10883             var el = El.cache[eid], d = el.dom;
10884             // -------------------------------------------------------
10885             // Determining what is garbage:
10886             // -------------------------------------------------------
10887             // !d
10888             // dom node is null, definitely garbage
10889             // -------------------------------------------------------
10890             // !d.parentNode
10891             // no parentNode == direct orphan, definitely garbage
10892             // -------------------------------------------------------
10893             // !d.offsetParent && !document.getElementById(eid)
10894             // display none elements have no offsetParent so we will
10895             // also try to look it up by it's id. However, check
10896             // offsetParent first so we don't do unneeded lookups.
10897             // This enables collection of elements that are not orphans
10898             // directly, but somewhere up the line they have an orphan
10899             // parent.
10900             // -------------------------------------------------------
10901             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10902                 delete El.cache[eid];
10903                 if(d && Roo.enableListenerCollection){
10904                     E.purgeElement(d);
10905                 }
10906             }
10907         }
10908     }
10909     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10910
10911
10912     // dom is optional
10913     El.Flyweight = function(dom){
10914         this.dom = dom;
10915     };
10916     El.Flyweight.prototype = El.prototype;
10917
10918     El._flyweights = {};
10919     /**
10920      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10921      * the dom node can be overwritten by other code.
10922      * @param {String/HTMLElement} el The dom node or id
10923      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10924      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10925      * @static
10926      * @return {Element} The shared Element object
10927      */
10928     El.fly = function(el, named){
10929         named = named || '_global';
10930         el = Roo.getDom(el);
10931         if(!el){
10932             return null;
10933         }
10934         if(!El._flyweights[named]){
10935             El._flyweights[named] = new El.Flyweight();
10936         }
10937         El._flyweights[named].dom = el;
10938         return El._flyweights[named];
10939     };
10940
10941     /**
10942      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10943      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10944      * Shorthand of {@link Roo.Element#get}
10945      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10946      * @return {Element} The Element object
10947      * @member Roo
10948      * @method get
10949      */
10950     Roo.get = El.get;
10951     /**
10952      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10953      * the dom node can be overwritten by other code.
10954      * Shorthand of {@link Roo.Element#fly}
10955      * @param {String/HTMLElement} el The dom node or id
10956      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10957      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10958      * @static
10959      * @return {Element} The shared Element object
10960      * @member Roo
10961      * @method fly
10962      */
10963     Roo.fly = El.fly;
10964
10965     // speedy lookup for elements never to box adjust
10966     var noBoxAdjust = Roo.isStrict ? {
10967         select:1
10968     } : {
10969         input:1, select:1, textarea:1
10970     };
10971     if(Roo.isIE || Roo.isGecko){
10972         noBoxAdjust['button'] = 1;
10973     }
10974
10975
10976     Roo.EventManager.on(window, 'unload', function(){
10977         delete El.cache;
10978         delete El._flyweights;
10979     });
10980 })();
10981
10982
10983
10984
10985 if(Roo.DomQuery){
10986     Roo.Element.selectorFunction = Roo.DomQuery.select;
10987 }
10988
10989 Roo.Element.select = function(selector, unique, root){
10990     var els;
10991     if(typeof selector == "string"){
10992         els = Roo.Element.selectorFunction(selector, root);
10993     }else if(selector.length !== undefined){
10994         els = selector;
10995     }else{
10996         throw "Invalid selector";
10997     }
10998     if(unique === true){
10999         return new Roo.CompositeElement(els);
11000     }else{
11001         return new Roo.CompositeElementLite(els);
11002     }
11003 };
11004 /**
11005  * Selects elements based on the passed CSS selector to enable working on them as 1.
11006  * @param {String/Array} selector The CSS selector or an array of elements
11007  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11008  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11009  * @return {CompositeElementLite/CompositeElement}
11010  * @member Roo
11011  * @method select
11012  */
11013 Roo.select = Roo.Element.select;
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028 /*
11029  * Based on:
11030  * Ext JS Library 1.1.1
11031  * Copyright(c) 2006-2007, Ext JS, LLC.
11032  *
11033  * Originally Released Under LGPL - original licence link has changed is not relivant.
11034  *
11035  * Fork - LGPL
11036  * <script type="text/javascript">
11037  */
11038
11039
11040
11041 //Notifies Element that fx methods are available
11042 Roo.enableFx = true;
11043
11044 /**
11045  * @class Roo.Fx
11046  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11047  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11048  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11049  * Element effects to work.</p><br/>
11050  *
11051  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11052  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11053  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11054  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11055  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11056  * expected results and should be done with care.</p><br/>
11057  *
11058  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11059  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11060 <pre>
11061 Value  Description
11062 -----  -----------------------------
11063 tl     The top left corner
11064 t      The center of the top edge
11065 tr     The top right corner
11066 l      The center of the left edge
11067 r      The center of the right edge
11068 bl     The bottom left corner
11069 b      The center of the bottom edge
11070 br     The bottom right corner
11071 </pre>
11072  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11073  * below are common options that can be passed to any Fx method.</b>
11074  * @cfg {Function} callback A function called when the effect is finished
11075  * @cfg {Object} scope The scope of the effect function
11076  * @cfg {String} easing A valid Easing value for the effect
11077  * @cfg {String} afterCls A css class to apply after the effect
11078  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11079  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11080  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11081  * effects that end with the element being visually hidden, ignored otherwise)
11082  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11083  * a function which returns such a specification that will be applied to the Element after the effect finishes
11084  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11085  * @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
11086  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11087  */
11088 Roo.Fx = {
11089         /**
11090          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11091          * origin for the slide effect.  This function automatically handles wrapping the element with
11092          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11093          * Usage:
11094          *<pre><code>
11095 // default: slide the element in from the top
11096 el.slideIn();
11097
11098 // custom: slide the element in from the right with a 2-second duration
11099 el.slideIn('r', { duration: 2 });
11100
11101 // common config options shown with default values
11102 el.slideIn('t', {
11103     easing: 'easeOut',
11104     duration: .5
11105 });
11106 </code></pre>
11107          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11108          * @param {Object} options (optional) Object literal with any of the Fx config options
11109          * @return {Roo.Element} The Element
11110          */
11111     slideIn : function(anchor, o){
11112         var el = this.getFxEl();
11113         o = o || {};
11114
11115         el.queueFx(o, function(){
11116
11117             anchor = anchor || "t";
11118
11119             // fix display to visibility
11120             this.fixDisplay();
11121
11122             // restore values after effect
11123             var r = this.getFxRestore();
11124             var b = this.getBox();
11125             // fixed size for slide
11126             this.setSize(b);
11127
11128             // wrap if needed
11129             var wrap = this.fxWrap(r.pos, o, "hidden");
11130
11131             var st = this.dom.style;
11132             st.visibility = "visible";
11133             st.position = "absolute";
11134
11135             // clear out temp styles after slide and unwrap
11136             var after = function(){
11137                 el.fxUnwrap(wrap, r.pos, o);
11138                 st.width = r.width;
11139                 st.height = r.height;
11140                 el.afterFx(o);
11141             };
11142             // time to calc the positions
11143             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11144
11145             switch(anchor.toLowerCase()){
11146                 case "t":
11147                     wrap.setSize(b.width, 0);
11148                     st.left = st.bottom = "0";
11149                     a = {height: bh};
11150                 break;
11151                 case "l":
11152                     wrap.setSize(0, b.height);
11153                     st.right = st.top = "0";
11154                     a = {width: bw};
11155                 break;
11156                 case "r":
11157                     wrap.setSize(0, b.height);
11158                     wrap.setX(b.right);
11159                     st.left = st.top = "0";
11160                     a = {width: bw, points: pt};
11161                 break;
11162                 case "b":
11163                     wrap.setSize(b.width, 0);
11164                     wrap.setY(b.bottom);
11165                     st.left = st.top = "0";
11166                     a = {height: bh, points: pt};
11167                 break;
11168                 case "tl":
11169                     wrap.setSize(0, 0);
11170                     st.right = st.bottom = "0";
11171                     a = {width: bw, height: bh};
11172                 break;
11173                 case "bl":
11174                     wrap.setSize(0, 0);
11175                     wrap.setY(b.y+b.height);
11176                     st.right = st.top = "0";
11177                     a = {width: bw, height: bh, points: pt};
11178                 break;
11179                 case "br":
11180                     wrap.setSize(0, 0);
11181                     wrap.setXY([b.right, b.bottom]);
11182                     st.left = st.top = "0";
11183                     a = {width: bw, height: bh, points: pt};
11184                 break;
11185                 case "tr":
11186                     wrap.setSize(0, 0);
11187                     wrap.setX(b.x+b.width);
11188                     st.left = st.bottom = "0";
11189                     a = {width: bw, height: bh, points: pt};
11190                 break;
11191             }
11192             this.dom.style.visibility = "visible";
11193             wrap.show();
11194
11195             arguments.callee.anim = wrap.fxanim(a,
11196                 o,
11197                 'motion',
11198                 .5,
11199                 'easeOut', after);
11200         });
11201         return this;
11202     },
11203     
11204         /**
11205          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11206          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11207          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11208          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11209          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11210          * Usage:
11211          *<pre><code>
11212 // default: slide the element out to the top
11213 el.slideOut();
11214
11215 // custom: slide the element out to the right with a 2-second duration
11216 el.slideOut('r', { duration: 2 });
11217
11218 // common config options shown with default values
11219 el.slideOut('t', {
11220     easing: 'easeOut',
11221     duration: .5,
11222     remove: false,
11223     useDisplay: false
11224 });
11225 </code></pre>
11226          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11227          * @param {Object} options (optional) Object literal with any of the Fx config options
11228          * @return {Roo.Element} The Element
11229          */
11230     slideOut : function(anchor, o){
11231         var el = this.getFxEl();
11232         o = o || {};
11233
11234         el.queueFx(o, function(){
11235
11236             anchor = anchor || "t";
11237
11238             // restore values after effect
11239             var r = this.getFxRestore();
11240             
11241             var b = this.getBox();
11242             // fixed size for slide
11243             this.setSize(b);
11244
11245             // wrap if needed
11246             var wrap = this.fxWrap(r.pos, o, "visible");
11247
11248             var st = this.dom.style;
11249             st.visibility = "visible";
11250             st.position = "absolute";
11251
11252             wrap.setSize(b);
11253
11254             var after = function(){
11255                 if(o.useDisplay){
11256                     el.setDisplayed(false);
11257                 }else{
11258                     el.hide();
11259                 }
11260
11261                 el.fxUnwrap(wrap, r.pos, o);
11262
11263                 st.width = r.width;
11264                 st.height = r.height;
11265
11266                 el.afterFx(o);
11267             };
11268
11269             var a, zero = {to: 0};
11270             switch(anchor.toLowerCase()){
11271                 case "t":
11272                     st.left = st.bottom = "0";
11273                     a = {height: zero};
11274                 break;
11275                 case "l":
11276                     st.right = st.top = "0";
11277                     a = {width: zero};
11278                 break;
11279                 case "r":
11280                     st.left = st.top = "0";
11281                     a = {width: zero, points: {to:[b.right, b.y]}};
11282                 break;
11283                 case "b":
11284                     st.left = st.top = "0";
11285                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11286                 break;
11287                 case "tl":
11288                     st.right = st.bottom = "0";
11289                     a = {width: zero, height: zero};
11290                 break;
11291                 case "bl":
11292                     st.right = st.top = "0";
11293                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11294                 break;
11295                 case "br":
11296                     st.left = st.top = "0";
11297                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11298                 break;
11299                 case "tr":
11300                     st.left = st.bottom = "0";
11301                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11302                 break;
11303             }
11304
11305             arguments.callee.anim = wrap.fxanim(a,
11306                 o,
11307                 'motion',
11308                 .5,
11309                 "easeOut", after);
11310         });
11311         return this;
11312     },
11313
11314         /**
11315          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11316          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11317          * The element must be removed from the DOM using the 'remove' config option if desired.
11318          * Usage:
11319          *<pre><code>
11320 // default
11321 el.puff();
11322
11323 // common config options shown with default values
11324 el.puff({
11325     easing: 'easeOut',
11326     duration: .5,
11327     remove: false,
11328     useDisplay: false
11329 });
11330 </code></pre>
11331          * @param {Object} options (optional) Object literal with any of the Fx config options
11332          * @return {Roo.Element} The Element
11333          */
11334     puff : function(o){
11335         var el = this.getFxEl();
11336         o = o || {};
11337
11338         el.queueFx(o, function(){
11339             this.clearOpacity();
11340             this.show();
11341
11342             // restore values after effect
11343             var r = this.getFxRestore();
11344             var st = this.dom.style;
11345
11346             var after = function(){
11347                 if(o.useDisplay){
11348                     el.setDisplayed(false);
11349                 }else{
11350                     el.hide();
11351                 }
11352
11353                 el.clearOpacity();
11354
11355                 el.setPositioning(r.pos);
11356                 st.width = r.width;
11357                 st.height = r.height;
11358                 st.fontSize = '';
11359                 el.afterFx(o);
11360             };
11361
11362             var width = this.getWidth();
11363             var height = this.getHeight();
11364
11365             arguments.callee.anim = this.fxanim({
11366                     width : {to: this.adjustWidth(width * 2)},
11367                     height : {to: this.adjustHeight(height * 2)},
11368                     points : {by: [-(width * .5), -(height * .5)]},
11369                     opacity : {to: 0},
11370                     fontSize: {to:200, unit: "%"}
11371                 },
11372                 o,
11373                 'motion',
11374                 .5,
11375                 "easeOut", after);
11376         });
11377         return this;
11378     },
11379
11380         /**
11381          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11382          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11383          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11384          * Usage:
11385          *<pre><code>
11386 // default
11387 el.switchOff();
11388
11389 // all config options shown with default values
11390 el.switchOff({
11391     easing: 'easeIn',
11392     duration: .3,
11393     remove: false,
11394     useDisplay: false
11395 });
11396 </code></pre>
11397          * @param {Object} options (optional) Object literal with any of the Fx config options
11398          * @return {Roo.Element} The Element
11399          */
11400     switchOff : function(o){
11401         var el = this.getFxEl();
11402         o = o || {};
11403
11404         el.queueFx(o, function(){
11405             this.clearOpacity();
11406             this.clip();
11407
11408             // restore values after effect
11409             var r = this.getFxRestore();
11410             var st = this.dom.style;
11411
11412             var after = function(){
11413                 if(o.useDisplay){
11414                     el.setDisplayed(false);
11415                 }else{
11416                     el.hide();
11417                 }
11418
11419                 el.clearOpacity();
11420                 el.setPositioning(r.pos);
11421                 st.width = r.width;
11422                 st.height = r.height;
11423
11424                 el.afterFx(o);
11425             };
11426
11427             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11428                 this.clearOpacity();
11429                 (function(){
11430                     this.fxanim({
11431                         height:{to:1},
11432                         points:{by:[0, this.getHeight() * .5]}
11433                     }, o, 'motion', 0.3, 'easeIn', after);
11434                 }).defer(100, this);
11435             });
11436         });
11437         return this;
11438     },
11439
11440     /**
11441      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11442      * changed using the "attr" config option) and then fading back to the original color. If no original
11443      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11444      * Usage:
11445 <pre><code>
11446 // default: highlight background to yellow
11447 el.highlight();
11448
11449 // custom: highlight foreground text to blue for 2 seconds
11450 el.highlight("0000ff", { attr: 'color', duration: 2 });
11451
11452 // common config options shown with default values
11453 el.highlight("ffff9c", {
11454     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11455     endColor: (current color) or "ffffff",
11456     easing: 'easeIn',
11457     duration: 1
11458 });
11459 </code></pre>
11460      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11461      * @param {Object} options (optional) Object literal with any of the Fx config options
11462      * @return {Roo.Element} The Element
11463      */ 
11464     highlight : function(color, o){
11465         var el = this.getFxEl();
11466         o = o || {};
11467
11468         el.queueFx(o, function(){
11469             color = color || "ffff9c";
11470             attr = o.attr || "backgroundColor";
11471
11472             this.clearOpacity();
11473             this.show();
11474
11475             var origColor = this.getColor(attr);
11476             var restoreColor = this.dom.style[attr];
11477             endColor = (o.endColor || origColor) || "ffffff";
11478
11479             var after = function(){
11480                 el.dom.style[attr] = restoreColor;
11481                 el.afterFx(o);
11482             };
11483
11484             var a = {};
11485             a[attr] = {from: color, to: endColor};
11486             arguments.callee.anim = this.fxanim(a,
11487                 o,
11488                 'color',
11489                 1,
11490                 'easeIn', after);
11491         });
11492         return this;
11493     },
11494
11495    /**
11496     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11497     * Usage:
11498 <pre><code>
11499 // default: a single light blue ripple
11500 el.frame();
11501
11502 // custom: 3 red ripples lasting 3 seconds total
11503 el.frame("ff0000", 3, { duration: 3 });
11504
11505 // common config options shown with default values
11506 el.frame("C3DAF9", 1, {
11507     duration: 1 //duration of entire animation (not each individual ripple)
11508     // Note: Easing is not configurable and will be ignored if included
11509 });
11510 </code></pre>
11511     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11512     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11513     * @param {Object} options (optional) Object literal with any of the Fx config options
11514     * @return {Roo.Element} The Element
11515     */
11516     frame : function(color, count, o){
11517         var el = this.getFxEl();
11518         o = o || {};
11519
11520         el.queueFx(o, function(){
11521             color = color || "#C3DAF9";
11522             if(color.length == 6){
11523                 color = "#" + color;
11524             }
11525             count = count || 1;
11526             duration = o.duration || 1;
11527             this.show();
11528
11529             var b = this.getBox();
11530             var animFn = function(){
11531                 var proxy = this.createProxy({
11532
11533                      style:{
11534                         visbility:"hidden",
11535                         position:"absolute",
11536                         "z-index":"35000", // yee haw
11537                         border:"0px solid " + color
11538                      }
11539                   });
11540                 var scale = Roo.isBorderBox ? 2 : 1;
11541                 proxy.animate({
11542                     top:{from:b.y, to:b.y - 20},
11543                     left:{from:b.x, to:b.x - 20},
11544                     borderWidth:{from:0, to:10},
11545                     opacity:{from:1, to:0},
11546                     height:{from:b.height, to:(b.height + (20*scale))},
11547                     width:{from:b.width, to:(b.width + (20*scale))}
11548                 }, duration, function(){
11549                     proxy.remove();
11550                 });
11551                 if(--count > 0){
11552                      animFn.defer((duration/2)*1000, this);
11553                 }else{
11554                     el.afterFx(o);
11555                 }
11556             };
11557             animFn.call(this);
11558         });
11559         return this;
11560     },
11561
11562    /**
11563     * Creates a pause before any subsequent queued effects begin.  If there are
11564     * no effects queued after the pause it will have no effect.
11565     * Usage:
11566 <pre><code>
11567 el.pause(1);
11568 </code></pre>
11569     * @param {Number} seconds The length of time to pause (in seconds)
11570     * @return {Roo.Element} The Element
11571     */
11572     pause : function(seconds){
11573         var el = this.getFxEl();
11574         var o = {};
11575
11576         el.queueFx(o, function(){
11577             setTimeout(function(){
11578                 el.afterFx(o);
11579             }, seconds * 1000);
11580         });
11581         return this;
11582     },
11583
11584    /**
11585     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11586     * using the "endOpacity" config option.
11587     * Usage:
11588 <pre><code>
11589 // default: fade in from opacity 0 to 100%
11590 el.fadeIn();
11591
11592 // custom: fade in from opacity 0 to 75% over 2 seconds
11593 el.fadeIn({ endOpacity: .75, duration: 2});
11594
11595 // common config options shown with default values
11596 el.fadeIn({
11597     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11598     easing: 'easeOut',
11599     duration: .5
11600 });
11601 </code></pre>
11602     * @param {Object} options (optional) Object literal with any of the Fx config options
11603     * @return {Roo.Element} The Element
11604     */
11605     fadeIn : function(o){
11606         var el = this.getFxEl();
11607         o = o || {};
11608         el.queueFx(o, function(){
11609             this.setOpacity(0);
11610             this.fixDisplay();
11611             this.dom.style.visibility = 'visible';
11612             var to = o.endOpacity || 1;
11613             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11614                 o, null, .5, "easeOut", function(){
11615                 if(to == 1){
11616                     this.clearOpacity();
11617                 }
11618                 el.afterFx(o);
11619             });
11620         });
11621         return this;
11622     },
11623
11624    /**
11625     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11626     * using the "endOpacity" config option.
11627     * Usage:
11628 <pre><code>
11629 // default: fade out from the element's current opacity to 0
11630 el.fadeOut();
11631
11632 // custom: fade out from the element's current opacity to 25% over 2 seconds
11633 el.fadeOut({ endOpacity: .25, duration: 2});
11634
11635 // common config options shown with default values
11636 el.fadeOut({
11637     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11638     easing: 'easeOut',
11639     duration: .5
11640     remove: false,
11641     useDisplay: false
11642 });
11643 </code></pre>
11644     * @param {Object} options (optional) Object literal with any of the Fx config options
11645     * @return {Roo.Element} The Element
11646     */
11647     fadeOut : function(o){
11648         var el = this.getFxEl();
11649         o = o || {};
11650         el.queueFx(o, function(){
11651             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11652                 o, null, .5, "easeOut", function(){
11653                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11654                      this.dom.style.display = "none";
11655                 }else{
11656                      this.dom.style.visibility = "hidden";
11657                 }
11658                 this.clearOpacity();
11659                 el.afterFx(o);
11660             });
11661         });
11662         return this;
11663     },
11664
11665    /**
11666     * Animates the transition of an element's dimensions from a starting height/width
11667     * to an ending height/width.
11668     * Usage:
11669 <pre><code>
11670 // change height and width to 100x100 pixels
11671 el.scale(100, 100);
11672
11673 // common config options shown with default values.  The height and width will default to
11674 // the element's existing values if passed as null.
11675 el.scale(
11676     [element's width],
11677     [element's height], {
11678     easing: 'easeOut',
11679     duration: .35
11680 });
11681 </code></pre>
11682     * @param {Number} width  The new width (pass undefined to keep the original width)
11683     * @param {Number} height  The new height (pass undefined to keep the original height)
11684     * @param {Object} options (optional) Object literal with any of the Fx config options
11685     * @return {Roo.Element} The Element
11686     */
11687     scale : function(w, h, o){
11688         this.shift(Roo.apply({}, o, {
11689             width: w,
11690             height: h
11691         }));
11692         return this;
11693     },
11694
11695    /**
11696     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11697     * Any of these properties not specified in the config object will not be changed.  This effect 
11698     * requires that at least one new dimension, position or opacity setting must be passed in on
11699     * the config object in order for the function to have any effect.
11700     * Usage:
11701 <pre><code>
11702 // slide the element horizontally to x position 200 while changing the height and opacity
11703 el.shift({ x: 200, height: 50, opacity: .8 });
11704
11705 // common config options shown with default values.
11706 el.shift({
11707     width: [element's width],
11708     height: [element's height],
11709     x: [element's x position],
11710     y: [element's y position],
11711     opacity: [element's opacity],
11712     easing: 'easeOut',
11713     duration: .35
11714 });
11715 </code></pre>
11716     * @param {Object} options  Object literal with any of the Fx config options
11717     * @return {Roo.Element} The Element
11718     */
11719     shift : function(o){
11720         var el = this.getFxEl();
11721         o = o || {};
11722         el.queueFx(o, function(){
11723             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11724             if(w !== undefined){
11725                 a.width = {to: this.adjustWidth(w)};
11726             }
11727             if(h !== undefined){
11728                 a.height = {to: this.adjustHeight(h)};
11729             }
11730             if(x !== undefined || y !== undefined){
11731                 a.points = {to: [
11732                     x !== undefined ? x : this.getX(),
11733                     y !== undefined ? y : this.getY()
11734                 ]};
11735             }
11736             if(op !== undefined){
11737                 a.opacity = {to: op};
11738             }
11739             if(o.xy !== undefined){
11740                 a.points = {to: o.xy};
11741             }
11742             arguments.callee.anim = this.fxanim(a,
11743                 o, 'motion', .35, "easeOut", function(){
11744                 el.afterFx(o);
11745             });
11746         });
11747         return this;
11748     },
11749
11750         /**
11751          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11752          * ending point of the effect.
11753          * Usage:
11754          *<pre><code>
11755 // default: slide the element downward while fading out
11756 el.ghost();
11757
11758 // custom: slide the element out to the right with a 2-second duration
11759 el.ghost('r', { duration: 2 });
11760
11761 // common config options shown with default values
11762 el.ghost('b', {
11763     easing: 'easeOut',
11764     duration: .5
11765     remove: false,
11766     useDisplay: false
11767 });
11768 </code></pre>
11769          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11770          * @param {Object} options (optional) Object literal with any of the Fx config options
11771          * @return {Roo.Element} The Element
11772          */
11773     ghost : function(anchor, o){
11774         var el = this.getFxEl();
11775         o = o || {};
11776
11777         el.queueFx(o, function(){
11778             anchor = anchor || "b";
11779
11780             // restore values after effect
11781             var r = this.getFxRestore();
11782             var w = this.getWidth(),
11783                 h = this.getHeight();
11784
11785             var st = this.dom.style;
11786
11787             var after = function(){
11788                 if(o.useDisplay){
11789                     el.setDisplayed(false);
11790                 }else{
11791                     el.hide();
11792                 }
11793
11794                 el.clearOpacity();
11795                 el.setPositioning(r.pos);
11796                 st.width = r.width;
11797                 st.height = r.height;
11798
11799                 el.afterFx(o);
11800             };
11801
11802             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11803             switch(anchor.toLowerCase()){
11804                 case "t":
11805                     pt.by = [0, -h];
11806                 break;
11807                 case "l":
11808                     pt.by = [-w, 0];
11809                 break;
11810                 case "r":
11811                     pt.by = [w, 0];
11812                 break;
11813                 case "b":
11814                     pt.by = [0, h];
11815                 break;
11816                 case "tl":
11817                     pt.by = [-w, -h];
11818                 break;
11819                 case "bl":
11820                     pt.by = [-w, h];
11821                 break;
11822                 case "br":
11823                     pt.by = [w, h];
11824                 break;
11825                 case "tr":
11826                     pt.by = [w, -h];
11827                 break;
11828             }
11829
11830             arguments.callee.anim = this.fxanim(a,
11831                 o,
11832                 'motion',
11833                 .5,
11834                 "easeOut", after);
11835         });
11836         return this;
11837     },
11838
11839         /**
11840          * Ensures that all effects queued after syncFx is called on the element are
11841          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11842          * @return {Roo.Element} The Element
11843          */
11844     syncFx : function(){
11845         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11846             block : false,
11847             concurrent : true,
11848             stopFx : false
11849         });
11850         return this;
11851     },
11852
11853         /**
11854          * Ensures that all effects queued after sequenceFx is called on the element are
11855          * run in sequence.  This is the opposite of {@link #syncFx}.
11856          * @return {Roo.Element} The Element
11857          */
11858     sequenceFx : function(){
11859         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11860             block : false,
11861             concurrent : false,
11862             stopFx : false
11863         });
11864         return this;
11865     },
11866
11867         /* @private */
11868     nextFx : function(){
11869         var ef = this.fxQueue[0];
11870         if(ef){
11871             ef.call(this);
11872         }
11873     },
11874
11875         /**
11876          * Returns true if the element has any effects actively running or queued, else returns false.
11877          * @return {Boolean} True if element has active effects, else false
11878          */
11879     hasActiveFx : function(){
11880         return this.fxQueue && this.fxQueue[0];
11881     },
11882
11883         /**
11884          * Stops any running effects and clears the element's internal effects queue if it contains
11885          * any additional effects that haven't started yet.
11886          * @return {Roo.Element} The Element
11887          */
11888     stopFx : function(){
11889         if(this.hasActiveFx()){
11890             var cur = this.fxQueue[0];
11891             if(cur && cur.anim && cur.anim.isAnimated()){
11892                 this.fxQueue = [cur]; // clear out others
11893                 cur.anim.stop(true);
11894             }
11895         }
11896         return this;
11897     },
11898
11899         /* @private */
11900     beforeFx : function(o){
11901         if(this.hasActiveFx() && !o.concurrent){
11902            if(o.stopFx){
11903                this.stopFx();
11904                return true;
11905            }
11906            return false;
11907         }
11908         return true;
11909     },
11910
11911         /**
11912          * Returns true if the element is currently blocking so that no other effect can be queued
11913          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11914          * used to ensure that an effect initiated by a user action runs to completion prior to the
11915          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11916          * @return {Boolean} True if blocking, else false
11917          */
11918     hasFxBlock : function(){
11919         var q = this.fxQueue;
11920         return q && q[0] && q[0].block;
11921     },
11922
11923         /* @private */
11924     queueFx : function(o, fn){
11925         if(!this.fxQueue){
11926             this.fxQueue = [];
11927         }
11928         if(!this.hasFxBlock()){
11929             Roo.applyIf(o, this.fxDefaults);
11930             if(!o.concurrent){
11931                 var run = this.beforeFx(o);
11932                 fn.block = o.block;
11933                 this.fxQueue.push(fn);
11934                 if(run){
11935                     this.nextFx();
11936                 }
11937             }else{
11938                 fn.call(this);
11939             }
11940         }
11941         return this;
11942     },
11943
11944         /* @private */
11945     fxWrap : function(pos, o, vis){
11946         var wrap;
11947         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11948             var wrapXY;
11949             if(o.fixPosition){
11950                 wrapXY = this.getXY();
11951             }
11952             var div = document.createElement("div");
11953             div.style.visibility = vis;
11954             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11955             wrap.setPositioning(pos);
11956             if(wrap.getStyle("position") == "static"){
11957                 wrap.position("relative");
11958             }
11959             this.clearPositioning('auto');
11960             wrap.clip();
11961             wrap.dom.appendChild(this.dom);
11962             if(wrapXY){
11963                 wrap.setXY(wrapXY);
11964             }
11965         }
11966         return wrap;
11967     },
11968
11969         /* @private */
11970     fxUnwrap : function(wrap, pos, o){
11971         this.clearPositioning();
11972         this.setPositioning(pos);
11973         if(!o.wrap){
11974             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11975             wrap.remove();
11976         }
11977     },
11978
11979         /* @private */
11980     getFxRestore : function(){
11981         var st = this.dom.style;
11982         return {pos: this.getPositioning(), width: st.width, height : st.height};
11983     },
11984
11985         /* @private */
11986     afterFx : function(o){
11987         if(o.afterStyle){
11988             this.applyStyles(o.afterStyle);
11989         }
11990         if(o.afterCls){
11991             this.addClass(o.afterCls);
11992         }
11993         if(o.remove === true){
11994             this.remove();
11995         }
11996         Roo.callback(o.callback, o.scope, [this]);
11997         if(!o.concurrent){
11998             this.fxQueue.shift();
11999             this.nextFx();
12000         }
12001     },
12002
12003         /* @private */
12004     getFxEl : function(){ // support for composite element fx
12005         return Roo.get(this.dom);
12006     },
12007
12008         /* @private */
12009     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12010         animType = animType || 'run';
12011         opt = opt || {};
12012         var anim = Roo.lib.Anim[animType](
12013             this.dom, args,
12014             (opt.duration || defaultDur) || .35,
12015             (opt.easing || defaultEase) || 'easeOut',
12016             function(){
12017                 Roo.callback(cb, this);
12018             },
12019             this
12020         );
12021         opt.anim = anim;
12022         return anim;
12023     }
12024 };
12025
12026 // backwords compat
12027 Roo.Fx.resize = Roo.Fx.scale;
12028
12029 //When included, Roo.Fx is automatically applied to Element so that all basic
12030 //effects are available directly via the Element API
12031 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12032  * Based on:
12033  * Ext JS Library 1.1.1
12034  * Copyright(c) 2006-2007, Ext JS, LLC.
12035  *
12036  * Originally Released Under LGPL - original licence link has changed is not relivant.
12037  *
12038  * Fork - LGPL
12039  * <script type="text/javascript">
12040  */
12041
12042
12043 /**
12044  * @class Roo.CompositeElement
12045  * Standard composite class. Creates a Roo.Element for every element in the collection.
12046  * <br><br>
12047  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12048  * actions will be performed on all the elements in this collection.</b>
12049  * <br><br>
12050  * All methods return <i>this</i> and can be chained.
12051  <pre><code>
12052  var els = Roo.select("#some-el div.some-class", true);
12053  // or select directly from an existing element
12054  var el = Roo.get('some-el');
12055  el.select('div.some-class', true);
12056
12057  els.setWidth(100); // all elements become 100 width
12058  els.hide(true); // all elements fade out and hide
12059  // or
12060  els.setWidth(100).hide(true);
12061  </code></pre>
12062  */
12063 Roo.CompositeElement = function(els){
12064     this.elements = [];
12065     this.addElements(els);
12066 };
12067 Roo.CompositeElement.prototype = {
12068     isComposite: true,
12069     addElements : function(els){
12070         if(!els) {
12071             return this;
12072         }
12073         if(typeof els == "string"){
12074             els = Roo.Element.selectorFunction(els);
12075         }
12076         var yels = this.elements;
12077         var index = yels.length-1;
12078         for(var i = 0, len = els.length; i < len; i++) {
12079                 yels[++index] = Roo.get(els[i]);
12080         }
12081         return this;
12082     },
12083
12084     /**
12085     * Clears this composite and adds the elements returned by the passed selector.
12086     * @param {String/Array} els A string CSS selector, an array of elements or an element
12087     * @return {CompositeElement} this
12088     */
12089     fill : function(els){
12090         this.elements = [];
12091         this.add(els);
12092         return this;
12093     },
12094
12095     /**
12096     * Filters this composite to only elements that match the passed selector.
12097     * @param {String} selector A string CSS selector
12098     * @param {Boolean} inverse return inverse filter (not matches)
12099     * @return {CompositeElement} this
12100     */
12101     filter : function(selector, inverse){
12102         var els = [];
12103         inverse = inverse || false;
12104         this.each(function(el){
12105             var match = inverse ? !el.is(selector) : el.is(selector);
12106             if(match){
12107                 els[els.length] = el.dom;
12108             }
12109         });
12110         this.fill(els);
12111         return this;
12112     },
12113
12114     invoke : function(fn, args){
12115         var els = this.elements;
12116         for(var i = 0, len = els.length; i < len; i++) {
12117                 Roo.Element.prototype[fn].apply(els[i], args);
12118         }
12119         return this;
12120     },
12121     /**
12122     * Adds elements to this composite.
12123     * @param {String/Array} els A string CSS selector, an array of elements or an element
12124     * @return {CompositeElement} this
12125     */
12126     add : function(els){
12127         if(typeof els == "string"){
12128             this.addElements(Roo.Element.selectorFunction(els));
12129         }else if(els.length !== undefined){
12130             this.addElements(els);
12131         }else{
12132             this.addElements([els]);
12133         }
12134         return this;
12135     },
12136     /**
12137     * Calls the passed function passing (el, this, index) for each element in this composite.
12138     * @param {Function} fn The function to call
12139     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12140     * @return {CompositeElement} this
12141     */
12142     each : function(fn, scope){
12143         var els = this.elements;
12144         for(var i = 0, len = els.length; i < len; i++){
12145             if(fn.call(scope || els[i], els[i], this, i) === false) {
12146                 break;
12147             }
12148         }
12149         return this;
12150     },
12151
12152     /**
12153      * Returns the Element object at the specified index
12154      * @param {Number} index
12155      * @return {Roo.Element}
12156      */
12157     item : function(index){
12158         return this.elements[index] || null;
12159     },
12160
12161     /**
12162      * Returns the first Element
12163      * @return {Roo.Element}
12164      */
12165     first : function(){
12166         return this.item(0);
12167     },
12168
12169     /**
12170      * Returns the last Element
12171      * @return {Roo.Element}
12172      */
12173     last : function(){
12174         return this.item(this.elements.length-1);
12175     },
12176
12177     /**
12178      * Returns the number of elements in this composite
12179      * @return Number
12180      */
12181     getCount : function(){
12182         return this.elements.length;
12183     },
12184
12185     /**
12186      * Returns true if this composite contains the passed element
12187      * @return Boolean
12188      */
12189     contains : function(el){
12190         return this.indexOf(el) !== -1;
12191     },
12192
12193     /**
12194      * Returns true if this composite contains the passed element
12195      * @return Boolean
12196      */
12197     indexOf : function(el){
12198         return this.elements.indexOf(Roo.get(el));
12199     },
12200
12201
12202     /**
12203     * Removes the specified element(s).
12204     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12205     * or an array of any of those.
12206     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12207     * @return {CompositeElement} this
12208     */
12209     removeElement : function(el, removeDom){
12210         if(el instanceof Array){
12211             for(var i = 0, len = el.length; i < len; i++){
12212                 this.removeElement(el[i]);
12213             }
12214             return this;
12215         }
12216         var index = typeof el == 'number' ? el : this.indexOf(el);
12217         if(index !== -1){
12218             if(removeDom){
12219                 var d = this.elements[index];
12220                 if(d.dom){
12221                     d.remove();
12222                 }else{
12223                     d.parentNode.removeChild(d);
12224                 }
12225             }
12226             this.elements.splice(index, 1);
12227         }
12228         return this;
12229     },
12230
12231     /**
12232     * Replaces the specified element with the passed element.
12233     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12234     * to replace.
12235     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12236     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12237     * @return {CompositeElement} this
12238     */
12239     replaceElement : function(el, replacement, domReplace){
12240         var index = typeof el == 'number' ? el : this.indexOf(el);
12241         if(index !== -1){
12242             if(domReplace){
12243                 this.elements[index].replaceWith(replacement);
12244             }else{
12245                 this.elements.splice(index, 1, Roo.get(replacement))
12246             }
12247         }
12248         return this;
12249     },
12250
12251     /**
12252      * Removes all elements.
12253      */
12254     clear : function(){
12255         this.elements = [];
12256     }
12257 };
12258 (function(){
12259     Roo.CompositeElement.createCall = function(proto, fnName){
12260         if(!proto[fnName]){
12261             proto[fnName] = function(){
12262                 return this.invoke(fnName, arguments);
12263             };
12264         }
12265     };
12266     for(var fnName in Roo.Element.prototype){
12267         if(typeof Roo.Element.prototype[fnName] == "function"){
12268             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12269         }
12270     };
12271 })();
12272 /*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282
12283 /**
12284  * @class Roo.CompositeElementLite
12285  * @extends Roo.CompositeElement
12286  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12287  <pre><code>
12288  var els = Roo.select("#some-el div.some-class");
12289  // or select directly from an existing element
12290  var el = Roo.get('some-el');
12291  el.select('div.some-class');
12292
12293  els.setWidth(100); // all elements become 100 width
12294  els.hide(true); // all elements fade out and hide
12295  // or
12296  els.setWidth(100).hide(true);
12297  </code></pre><br><br>
12298  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12299  * actions will be performed on all the elements in this collection.</b>
12300  */
12301 Roo.CompositeElementLite = function(els){
12302     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12303     this.el = new Roo.Element.Flyweight();
12304 };
12305 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12306     addElements : function(els){
12307         if(els){
12308             if(els instanceof Array){
12309                 this.elements = this.elements.concat(els);
12310             }else{
12311                 var yels = this.elements;
12312                 var index = yels.length-1;
12313                 for(var i = 0, len = els.length; i < len; i++) {
12314                     yels[++index] = els[i];
12315                 }
12316             }
12317         }
12318         return this;
12319     },
12320     invoke : function(fn, args){
12321         var els = this.elements;
12322         var el = this.el;
12323         for(var i = 0, len = els.length; i < len; i++) {
12324             el.dom = els[i];
12325                 Roo.Element.prototype[fn].apply(el, args);
12326         }
12327         return this;
12328     },
12329     /**
12330      * Returns a flyweight Element of the dom element object at the specified index
12331      * @param {Number} index
12332      * @return {Roo.Element}
12333      */
12334     item : function(index){
12335         if(!this.elements[index]){
12336             return null;
12337         }
12338         this.el.dom = this.elements[index];
12339         return this.el;
12340     },
12341
12342     // fixes scope with flyweight
12343     addListener : function(eventName, handler, scope, opt){
12344         var els = this.elements;
12345         for(var i = 0, len = els.length; i < len; i++) {
12346             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12347         }
12348         return this;
12349     },
12350
12351     /**
12352     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12353     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12354     * a reference to the dom node, use el.dom.</b>
12355     * @param {Function} fn The function to call
12356     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12357     * @return {CompositeElement} this
12358     */
12359     each : function(fn, scope){
12360         var els = this.elements;
12361         var el = this.el;
12362         for(var i = 0, len = els.length; i < len; i++){
12363             el.dom = els[i];
12364                 if(fn.call(scope || el, el, this, i) === false){
12365                 break;
12366             }
12367         }
12368         return this;
12369     },
12370
12371     indexOf : function(el){
12372         return this.elements.indexOf(Roo.getDom(el));
12373     },
12374
12375     replaceElement : function(el, replacement, domReplace){
12376         var index = typeof el == 'number' ? el : this.indexOf(el);
12377         if(index !== -1){
12378             replacement = Roo.getDom(replacement);
12379             if(domReplace){
12380                 var d = this.elements[index];
12381                 d.parentNode.insertBefore(replacement, d);
12382                 d.parentNode.removeChild(d);
12383             }
12384             this.elements.splice(index, 1, replacement);
12385         }
12386         return this;
12387     }
12388 });
12389 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12390
12391 /*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401
12402  
12403
12404 /**
12405  * @class Roo.data.Connection
12406  * @extends Roo.util.Observable
12407  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12408  * either to a configured URL, or to a URL specified at request time. 
12409  * 
12410  * Requests made by this class are asynchronous, and will return immediately. No data from
12411  * the server will be available to the statement immediately following the {@link #request} call.
12412  * To process returned data, use a callback in the request options object, or an event listener.
12413  * 
12414  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12415  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12416  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12417  * property and, if present, the IFRAME's XML document as the responseXML property.
12418  * 
12419  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12420  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12421  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12422  * standard DOM methods.
12423  * @constructor
12424  * @param {Object} config a configuration object.
12425  */
12426 Roo.data.Connection = function(config){
12427     Roo.apply(this, config);
12428     this.addEvents({
12429         /**
12430          * @event beforerequest
12431          * Fires before a network request is made to retrieve a data object.
12432          * @param {Connection} conn This Connection object.
12433          * @param {Object} options The options config object passed to the {@link #request} method.
12434          */
12435         "beforerequest" : true,
12436         /**
12437          * @event requestcomplete
12438          * Fires if the request was successfully completed.
12439          * @param {Connection} conn This Connection object.
12440          * @param {Object} response The XHR object containing the response data.
12441          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12442          * @param {Object} options The options config object passed to the {@link #request} method.
12443          */
12444         "requestcomplete" : true,
12445         /**
12446          * @event requestexception
12447          * Fires if an error HTTP status was returned from the server.
12448          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12449          * @param {Connection} conn This Connection object.
12450          * @param {Object} response The XHR object containing the response data.
12451          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "requestexception" : true
12455     });
12456     Roo.data.Connection.superclass.constructor.call(this);
12457 };
12458
12459 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12460     /**
12461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12462      */
12463     /**
12464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12465      * extra parameters to each request made by this object. (defaults to undefined)
12466      */
12467     /**
12468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12469      *  to each request made by this object. (defaults to undefined)
12470      */
12471     /**
12472      * @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)
12473      */
12474     /**
12475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12476      */
12477     timeout : 30000,
12478     /**
12479      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12480      * @type Boolean
12481      */
12482     autoAbort:false,
12483
12484     /**
12485      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12486      * @type Boolean
12487      */
12488     disableCaching: true,
12489
12490     /**
12491      * Sends an HTTP request to a remote server.
12492      * @param {Object} options An object which may contain the following properties:<ul>
12493      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12494      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12495      * request, a url encoded string or a function to call to get either.</li>
12496      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12497      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12498      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12499      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12500      * <li>options {Object} The parameter to the request call.</li>
12501      * <li>success {Boolean} True if the request succeeded.</li>
12502      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12503      * </ul></li>
12504      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12505      * The callback is passed the following parameters:<ul>
12506      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12507      * <li>options {Object} The parameter to the request call.</li>
12508      * </ul></li>
12509      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12510      * The callback is passed the following parameters:<ul>
12511      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12512      * <li>options {Object} The parameter to the request call.</li>
12513      * </ul></li>
12514      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12515      * for the callback function. Defaults to the browser window.</li>
12516      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12517      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12518      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12519      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12520      * params for the post data. Any params will be appended to the URL.</li>
12521      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12522      * </ul>
12523      * @return {Number} transactionId
12524      */
12525     request : function(o){
12526         if(this.fireEvent("beforerequest", this, o) !== false){
12527             var p = o.params;
12528
12529             if(typeof p == "function"){
12530                 p = p.call(o.scope||window, o);
12531             }
12532             if(typeof p == "object"){
12533                 p = Roo.urlEncode(o.params);
12534             }
12535             if(this.extraParams){
12536                 var extras = Roo.urlEncode(this.extraParams);
12537                 p = p ? (p + '&' + extras) : extras;
12538             }
12539
12540             var url = o.url || this.url;
12541             if(typeof url == 'function'){
12542                 url = url.call(o.scope||window, o);
12543             }
12544
12545             if(o.form){
12546                 var form = Roo.getDom(o.form);
12547                 url = url || form.action;
12548
12549                 var enctype = form.getAttribute("enctype");
12550                 
12551                 if (o.formData) {
12552                     return this.doFormDataUpload(o, url);
12553                 }
12554                 
12555                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12556                     return this.doFormUpload(o, p, url);
12557                 }
12558                 var f = Roo.lib.Ajax.serializeForm(form);
12559                 p = p ? (p + '&' + f) : f;
12560             }
12561             
12562             if (!o.form && o.formData) {
12563                 o.formData = o.formData === true ? new FormData() : o.formData;
12564                 for (var k in o.params) {
12565                     o.formData.append(k,o.params[k]);
12566                 }
12567                     
12568                 return this.doFormDataUpload(o, url);
12569             }
12570             
12571
12572             var hs = o.headers;
12573             if(this.defaultHeaders){
12574                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12575                 if(!o.headers){
12576                     o.headers = hs;
12577                 }
12578             }
12579
12580             var cb = {
12581                 success: this.handleResponse,
12582                 failure: this.handleFailure,
12583                 scope: this,
12584                 argument: {options: o},
12585                 timeout : o.timeout || this.timeout
12586             };
12587
12588             var method = o.method||this.method||(p ? "POST" : "GET");
12589
12590             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12591                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12592             }
12593
12594             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12595                 if(o.autoAbort){
12596                     this.abort();
12597                 }
12598             }else if(this.autoAbort !== false){
12599                 this.abort();
12600             }
12601
12602             if((method == 'GET' && p) || o.xmlData){
12603                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12604                 p = '';
12605             }
12606             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12607             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12608             Roo.lib.Ajax.useDefaultHeader == true;
12609             return this.transId;
12610         }else{
12611             Roo.callback(o.callback, o.scope, [o, null, null]);
12612             return null;
12613         }
12614     },
12615
12616     /**
12617      * Determine whether this object has a request outstanding.
12618      * @param {Number} transactionId (Optional) defaults to the last transaction
12619      * @return {Boolean} True if there is an outstanding request.
12620      */
12621     isLoading : function(transId){
12622         if(transId){
12623             return Roo.lib.Ajax.isCallInProgress(transId);
12624         }else{
12625             return this.transId ? true : false;
12626         }
12627     },
12628
12629     /**
12630      * Aborts any outstanding request.
12631      * @param {Number} transactionId (Optional) defaults to the last transaction
12632      */
12633     abort : function(transId){
12634         if(transId || this.isLoading()){
12635             Roo.lib.Ajax.abort(transId || this.transId);
12636         }
12637     },
12638
12639     // private
12640     handleResponse : function(response){
12641         this.transId = false;
12642         var options = response.argument.options;
12643         response.argument = options ? options.argument : null;
12644         this.fireEvent("requestcomplete", this, response, options);
12645         Roo.callback(options.success, options.scope, [response, options]);
12646         Roo.callback(options.callback, options.scope, [options, true, response]);
12647     },
12648
12649     // private
12650     handleFailure : function(response, e){
12651         this.transId = false;
12652         var options = response.argument.options;
12653         response.argument = options ? options.argument : null;
12654         this.fireEvent("requestexception", this, response, options, e);
12655         Roo.callback(options.failure, options.scope, [response, options]);
12656         Roo.callback(options.callback, options.scope, [options, false, response]);
12657     },
12658
12659     // private
12660     doFormUpload : function(o, ps, url){
12661         var id = Roo.id();
12662         var frame = document.createElement('iframe');
12663         frame.id = id;
12664         frame.name = id;
12665         frame.className = 'x-hidden';
12666         if(Roo.isIE){
12667             frame.src = Roo.SSL_SECURE_URL;
12668         }
12669         document.body.appendChild(frame);
12670
12671         if(Roo.isIE){
12672            document.frames[id].name = id;
12673         }
12674
12675         var form = Roo.getDom(o.form);
12676         form.target = id;
12677         form.method = 'POST';
12678         form.enctype = form.encoding = 'multipart/form-data';
12679         if(url){
12680             form.action = url;
12681         }
12682
12683         var hiddens, hd;
12684         if(ps){ // add dynamic params
12685             hiddens = [];
12686             ps = Roo.urlDecode(ps, false);
12687             for(var k in ps){
12688                 if(ps.hasOwnProperty(k)){
12689                     hd = document.createElement('input');
12690                     hd.type = 'hidden';
12691                     hd.name = k;
12692                     hd.value = ps[k];
12693                     form.appendChild(hd);
12694                     hiddens.push(hd);
12695                 }
12696             }
12697         }
12698
12699         function cb(){
12700             var r = {  // bogus response object
12701                 responseText : '',
12702                 responseXML : null
12703             };
12704
12705             r.argument = o ? o.argument : null;
12706
12707             try { //
12708                 var doc;
12709                 if(Roo.isIE){
12710                     doc = frame.contentWindow.document;
12711                 }else {
12712                     doc = (frame.contentDocument || window.frames[id].document);
12713                 }
12714                 if(doc && doc.body){
12715                     r.responseText = doc.body.innerHTML;
12716                 }
12717                 if(doc && doc.XMLDocument){
12718                     r.responseXML = doc.XMLDocument;
12719                 }else {
12720                     r.responseXML = doc;
12721                 }
12722             }
12723             catch(e) {
12724                 // ignore
12725             }
12726
12727             Roo.EventManager.removeListener(frame, 'load', cb, this);
12728
12729             this.fireEvent("requestcomplete", this, r, o);
12730             Roo.callback(o.success, o.scope, [r, o]);
12731             Roo.callback(o.callback, o.scope, [o, true, r]);
12732
12733             setTimeout(function(){document.body.removeChild(frame);}, 100);
12734         }
12735
12736         Roo.EventManager.on(frame, 'load', cb, this);
12737         form.submit();
12738
12739         if(hiddens){ // remove dynamic params
12740             for(var i = 0, len = hiddens.length; i < len; i++){
12741                 form.removeChild(hiddens[i]);
12742             }
12743         }
12744     },
12745     // this is a 'formdata version???'
12746     
12747     
12748     doFormDataUpload : function(o,  url)
12749     {
12750         var formData;
12751         if (o.form) {
12752             var form =  Roo.getDom(o.form);
12753             form.enctype = form.encoding = 'multipart/form-data';
12754             formData = o.formData === true ? new FormData(form) : o.formData;
12755         } else {
12756             formData = o.formData === true ? new FormData() : o.formData;
12757         }
12758         
12759       
12760         var cb = {
12761             success: this.handleResponse,
12762             failure: this.handleFailure,
12763             scope: this,
12764             argument: {options: o},
12765             timeout : o.timeout || this.timeout
12766         };
12767  
12768         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12769             if(o.autoAbort){
12770                 this.abort();
12771             }
12772         }else if(this.autoAbort !== false){
12773             this.abort();
12774         }
12775
12776         //Roo.lib.Ajax.defaultPostHeader = null;
12777         Roo.lib.Ajax.useDefaultHeader = false;
12778         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12779         Roo.lib.Ajax.useDefaultHeader = true;
12780  
12781          
12782     }
12783     
12784 });
12785 /*
12786  * Based on:
12787  * Ext JS Library 1.1.1
12788  * Copyright(c) 2006-2007, Ext JS, LLC.
12789  *
12790  * Originally Released Under LGPL - original licence link has changed is not relivant.
12791  *
12792  * Fork - LGPL
12793  * <script type="text/javascript">
12794  */
12795  
12796 /**
12797  * Global Ajax request class.
12798  * 
12799  * @class Roo.Ajax
12800  * @extends Roo.data.Connection
12801  * @static
12802  * 
12803  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12804  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12805  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12806  * @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)
12807  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12808  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12809  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12810  */
12811 Roo.Ajax = new Roo.data.Connection({
12812     // fix up the docs
12813     /**
12814      * @scope Roo.Ajax
12815      * @type {Boolear} 
12816      */
12817     autoAbort : false,
12818
12819     /**
12820      * Serialize the passed form into a url encoded string
12821      * @scope Roo.Ajax
12822      * @param {String/HTMLElement} form
12823      * @return {String}
12824      */
12825     serializeForm : function(form){
12826         return Roo.lib.Ajax.serializeForm(form);
12827     }
12828 });/*
12829  * Based on:
12830  * Ext JS Library 1.1.1
12831  * Copyright(c) 2006-2007, Ext JS, LLC.
12832  *
12833  * Originally Released Under LGPL - original licence link has changed is not relivant.
12834  *
12835  * Fork - LGPL
12836  * <script type="text/javascript">
12837  */
12838
12839  
12840 /**
12841  * @class Roo.UpdateManager
12842  * @extends Roo.util.Observable
12843  * Provides AJAX-style update for Element object.<br><br>
12844  * Usage:<br>
12845  * <pre><code>
12846  * // Get it from a Roo.Element object
12847  * var el = Roo.get("foo");
12848  * var mgr = el.getUpdateManager();
12849  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12850  * ...
12851  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12852  * <br>
12853  * // or directly (returns the same UpdateManager instance)
12854  * var mgr = new Roo.UpdateManager("myElementId");
12855  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12856  * mgr.on("update", myFcnNeedsToKnow);
12857  * <br>
12858    // short handed call directly from the element object
12859    Roo.get("foo").load({
12860         url: "bar.php",
12861         scripts:true,
12862         params: "for=bar",
12863         text: "Loading Foo..."
12864    });
12865  * </code></pre>
12866  * @constructor
12867  * Create new UpdateManager directly.
12868  * @param {String/HTMLElement/Roo.Element} el The element to update
12869  * @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).
12870  */
12871 Roo.UpdateManager = function(el, forceNew){
12872     el = Roo.get(el);
12873     if(!forceNew && el.updateManager){
12874         return el.updateManager;
12875     }
12876     /**
12877      * The Element object
12878      * @type Roo.Element
12879      */
12880     this.el = el;
12881     /**
12882      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12883      * @type String
12884      */
12885     this.defaultUrl = null;
12886
12887     this.addEvents({
12888         /**
12889          * @event beforeupdate
12890          * Fired before an update is made, return false from your handler and the update is cancelled.
12891          * @param {Roo.Element} el
12892          * @param {String/Object/Function} url
12893          * @param {String/Object} params
12894          */
12895         "beforeupdate": true,
12896         /**
12897          * @event update
12898          * Fired after successful update is made.
12899          * @param {Roo.Element} el
12900          * @param {Object} oResponseObject The response Object
12901          */
12902         "update": true,
12903         /**
12904          * @event failure
12905          * Fired on update failure.
12906          * @param {Roo.Element} el
12907          * @param {Object} oResponseObject The response Object
12908          */
12909         "failure": true
12910     });
12911     var d = Roo.UpdateManager.defaults;
12912     /**
12913      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12914      * @type String
12915      */
12916     this.sslBlankUrl = d.sslBlankUrl;
12917     /**
12918      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12919      * @type Boolean
12920      */
12921     this.disableCaching = d.disableCaching;
12922     /**
12923      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12924      * @type String
12925      */
12926     this.indicatorText = d.indicatorText;
12927     /**
12928      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12929      * @type String
12930      */
12931     this.showLoadIndicator = d.showLoadIndicator;
12932     /**
12933      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12934      * @type Number
12935      */
12936     this.timeout = d.timeout;
12937
12938     /**
12939      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12940      * @type Boolean
12941      */
12942     this.loadScripts = d.loadScripts;
12943
12944     /**
12945      * Transaction object of current executing transaction
12946      */
12947     this.transaction = null;
12948
12949     /**
12950      * @private
12951      */
12952     this.autoRefreshProcId = null;
12953     /**
12954      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12955      * @type Function
12956      */
12957     this.refreshDelegate = this.refresh.createDelegate(this);
12958     /**
12959      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12960      * @type Function
12961      */
12962     this.updateDelegate = this.update.createDelegate(this);
12963     /**
12964      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12965      * @type Function
12966      */
12967     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12968     /**
12969      * @private
12970      */
12971     this.successDelegate = this.processSuccess.createDelegate(this);
12972     /**
12973      * @private
12974      */
12975     this.failureDelegate = this.processFailure.createDelegate(this);
12976
12977     if(!this.renderer){
12978      /**
12979       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12980       */
12981     this.renderer = new Roo.UpdateManager.BasicRenderer();
12982     }
12983     
12984     Roo.UpdateManager.superclass.constructor.call(this);
12985 };
12986
12987 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12988     /**
12989      * Get the Element this UpdateManager is bound to
12990      * @return {Roo.Element} The element
12991      */
12992     getEl : function(){
12993         return this.el;
12994     },
12995     /**
12996      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12997      * @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:
12998 <pre><code>
12999 um.update({<br/>
13000     url: "your-url.php",<br/>
13001     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13002     callback: yourFunction,<br/>
13003     scope: yourObject, //(optional scope)  <br/>
13004     discardUrl: false, <br/>
13005     nocache: false,<br/>
13006     text: "Loading...",<br/>
13007     timeout: 30,<br/>
13008     scripts: false<br/>
13009 });
13010 </code></pre>
13011      * The only required property is url. The optional properties nocache, text and scripts
13012      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13013      * @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}
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13015      * @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.
13016      */
13017     update : function(url, params, callback, discardUrl){
13018         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13019             var method = this.method,
13020                 cfg;
13021             if(typeof url == "object"){ // must be config object
13022                 cfg = url;
13023                 url = cfg.url;
13024                 params = params || cfg.params;
13025                 callback = callback || cfg.callback;
13026                 discardUrl = discardUrl || cfg.discardUrl;
13027                 if(callback && cfg.scope){
13028                     callback = callback.createDelegate(cfg.scope);
13029                 }
13030                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13031                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13032                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13033                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13034                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13035             }
13036             this.showLoading();
13037             if(!discardUrl){
13038                 this.defaultUrl = url;
13039             }
13040             if(typeof url == "function"){
13041                 url = url.call(this);
13042             }
13043
13044             method = method || (params ? "POST" : "GET");
13045             if(method == "GET"){
13046                 url = this.prepareUrl(url);
13047             }
13048
13049             var o = Roo.apply(cfg ||{}, {
13050                 url : url,
13051                 params: params,
13052                 success: this.successDelegate,
13053                 failure: this.failureDelegate,
13054                 callback: undefined,
13055                 timeout: (this.timeout*1000),
13056                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13057             });
13058             Roo.log("updated manager called with timeout of " + o.timeout);
13059             this.transaction = Roo.Ajax.request(o);
13060         }
13061     },
13062
13063     /**
13064      * 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.
13065      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13066      * @param {String/HTMLElement} form The form Id or form element
13067      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13068      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13069      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13070      */
13071     formUpdate : function(form, url, reset, callback){
13072         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13073             if(typeof url == "function"){
13074                 url = url.call(this);
13075             }
13076             form = Roo.getDom(form);
13077             this.transaction = Roo.Ajax.request({
13078                 form: form,
13079                 url:url,
13080                 success: this.successDelegate,
13081                 failure: this.failureDelegate,
13082                 timeout: (this.timeout*1000),
13083                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13084             });
13085             this.showLoading.defer(1, this);
13086         }
13087     },
13088
13089     /**
13090      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13091      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13092      */
13093     refresh : function(callback){
13094         if(this.defaultUrl == null){
13095             return;
13096         }
13097         this.update(this.defaultUrl, null, callback, true);
13098     },
13099
13100     /**
13101      * Set this element to auto refresh.
13102      * @param {Number} interval How often to update (in seconds).
13103      * @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)
13104      * @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}
13105      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13106      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13107      */
13108     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13109         if(refreshNow){
13110             this.update(url || this.defaultUrl, params, callback, true);
13111         }
13112         if(this.autoRefreshProcId){
13113             clearInterval(this.autoRefreshProcId);
13114         }
13115         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13116     },
13117
13118     /**
13119      * Stop auto refresh on this element.
13120      */
13121      stopAutoRefresh : function(){
13122         if(this.autoRefreshProcId){
13123             clearInterval(this.autoRefreshProcId);
13124             delete this.autoRefreshProcId;
13125         }
13126     },
13127
13128     isAutoRefreshing : function(){
13129        return this.autoRefreshProcId ? true : false;
13130     },
13131     /**
13132      * Called to update the element to "Loading" state. Override to perform custom action.
13133      */
13134     showLoading : function(){
13135         if(this.showLoadIndicator){
13136             this.el.update(this.indicatorText);
13137         }
13138     },
13139
13140     /**
13141      * Adds unique parameter to query string if disableCaching = true
13142      * @private
13143      */
13144     prepareUrl : function(url){
13145         if(this.disableCaching){
13146             var append = "_dc=" + (new Date().getTime());
13147             if(url.indexOf("?") !== -1){
13148                 url += "&" + append;
13149             }else{
13150                 url += "?" + append;
13151             }
13152         }
13153         return url;
13154     },
13155
13156     /**
13157      * @private
13158      */
13159     processSuccess : function(response){
13160         this.transaction = null;
13161         if(response.argument.form && response.argument.reset){
13162             try{ // put in try/catch since some older FF releases had problems with this
13163                 response.argument.form.reset();
13164             }catch(e){}
13165         }
13166         if(this.loadScripts){
13167             this.renderer.render(this.el, response, this,
13168                 this.updateComplete.createDelegate(this, [response]));
13169         }else{
13170             this.renderer.render(this.el, response, this);
13171             this.updateComplete(response);
13172         }
13173     },
13174
13175     updateComplete : function(response){
13176         this.fireEvent("update", this.el, response);
13177         if(typeof response.argument.callback == "function"){
13178             response.argument.callback(this.el, true, response);
13179         }
13180     },
13181
13182     /**
13183      * @private
13184      */
13185     processFailure : function(response){
13186         this.transaction = null;
13187         this.fireEvent("failure", this.el, response);
13188         if(typeof response.argument.callback == "function"){
13189             response.argument.callback(this.el, false, response);
13190         }
13191     },
13192
13193     /**
13194      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13195      * @param {Object} renderer The object implementing the render() method
13196      */
13197     setRenderer : function(renderer){
13198         this.renderer = renderer;
13199     },
13200
13201     getRenderer : function(){
13202        return this.renderer;
13203     },
13204
13205     /**
13206      * Set the defaultUrl used for updates
13207      * @param {String/Function} defaultUrl The url or a function to call to get the url
13208      */
13209     setDefaultUrl : function(defaultUrl){
13210         this.defaultUrl = defaultUrl;
13211     },
13212
13213     /**
13214      * Aborts the executing transaction
13215      */
13216     abort : function(){
13217         if(this.transaction){
13218             Roo.Ajax.abort(this.transaction);
13219         }
13220     },
13221
13222     /**
13223      * Returns true if an update is in progress
13224      * @return {Boolean}
13225      */
13226     isUpdating : function(){
13227         if(this.transaction){
13228             return Roo.Ajax.isLoading(this.transaction);
13229         }
13230         return false;
13231     }
13232 });
13233
13234 /**
13235  * @class Roo.UpdateManager.defaults
13236  * @static (not really - but it helps the doc tool)
13237  * The defaults collection enables customizing the default properties of UpdateManager
13238  */
13239    Roo.UpdateManager.defaults = {
13240        /**
13241          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13242          * @type Number
13243          */
13244          timeout : 30,
13245
13246          /**
13247          * True to process scripts by default (Defaults to false).
13248          * @type Boolean
13249          */
13250         loadScripts : false,
13251
13252         /**
13253         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13254         * @type String
13255         */
13256         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13257         /**
13258          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13259          * @type Boolean
13260          */
13261         disableCaching : false,
13262         /**
13263          * Whether to show indicatorText when loading (Defaults to true).
13264          * @type Boolean
13265          */
13266         showLoadIndicator : true,
13267         /**
13268          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13269          * @type String
13270          */
13271         indicatorText : '<div class="loading-indicator">Loading...</div>'
13272    };
13273
13274 /**
13275  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13276  *Usage:
13277  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13278  * @param {String/HTMLElement/Roo.Element} el The element to update
13279  * @param {String} url The url
13280  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13281  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13282  * @static
13283  * @deprecated
13284  * @member Roo.UpdateManager
13285  */
13286 Roo.UpdateManager.updateElement = function(el, url, params, options){
13287     var um = Roo.get(el, true).getUpdateManager();
13288     Roo.apply(um, options);
13289     um.update(url, params, options ? options.callback : null);
13290 };
13291 // alias for backwards compat
13292 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13293 /**
13294  * @class Roo.UpdateManager.BasicRenderer
13295  * Default Content renderer. Updates the elements innerHTML with the responseText.
13296  */
13297 Roo.UpdateManager.BasicRenderer = function(){};
13298
13299 Roo.UpdateManager.BasicRenderer.prototype = {
13300     /**
13301      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13302      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13303      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13304      * @param {Roo.Element} el The element being rendered
13305      * @param {Object} response The YUI Connect response object
13306      * @param {UpdateManager} updateManager The calling update manager
13307      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13308      */
13309      render : function(el, response, updateManager, callback){
13310         el.update(response.responseText, updateManager.loadScripts, callback);
13311     }
13312 };
13313 /*
13314  * Based on:
13315  * Roo JS
13316  * (c)) Alan Knowles
13317  * Licence : LGPL
13318  */
13319
13320
13321 /**
13322  * @class Roo.DomTemplate
13323  * @extends Roo.Template
13324  * An effort at a dom based template engine..
13325  *
13326  * Similar to XTemplate, except it uses dom parsing to create the template..
13327  *
13328  * Supported features:
13329  *
13330  *  Tags:
13331
13332 <pre><code>
13333       {a_variable} - output encoded.
13334       {a_variable.format:("Y-m-d")} - call a method on the variable
13335       {a_variable:raw} - unencoded output
13336       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13337       {a_variable:this.method_on_template(...)} - call a method on the template object.
13338  
13339 </code></pre>
13340  *  The tpl tag:
13341 <pre><code>
13342         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13343         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13344         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13345         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13346   
13347 </code></pre>
13348  *      
13349  */
13350 Roo.DomTemplate = function()
13351 {
13352      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13353      if (this.html) {
13354         this.compile();
13355      }
13356 };
13357
13358
13359 Roo.extend(Roo.DomTemplate, Roo.Template, {
13360     /**
13361      * id counter for sub templates.
13362      */
13363     id : 0,
13364     /**
13365      * flag to indicate if dom parser is inside a pre,
13366      * it will strip whitespace if not.
13367      */
13368     inPre : false,
13369     
13370     /**
13371      * The various sub templates
13372      */
13373     tpls : false,
13374     
13375     
13376     
13377     /**
13378      *
13379      * basic tag replacing syntax
13380      * WORD:WORD()
13381      *
13382      * // you can fake an object call by doing this
13383      *  x.t:(test,tesT) 
13384      * 
13385      */
13386     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13387     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13388     
13389     iterChild : function (node, method) {
13390         
13391         var oldPre = this.inPre;
13392         if (node.tagName == 'PRE') {
13393             this.inPre = true;
13394         }
13395         for( var i = 0; i < node.childNodes.length; i++) {
13396             method.call(this, node.childNodes[i]);
13397         }
13398         this.inPre = oldPre;
13399     },
13400     
13401     
13402     
13403     /**
13404      * compile the template
13405      *
13406      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13407      *
13408      */
13409     compile: function()
13410     {
13411         var s = this.html;
13412         
13413         // covert the html into DOM...
13414         var doc = false;
13415         var div =false;
13416         try {
13417             doc = document.implementation.createHTMLDocument("");
13418             doc.documentElement.innerHTML =   this.html  ;
13419             div = doc.documentElement;
13420         } catch (e) {
13421             // old IE... - nasty -- it causes all sorts of issues.. with
13422             // images getting pulled from server..
13423             div = document.createElement('div');
13424             div.innerHTML = this.html;
13425         }
13426         //doc.documentElement.innerHTML = htmlBody
13427          
13428         
13429         
13430         this.tpls = [];
13431         var _t = this;
13432         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13433         
13434         var tpls = this.tpls;
13435         
13436         // create a top level template from the snippet..
13437         
13438         //Roo.log(div.innerHTML);
13439         
13440         var tpl = {
13441             uid : 'master',
13442             id : this.id++,
13443             attr : false,
13444             value : false,
13445             body : div.innerHTML,
13446             
13447             forCall : false,
13448             execCall : false,
13449             dom : div,
13450             isTop : true
13451             
13452         };
13453         tpls.unshift(tpl);
13454         
13455         
13456         // compile them...
13457         this.tpls = [];
13458         Roo.each(tpls, function(tp){
13459             this.compileTpl(tp);
13460             this.tpls[tp.id] = tp;
13461         }, this);
13462         
13463         this.master = tpls[0];
13464         return this;
13465         
13466         
13467     },
13468     
13469     compileNode : function(node, istop) {
13470         // test for
13471         //Roo.log(node);
13472         
13473         
13474         // skip anything not a tag..
13475         if (node.nodeType != 1) {
13476             if (node.nodeType == 3 && !this.inPre) {
13477                 // reduce white space..
13478                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13479                 
13480             }
13481             return;
13482         }
13483         
13484         var tpl = {
13485             uid : false,
13486             id : false,
13487             attr : false,
13488             value : false,
13489             body : '',
13490             
13491             forCall : false,
13492             execCall : false,
13493             dom : false,
13494             isTop : istop
13495             
13496             
13497         };
13498         
13499         
13500         switch(true) {
13501             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13502             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13503             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13504             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13505             // no default..
13506         }
13507         
13508         
13509         if (!tpl.attr) {
13510             // just itterate children..
13511             this.iterChild(node,this.compileNode);
13512             return;
13513         }
13514         tpl.uid = this.id++;
13515         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13516         node.removeAttribute('roo-'+ tpl.attr);
13517         if (tpl.attr != 'name') {
13518             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13519             node.parentNode.replaceChild(placeholder,  node);
13520         } else {
13521             
13522             var placeholder =  document.createElement('span');
13523             placeholder.className = 'roo-tpl-' + tpl.value;
13524             node.parentNode.replaceChild(placeholder,  node);
13525         }
13526         
13527         // parent now sees '{domtplXXXX}
13528         this.iterChild(node,this.compileNode);
13529         
13530         // we should now have node body...
13531         var div = document.createElement('div');
13532         div.appendChild(node);
13533         tpl.dom = node;
13534         // this has the unfortunate side effect of converting tagged attributes
13535         // eg. href="{...}" into %7C...%7D
13536         // this has been fixed by searching for those combo's although it's a bit hacky..
13537         
13538         
13539         tpl.body = div.innerHTML;
13540         
13541         
13542          
13543         tpl.id = tpl.uid;
13544         switch(tpl.attr) {
13545             case 'for' :
13546                 switch (tpl.value) {
13547                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13548                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13549                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13550                 }
13551                 break;
13552             
13553             case 'exec':
13554                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13555                 break;
13556             
13557             case 'if':     
13558                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13559                 break;
13560             
13561             case 'name':
13562                 tpl.id  = tpl.value; // replace non characters???
13563                 break;
13564             
13565         }
13566         
13567         
13568         this.tpls.push(tpl);
13569         
13570         
13571         
13572     },
13573     
13574     
13575     
13576     
13577     /**
13578      * Compile a segment of the template into a 'sub-template'
13579      *
13580      * 
13581      * 
13582      *
13583      */
13584     compileTpl : function(tpl)
13585     {
13586         var fm = Roo.util.Format;
13587         var useF = this.disableFormats !== true;
13588         
13589         var sep = Roo.isGecko ? "+\n" : ",\n";
13590         
13591         var undef = function(str) {
13592             Roo.debug && Roo.log("Property not found :"  + str);
13593             return '';
13594         };
13595           
13596         //Roo.log(tpl.body);
13597         
13598         
13599         
13600         var fn = function(m, lbrace, name, format, args)
13601         {
13602             //Roo.log("ARGS");
13603             //Roo.log(arguments);
13604             args = args ? args.replace(/\\'/g,"'") : args;
13605             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13606             if (typeof(format) == 'undefined') {
13607                 format =  'htmlEncode'; 
13608             }
13609             if (format == 'raw' ) {
13610                 format = false;
13611             }
13612             
13613             if(name.substr(0, 6) == 'domtpl'){
13614                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13615             }
13616             
13617             // build an array of options to determine if value is undefined..
13618             
13619             // basically get 'xxxx.yyyy' then do
13620             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13621             //    (function () { Roo.log("Property not found"); return ''; })() :
13622             //    ......
13623             
13624             var udef_ar = [];
13625             var lookfor = '';
13626             Roo.each(name.split('.'), function(st) {
13627                 lookfor += (lookfor.length ? '.': '') + st;
13628                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13629             });
13630             
13631             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13632             
13633             
13634             if(format && useF){
13635                 
13636                 args = args ? ',' + args : "";
13637                  
13638                 if(format.substr(0, 5) != "this."){
13639                     format = "fm." + format + '(';
13640                 }else{
13641                     format = 'this.call("'+ format.substr(5) + '", ';
13642                     args = ", values";
13643                 }
13644                 
13645                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13646             }
13647              
13648             if (args && args.length) {
13649                 // called with xxyx.yuu:(test,test)
13650                 // change to ()
13651                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13652             }
13653             // raw.. - :raw modifier..
13654             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13655             
13656         };
13657         var body;
13658         // branched to use + in gecko and [].join() in others
13659         if(Roo.isGecko){
13660             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13661                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13662                     "';};};";
13663         }else{
13664             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13665             body.push(tpl.body.replace(/(\r\n|\n)/g,
13666                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13667             body.push("'].join('');};};");
13668             body = body.join('');
13669         }
13670         
13671         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13672        
13673         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13674         eval(body);
13675         
13676         return this;
13677     },
13678      
13679     /**
13680      * same as applyTemplate, except it's done to one of the subTemplates
13681      * when using named templates, you can do:
13682      *
13683      * var str = pl.applySubTemplate('your-name', values);
13684      *
13685      * 
13686      * @param {Number} id of the template
13687      * @param {Object} values to apply to template
13688      * @param {Object} parent (normaly the instance of this object)
13689      */
13690     applySubTemplate : function(id, values, parent)
13691     {
13692         
13693         
13694         var t = this.tpls[id];
13695         
13696         
13697         try { 
13698             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13699                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13700                 return '';
13701             }
13702         } catch(e) {
13703             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13704             Roo.log(values);
13705           
13706             return '';
13707         }
13708         try { 
13709             
13710             if(t.execCall && t.execCall.call(this, values, parent)){
13711                 return '';
13712             }
13713         } catch(e) {
13714             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13715             Roo.log(values);
13716             return '';
13717         }
13718         
13719         try {
13720             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13721             parent = t.target ? values : parent;
13722             if(t.forCall && vs instanceof Array){
13723                 var buf = [];
13724                 for(var i = 0, len = vs.length; i < len; i++){
13725                     try {
13726                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13727                     } catch (e) {
13728                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13729                         Roo.log(e.body);
13730                         //Roo.log(t.compiled);
13731                         Roo.log(vs[i]);
13732                     }   
13733                 }
13734                 return buf.join('');
13735             }
13736         } catch (e) {
13737             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13738             Roo.log(values);
13739             return '';
13740         }
13741         try {
13742             return t.compiled.call(this, vs, parent);
13743         } catch (e) {
13744             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13745             Roo.log(e.body);
13746             //Roo.log(t.compiled);
13747             Roo.log(values);
13748             return '';
13749         }
13750     },
13751
13752    
13753
13754     applyTemplate : function(values){
13755         return this.master.compiled.call(this, values, {});
13756         //var s = this.subs;
13757     },
13758
13759     apply : function(){
13760         return this.applyTemplate.apply(this, arguments);
13761     }
13762
13763  });
13764
13765 Roo.DomTemplate.from = function(el){
13766     el = Roo.getDom(el);
13767     return new Roo.Domtemplate(el.value || el.innerHTML);
13768 };/*
13769  * Based on:
13770  * Ext JS Library 1.1.1
13771  * Copyright(c) 2006-2007, Ext JS, LLC.
13772  *
13773  * Originally Released Under LGPL - original licence link has changed is not relivant.
13774  *
13775  * Fork - LGPL
13776  * <script type="text/javascript">
13777  */
13778
13779 /**
13780  * @class Roo.util.DelayedTask
13781  * Provides a convenient method of performing setTimeout where a new
13782  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13783  * You can use this class to buffer
13784  * the keypress events for a certain number of milliseconds, and perform only if they stop
13785  * for that amount of time.
13786  * @constructor The parameters to this constructor serve as defaults and are not required.
13787  * @param {Function} fn (optional) The default function to timeout
13788  * @param {Object} scope (optional) The default scope of that timeout
13789  * @param {Array} args (optional) The default Array of arguments
13790  */
13791 Roo.util.DelayedTask = function(fn, scope, args){
13792     var id = null, d, t;
13793
13794     var call = function(){
13795         var now = new Date().getTime();
13796         if(now - t >= d){
13797             clearInterval(id);
13798             id = null;
13799             fn.apply(scope, args || []);
13800         }
13801     };
13802     /**
13803      * Cancels any pending timeout and queues a new one
13804      * @param {Number} delay The milliseconds to delay
13805      * @param {Function} newFn (optional) Overrides function passed to constructor
13806      * @param {Object} newScope (optional) Overrides scope passed to constructor
13807      * @param {Array} newArgs (optional) Overrides args passed to constructor
13808      */
13809     this.delay = function(delay, newFn, newScope, newArgs){
13810         if(id && delay != d){
13811             this.cancel();
13812         }
13813         d = delay;
13814         t = new Date().getTime();
13815         fn = newFn || fn;
13816         scope = newScope || scope;
13817         args = newArgs || args;
13818         if(!id){
13819             id = setInterval(call, d);
13820         }
13821     };
13822
13823     /**
13824      * Cancel the last queued timeout
13825      */
13826     this.cancel = function(){
13827         if(id){
13828             clearInterval(id);
13829             id = null;
13830         }
13831     };
13832 };/*
13833  * Based on:
13834  * Ext JS Library 1.1.1
13835  * Copyright(c) 2006-2007, Ext JS, LLC.
13836  *
13837  * Originally Released Under LGPL - original licence link has changed is not relivant.
13838  *
13839  * Fork - LGPL
13840  * <script type="text/javascript">
13841  */
13842 /**
13843  * @class Roo.util.TaskRunner
13844  * Manage background tasks - not sure why this is better that setInterval?
13845  * @static
13846  *
13847  */
13848  
13849 Roo.util.TaskRunner = function(interval){
13850     interval = interval || 10;
13851     var tasks = [], removeQueue = [];
13852     var id = 0;
13853     var running = false;
13854
13855     var stopThread = function(){
13856         running = false;
13857         clearInterval(id);
13858         id = 0;
13859     };
13860
13861     var startThread = function(){
13862         if(!running){
13863             running = true;
13864             id = setInterval(runTasks, interval);
13865         }
13866     };
13867
13868     var removeTask = function(task){
13869         removeQueue.push(task);
13870         if(task.onStop){
13871             task.onStop();
13872         }
13873     };
13874
13875     var runTasks = function(){
13876         if(removeQueue.length > 0){
13877             for(var i = 0, len = removeQueue.length; i < len; i++){
13878                 tasks.remove(removeQueue[i]);
13879             }
13880             removeQueue = [];
13881             if(tasks.length < 1){
13882                 stopThread();
13883                 return;
13884             }
13885         }
13886         var now = new Date().getTime();
13887         for(var i = 0, len = tasks.length; i < len; ++i){
13888             var t = tasks[i];
13889             var itime = now - t.taskRunTime;
13890             if(t.interval <= itime){
13891                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13892                 t.taskRunTime = now;
13893                 if(rt === false || t.taskRunCount === t.repeat){
13894                     removeTask(t);
13895                     return;
13896                 }
13897             }
13898             if(t.duration && t.duration <= (now - t.taskStartTime)){
13899                 removeTask(t);
13900             }
13901         }
13902     };
13903
13904     /**
13905      * Queues a new task.
13906      * @param {Object} task
13907      *
13908      * Task property : interval = how frequent to run.
13909      * Task object should implement
13910      * function run()
13911      * Task object may implement
13912      * function onStop()
13913      */
13914     this.start = function(task){
13915         tasks.push(task);
13916         task.taskStartTime = new Date().getTime();
13917         task.taskRunTime = 0;
13918         task.taskRunCount = 0;
13919         startThread();
13920         return task;
13921     };
13922     /**
13923      * Stop  new task.
13924      * @param {Object} task
13925      */
13926     this.stop = function(task){
13927         removeTask(task);
13928         return task;
13929     };
13930     /**
13931      * Stop all Tasks
13932      */
13933     this.stopAll = function(){
13934         stopThread();
13935         for(var i = 0, len = tasks.length; i < len; i++){
13936             if(tasks[i].onStop){
13937                 tasks[i].onStop();
13938             }
13939         }
13940         tasks = [];
13941         removeQueue = [];
13942     };
13943 };
13944
13945 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956  
13957 /**
13958  * @class Roo.util.MixedCollection
13959  * @extends Roo.util.Observable
13960  * A Collection class that maintains both numeric indexes and keys and exposes events.
13961  * @constructor
13962  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13963  * collection (defaults to false)
13964  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13965  * and return the key value for that item.  This is used when available to look up the key on items that
13966  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13967  * equivalent to providing an implementation for the {@link #getKey} method.
13968  */
13969 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13970     this.items = [];
13971     this.map = {};
13972     this.keys = [];
13973     this.length = 0;
13974     this.addEvents({
13975         /**
13976          * @event clear
13977          * Fires when the collection is cleared.
13978          */
13979         "clear" : true,
13980         /**
13981          * @event add
13982          * Fires when an item is added to the collection.
13983          * @param {Number} index The index at which the item was added.
13984          * @param {Object} o The item added.
13985          * @param {String} key The key associated with the added item.
13986          */
13987         "add" : true,
13988         /**
13989          * @event replace
13990          * Fires when an item is replaced in the collection.
13991          * @param {String} key he key associated with the new added.
13992          * @param {Object} old The item being replaced.
13993          * @param {Object} new The new item.
13994          */
13995         "replace" : true,
13996         /**
13997          * @event remove
13998          * Fires when an item is removed from the collection.
13999          * @param {Object} o The item being removed.
14000          * @param {String} key (optional) The key associated with the removed item.
14001          */
14002         "remove" : true,
14003         "sort" : true
14004     });
14005     this.allowFunctions = allowFunctions === true;
14006     if(keyFn){
14007         this.getKey = keyFn;
14008     }
14009     Roo.util.MixedCollection.superclass.constructor.call(this);
14010 };
14011
14012 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14013     allowFunctions : false,
14014     
14015 /**
14016  * Adds an item to the collection.
14017  * @param {String} key The key to associate with the item
14018  * @param {Object} o The item to add.
14019  * @return {Object} The item added.
14020  */
14021     add : function(key, o){
14022         if(arguments.length == 1){
14023             o = arguments[0];
14024             key = this.getKey(o);
14025         }
14026         if(typeof key == "undefined" || key === null){
14027             this.length++;
14028             this.items.push(o);
14029             this.keys.push(null);
14030         }else{
14031             var old = this.map[key];
14032             if(old){
14033                 return this.replace(key, o);
14034             }
14035             this.length++;
14036             this.items.push(o);
14037             this.map[key] = o;
14038             this.keys.push(key);
14039         }
14040         this.fireEvent("add", this.length-1, o, key);
14041         return o;
14042     },
14043        
14044 /**
14045   * MixedCollection has a generic way to fetch keys if you implement getKey.
14046 <pre><code>
14047 // normal way
14048 var mc = new Roo.util.MixedCollection();
14049 mc.add(someEl.dom.id, someEl);
14050 mc.add(otherEl.dom.id, otherEl);
14051 //and so on
14052
14053 // using getKey
14054 var mc = new Roo.util.MixedCollection();
14055 mc.getKey = function(el){
14056    return el.dom.id;
14057 };
14058 mc.add(someEl);
14059 mc.add(otherEl);
14060
14061 // or via the constructor
14062 var mc = new Roo.util.MixedCollection(false, function(el){
14063    return el.dom.id;
14064 });
14065 mc.add(someEl);
14066 mc.add(otherEl);
14067 </code></pre>
14068  * @param o {Object} The item for which to find the key.
14069  * @return {Object} The key for the passed item.
14070  */
14071     getKey : function(o){
14072          return o.id; 
14073     },
14074    
14075 /**
14076  * Replaces an item in the collection.
14077  * @param {String} key The key associated with the item to replace, or the item to replace.
14078  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14079  * @return {Object}  The new item.
14080  */
14081     replace : function(key, o){
14082         if(arguments.length == 1){
14083             o = arguments[0];
14084             key = this.getKey(o);
14085         }
14086         var old = this.item(key);
14087         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14088              return this.add(key, o);
14089         }
14090         var index = this.indexOfKey(key);
14091         this.items[index] = o;
14092         this.map[key] = o;
14093         this.fireEvent("replace", key, old, o);
14094         return o;
14095     },
14096    
14097 /**
14098  * Adds all elements of an Array or an Object to the collection.
14099  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14100  * an Array of values, each of which are added to the collection.
14101  */
14102     addAll : function(objs){
14103         if(arguments.length > 1 || objs instanceof Array){
14104             var args = arguments.length > 1 ? arguments : objs;
14105             for(var i = 0, len = args.length; i < len; i++){
14106                 this.add(args[i]);
14107             }
14108         }else{
14109             for(var key in objs){
14110                 if(this.allowFunctions || typeof objs[key] != "function"){
14111                     this.add(key, objs[key]);
14112                 }
14113             }
14114         }
14115     },
14116    
14117 /**
14118  * Executes the specified function once for every item in the collection, passing each
14119  * item as the first and only parameter. returning false from the function will stop the iteration.
14120  * @param {Function} fn The function to execute for each item.
14121  * @param {Object} scope (optional) The scope in which to execute the function.
14122  */
14123     each : function(fn, scope){
14124         var items = [].concat(this.items); // each safe for removal
14125         for(var i = 0, len = items.length; i < len; i++){
14126             if(fn.call(scope || items[i], items[i], i, len) === false){
14127                 break;
14128             }
14129         }
14130     },
14131    
14132 /**
14133  * Executes the specified function once for every key in the collection, passing each
14134  * key, and its associated item as the first two parameters.
14135  * @param {Function} fn The function to execute for each item.
14136  * @param {Object} scope (optional) The scope in which to execute the function.
14137  */
14138     eachKey : function(fn, scope){
14139         for(var i = 0, len = this.keys.length; i < len; i++){
14140             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14141         }
14142     },
14143    
14144 /**
14145  * Returns the first item in the collection which elicits a true return value from the
14146  * passed selection function.
14147  * @param {Function} fn The selection function to execute for each item.
14148  * @param {Object} scope (optional) The scope in which to execute the function.
14149  * @return {Object} The first item in the collection which returned true from the selection function.
14150  */
14151     find : function(fn, scope){
14152         for(var i = 0, len = this.items.length; i < len; i++){
14153             if(fn.call(scope || window, this.items[i], this.keys[i])){
14154                 return this.items[i];
14155             }
14156         }
14157         return null;
14158     },
14159    
14160 /**
14161  * Inserts an item at the specified index in the collection.
14162  * @param {Number} index The index to insert the item at.
14163  * @param {String} key The key to associate with the new item, or the item itself.
14164  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14165  * @return {Object} The item inserted.
14166  */
14167     insert : function(index, key, o){
14168         if(arguments.length == 2){
14169             o = arguments[1];
14170             key = this.getKey(o);
14171         }
14172         if(index >= this.length){
14173             return this.add(key, o);
14174         }
14175         this.length++;
14176         this.items.splice(index, 0, o);
14177         if(typeof key != "undefined" && key != null){
14178             this.map[key] = o;
14179         }
14180         this.keys.splice(index, 0, key);
14181         this.fireEvent("add", index, o, key);
14182         return o;
14183     },
14184    
14185 /**
14186  * Removed an item from the collection.
14187  * @param {Object} o The item to remove.
14188  * @return {Object} The item removed.
14189  */
14190     remove : function(o){
14191         return this.removeAt(this.indexOf(o));
14192     },
14193    
14194 /**
14195  * Remove an item from a specified index in the collection.
14196  * @param {Number} index The index within the collection of the item to remove.
14197  */
14198     removeAt : function(index){
14199         if(index < this.length && index >= 0){
14200             this.length--;
14201             var o = this.items[index];
14202             this.items.splice(index, 1);
14203             var key = this.keys[index];
14204             if(typeof key != "undefined"){
14205                 delete this.map[key];
14206             }
14207             this.keys.splice(index, 1);
14208             this.fireEvent("remove", o, key);
14209         }
14210     },
14211    
14212 /**
14213  * Removed an item associated with the passed key fom the collection.
14214  * @param {String} key The key of the item to remove.
14215  */
14216     removeKey : function(key){
14217         return this.removeAt(this.indexOfKey(key));
14218     },
14219    
14220 /**
14221  * Returns the number of items in the collection.
14222  * @return {Number} the number of items in the collection.
14223  */
14224     getCount : function(){
14225         return this.length; 
14226     },
14227    
14228 /**
14229  * Returns index within the collection of the passed Object.
14230  * @param {Object} o The item to find the index of.
14231  * @return {Number} index of the item.
14232  */
14233     indexOf : function(o){
14234         if(!this.items.indexOf){
14235             for(var i = 0, len = this.items.length; i < len; i++){
14236                 if(this.items[i] == o) {
14237                     return i;
14238                 }
14239             }
14240             return -1;
14241         }else{
14242             return this.items.indexOf(o);
14243         }
14244     },
14245    
14246 /**
14247  * Returns index within the collection of the passed key.
14248  * @param {String} key The key to find the index of.
14249  * @return {Number} index of the key.
14250  */
14251     indexOfKey : function(key){
14252         if(!this.keys.indexOf){
14253             for(var i = 0, len = this.keys.length; i < len; i++){
14254                 if(this.keys[i] == key) {
14255                     return i;
14256                 }
14257             }
14258             return -1;
14259         }else{
14260             return this.keys.indexOf(key);
14261         }
14262     },
14263    
14264 /**
14265  * Returns the item associated with the passed key OR index. Key has priority over index.
14266  * @param {String/Number} key The key or index of the item.
14267  * @return {Object} The item associated with the passed key.
14268  */
14269     item : function(key){
14270         if (key === 'length') {
14271             return null;
14272         }
14273         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14274         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14275     },
14276     
14277 /**
14278  * Returns the item at the specified index.
14279  * @param {Number} index The index of the item.
14280  * @return {Object}
14281  */
14282     itemAt : function(index){
14283         return this.items[index];
14284     },
14285     
14286 /**
14287  * Returns the item associated with the passed key.
14288  * @param {String/Number} key The key of the item.
14289  * @return {Object} The item associated with the passed key.
14290  */
14291     key : function(key){
14292         return this.map[key];
14293     },
14294    
14295 /**
14296  * Returns true if the collection contains the passed Object as an item.
14297  * @param {Object} o  The Object to look for in the collection.
14298  * @return {Boolean} True if the collection contains the Object as an item.
14299  */
14300     contains : function(o){
14301         return this.indexOf(o) != -1;
14302     },
14303    
14304 /**
14305  * Returns true if the collection contains the passed Object as a key.
14306  * @param {String} key The key to look for in the collection.
14307  * @return {Boolean} True if the collection contains the Object as a key.
14308  */
14309     containsKey : function(key){
14310         return typeof this.map[key] != "undefined";
14311     },
14312    
14313 /**
14314  * Removes all items from the collection.
14315  */
14316     clear : function(){
14317         this.length = 0;
14318         this.items = [];
14319         this.keys = [];
14320         this.map = {};
14321         this.fireEvent("clear");
14322     },
14323    
14324 /**
14325  * Returns the first item in the collection.
14326  * @return {Object} the first item in the collection..
14327  */
14328     first : function(){
14329         return this.items[0]; 
14330     },
14331    
14332 /**
14333  * Returns the last item in the collection.
14334  * @return {Object} the last item in the collection..
14335  */
14336     last : function(){
14337         return this.items[this.length-1];   
14338     },
14339     
14340     _sort : function(property, dir, fn){
14341         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14342         fn = fn || function(a, b){
14343             return a-b;
14344         };
14345         var c = [], k = this.keys, items = this.items;
14346         for(var i = 0, len = items.length; i < len; i++){
14347             c[c.length] = {key: k[i], value: items[i], index: i};
14348         }
14349         c.sort(function(a, b){
14350             var v = fn(a[property], b[property]) * dsc;
14351             if(v == 0){
14352                 v = (a.index < b.index ? -1 : 1);
14353             }
14354             return v;
14355         });
14356         for(var i = 0, len = c.length; i < len; i++){
14357             items[i] = c[i].value;
14358             k[i] = c[i].key;
14359         }
14360         this.fireEvent("sort", this);
14361     },
14362     
14363     /**
14364      * Sorts this collection with the passed comparison function
14365      * @param {String} direction (optional) "ASC" or "DESC"
14366      * @param {Function} fn (optional) comparison function
14367      */
14368     sort : function(dir, fn){
14369         this._sort("value", dir, fn);
14370     },
14371     
14372     /**
14373      * Sorts this collection by keys
14374      * @param {String} direction (optional) "ASC" or "DESC"
14375      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14376      */
14377     keySort : function(dir, fn){
14378         this._sort("key", dir, fn || function(a, b){
14379             return String(a).toUpperCase()-String(b).toUpperCase();
14380         });
14381     },
14382     
14383     /**
14384      * Returns a range of items in this collection
14385      * @param {Number} startIndex (optional) defaults to 0
14386      * @param {Number} endIndex (optional) default to the last item
14387      * @return {Array} An array of items
14388      */
14389     getRange : function(start, end){
14390         var items = this.items;
14391         if(items.length < 1){
14392             return [];
14393         }
14394         start = start || 0;
14395         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14396         var r = [];
14397         if(start <= end){
14398             for(var i = start; i <= end; i++) {
14399                     r[r.length] = items[i];
14400             }
14401         }else{
14402             for(var i = start; i >= end; i--) {
14403                     r[r.length] = items[i];
14404             }
14405         }
14406         return r;
14407     },
14408         
14409     /**
14410      * Filter the <i>objects</i> in this collection by a specific property. 
14411      * Returns a new collection that has been filtered.
14412      * @param {String} property A property on your objects
14413      * @param {String/RegExp} value Either string that the property values 
14414      * should start with or a RegExp to test against the property
14415      * @return {MixedCollection} The new filtered collection
14416      */
14417     filter : function(property, value){
14418         if(!value.exec){ // not a regex
14419             value = String(value);
14420             if(value.length == 0){
14421                 return this.clone();
14422             }
14423             value = new RegExp("^" + Roo.escapeRe(value), "i");
14424         }
14425         return this.filterBy(function(o){
14426             return o && value.test(o[property]);
14427         });
14428         },
14429     
14430     /**
14431      * Filter by a function. * Returns a new collection that has been filtered.
14432      * The passed function will be called with each 
14433      * object in the collection. If the function returns true, the value is included 
14434      * otherwise it is filtered.
14435      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14436      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14437      * @return {MixedCollection} The new filtered collection
14438      */
14439     filterBy : function(fn, scope){
14440         var r = new Roo.util.MixedCollection();
14441         r.getKey = this.getKey;
14442         var k = this.keys, it = this.items;
14443         for(var i = 0, len = it.length; i < len; i++){
14444             if(fn.call(scope||this, it[i], k[i])){
14445                                 r.add(k[i], it[i]);
14446                         }
14447         }
14448         return r;
14449     },
14450     
14451     /**
14452      * Creates a duplicate of this collection
14453      * @return {MixedCollection}
14454      */
14455     clone : function(){
14456         var r = new Roo.util.MixedCollection();
14457         var k = this.keys, it = this.items;
14458         for(var i = 0, len = it.length; i < len; i++){
14459             r.add(k[i], it[i]);
14460         }
14461         r.getKey = this.getKey;
14462         return r;
14463     }
14464 });
14465 /**
14466  * Returns the item associated with the passed key or index.
14467  * @method
14468  * @param {String/Number} key The key or index of the item.
14469  * @return {Object} The item associated with the passed key.
14470  */
14471 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14472  * Based on:
14473  * Ext JS Library 1.1.1
14474  * Copyright(c) 2006-2007, Ext JS, LLC.
14475  *
14476  * Originally Released Under LGPL - original licence link has changed is not relivant.
14477  *
14478  * Fork - LGPL
14479  * <script type="text/javascript">
14480  */
14481 /**
14482  * @class Roo.util.JSON
14483  * Modified version of Douglas Crockford"s json.js that doesn"t
14484  * mess with the Object prototype 
14485  * http://www.json.org/js.html
14486  * @static
14487  */
14488 Roo.util.JSON = new (function(){
14489     var useHasOwn = {}.hasOwnProperty ? true : false;
14490     
14491     // crashes Safari in some instances
14492     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14493     
14494     var pad = function(n) {
14495         return n < 10 ? "0" + n : n;
14496     };
14497     
14498     var m = {
14499         "\b": '\\b',
14500         "\t": '\\t',
14501         "\n": '\\n',
14502         "\f": '\\f',
14503         "\r": '\\r',
14504         '"' : '\\"',
14505         "\\": '\\\\'
14506     };
14507
14508     var encodeString = function(s){
14509         if (/["\\\x00-\x1f]/.test(s)) {
14510             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14511                 var c = m[b];
14512                 if(c){
14513                     return c;
14514                 }
14515                 c = b.charCodeAt();
14516                 return "\\u00" +
14517                     Math.floor(c / 16).toString(16) +
14518                     (c % 16).toString(16);
14519             }) + '"';
14520         }
14521         return '"' + s + '"';
14522     };
14523     
14524     var encodeArray = function(o){
14525         var a = ["["], b, i, l = o.length, v;
14526             for (i = 0; i < l; i += 1) {
14527                 v = o[i];
14528                 switch (typeof v) {
14529                     case "undefined":
14530                     case "function":
14531                     case "unknown":
14532                         break;
14533                     default:
14534                         if (b) {
14535                             a.push(',');
14536                         }
14537                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14538                         b = true;
14539                 }
14540             }
14541             a.push("]");
14542             return a.join("");
14543     };
14544     
14545     var encodeDate = function(o){
14546         return '"' + o.getFullYear() + "-" +
14547                 pad(o.getMonth() + 1) + "-" +
14548                 pad(o.getDate()) + "T" +
14549                 pad(o.getHours()) + ":" +
14550                 pad(o.getMinutes()) + ":" +
14551                 pad(o.getSeconds()) + '"';
14552     };
14553     
14554     /**
14555      * Encodes an Object, Array or other value
14556      * @param {Mixed} o The variable to encode
14557      * @return {String} The JSON string
14558      */
14559     this.encode = function(o)
14560     {
14561         // should this be extended to fully wrap stringify..
14562         
14563         if(typeof o == "undefined" || o === null){
14564             return "null";
14565         }else if(o instanceof Array){
14566             return encodeArray(o);
14567         }else if(o instanceof Date){
14568             return encodeDate(o);
14569         }else if(typeof o == "string"){
14570             return encodeString(o);
14571         }else if(typeof o == "number"){
14572             return isFinite(o) ? String(o) : "null";
14573         }else if(typeof o == "boolean"){
14574             return String(o);
14575         }else {
14576             var a = ["{"], b, i, v;
14577             for (i in o) {
14578                 if(!useHasOwn || o.hasOwnProperty(i)) {
14579                     v = o[i];
14580                     switch (typeof v) {
14581                     case "undefined":
14582                     case "function":
14583                     case "unknown":
14584                         break;
14585                     default:
14586                         if(b){
14587                             a.push(',');
14588                         }
14589                         a.push(this.encode(i), ":",
14590                                 v === null ? "null" : this.encode(v));
14591                         b = true;
14592                     }
14593                 }
14594             }
14595             a.push("}");
14596             return a.join("");
14597         }
14598     };
14599     
14600     /**
14601      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14602      * @param {String} json The JSON string
14603      * @return {Object} The resulting object
14604      */
14605     this.decode = function(json){
14606         
14607         return  /** eval:var:json */ eval("(" + json + ')');
14608     };
14609 })();
14610 /** 
14611  * Shorthand for {@link Roo.util.JSON#encode}
14612  * @member Roo encode 
14613  * @method */
14614 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14615 /** 
14616  * Shorthand for {@link Roo.util.JSON#decode}
14617  * @member Roo decode 
14618  * @method */
14619 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14620 /*
14621  * Based on:
14622  * Ext JS Library 1.1.1
14623  * Copyright(c) 2006-2007, Ext JS, LLC.
14624  *
14625  * Originally Released Under LGPL - original licence link has changed is not relivant.
14626  *
14627  * Fork - LGPL
14628  * <script type="text/javascript">
14629  */
14630  
14631 /**
14632  * @class Roo.util.Format
14633  * Reusable data formatting functions
14634  * @static
14635  */
14636 Roo.util.Format = function(){
14637     var trimRe = /^\s+|\s+$/g;
14638     return {
14639         /**
14640          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14641          * @param {String} value The string to truncate
14642          * @param {Number} length The maximum length to allow before truncating
14643          * @return {String} The converted text
14644          */
14645         ellipsis : function(value, len){
14646             if(value && value.length > len){
14647                 return value.substr(0, len-3)+"...";
14648             }
14649             return value;
14650         },
14651
14652         /**
14653          * Checks a reference and converts it to empty string if it is undefined
14654          * @param {Mixed} value Reference to check
14655          * @return {Mixed} Empty string if converted, otherwise the original value
14656          */
14657         undef : function(value){
14658             return typeof value != "undefined" ? value : "";
14659         },
14660
14661         /**
14662          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14663          * @param {String} value The string to encode
14664          * @return {String} The encoded text
14665          */
14666         htmlEncode : function(value){
14667             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14668         },
14669
14670         /**
14671          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14672          * @param {String} value The string to decode
14673          * @return {String} The decoded text
14674          */
14675         htmlDecode : function(value){
14676             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14677         },
14678
14679         /**
14680          * Trims any whitespace from either side of a string
14681          * @param {String} value The text to trim
14682          * @return {String} The trimmed text
14683          */
14684         trim : function(value){
14685             return String(value).replace(trimRe, "");
14686         },
14687
14688         /**
14689          * Returns a substring from within an original string
14690          * @param {String} value The original text
14691          * @param {Number} start The start index of the substring
14692          * @param {Number} length The length of the substring
14693          * @return {String} The substring
14694          */
14695         substr : function(value, start, length){
14696             return String(value).substr(start, length);
14697         },
14698
14699         /**
14700          * Converts a string to all lower case letters
14701          * @param {String} value The text to convert
14702          * @return {String} The converted text
14703          */
14704         lowercase : function(value){
14705             return String(value).toLowerCase();
14706         },
14707
14708         /**
14709          * Converts a string to all upper case letters
14710          * @param {String} value The text to convert
14711          * @return {String} The converted text
14712          */
14713         uppercase : function(value){
14714             return String(value).toUpperCase();
14715         },
14716
14717         /**
14718          * Converts the first character only of a string to upper case
14719          * @param {String} value The text to convert
14720          * @return {String} The converted text
14721          */
14722         capitalize : function(value){
14723             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14724         },
14725
14726         // private
14727         call : function(value, fn){
14728             if(arguments.length > 2){
14729                 var args = Array.prototype.slice.call(arguments, 2);
14730                 args.unshift(value);
14731                  
14732                 return /** eval:var:value */  eval(fn).apply(window, args);
14733             }else{
14734                 /** eval:var:value */
14735                 return /** eval:var:value */ eval(fn).call(window, value);
14736             }
14737         },
14738
14739        
14740         /**
14741          * safer version of Math.toFixed..??/
14742          * @param {Number/String} value The numeric value to format
14743          * @param {Number/String} value Decimal places 
14744          * @return {String} The formatted currency string
14745          */
14746         toFixed : function(v, n)
14747         {
14748             // why not use to fixed - precision is buggered???
14749             if (!n) {
14750                 return Math.round(v-0);
14751             }
14752             var fact = Math.pow(10,n+1);
14753             v = (Math.round((v-0)*fact))/fact;
14754             var z = (''+fact).substring(2);
14755             if (v == Math.floor(v)) {
14756                 return Math.floor(v) + '.' + z;
14757             }
14758             
14759             // now just padd decimals..
14760             var ps = String(v).split('.');
14761             var fd = (ps[1] + z);
14762             var r = fd.substring(0,n); 
14763             var rm = fd.substring(n); 
14764             if (rm < 5) {
14765                 return ps[0] + '.' + r;
14766             }
14767             r*=1; // turn it into a number;
14768             r++;
14769             if (String(r).length != n) {
14770                 ps[0]*=1;
14771                 ps[0]++;
14772                 r = String(r).substring(1); // chop the end off.
14773             }
14774             
14775             return ps[0] + '.' + r;
14776              
14777         },
14778         
14779         /**
14780          * Format a number as US currency
14781          * @param {Number/String} value The numeric value to format
14782          * @return {String} The formatted currency string
14783          */
14784         usMoney : function(v){
14785             return '$' + Roo.util.Format.number(v);
14786         },
14787         
14788         /**
14789          * Format a number
14790          * eventually this should probably emulate php's number_format
14791          * @param {Number/String} value The numeric value to format
14792          * @param {Number} decimals number of decimal places
14793          * @param {String} delimiter for thousands (default comma)
14794          * @return {String} The formatted currency string
14795          */
14796         number : function(v, decimals, thousandsDelimiter)
14797         {
14798             // multiply and round.
14799             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14800             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14801             
14802             var mul = Math.pow(10, decimals);
14803             var zero = String(mul).substring(1);
14804             v = (Math.round((v-0)*mul))/mul;
14805             
14806             // if it's '0' number.. then
14807             
14808             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14809             v = String(v);
14810             var ps = v.split('.');
14811             var whole = ps[0];
14812             
14813             var r = /(\d+)(\d{3})/;
14814             // add comma's
14815             
14816             if(thousandsDelimiter.length != 0) {
14817                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14818             } 
14819             
14820             var sub = ps[1] ?
14821                     // has decimals..
14822                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14823                     // does not have decimals
14824                     (decimals ? ('.' + zero) : '');
14825             
14826             
14827             return whole + sub ;
14828         },
14829         
14830         /**
14831          * Parse a value into a formatted date using the specified format pattern.
14832          * @param {Mixed} value The value to format
14833          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14834          * @return {String} The formatted date string
14835          */
14836         date : function(v, format){
14837             if(!v){
14838                 return "";
14839             }
14840             if(!(v instanceof Date)){
14841                 v = new Date(Date.parse(v));
14842             }
14843             return v.dateFormat(format || Roo.util.Format.defaults.date);
14844         },
14845
14846         /**
14847          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14848          * @param {String} format Any valid date format string
14849          * @return {Function} The date formatting function
14850          */
14851         dateRenderer : function(format){
14852             return function(v){
14853                 return Roo.util.Format.date(v, format);  
14854             };
14855         },
14856
14857         // private
14858         stripTagsRE : /<\/?[^>]+>/gi,
14859         
14860         /**
14861          * Strips all HTML tags
14862          * @param {Mixed} value The text from which to strip tags
14863          * @return {String} The stripped text
14864          */
14865         stripTags : function(v){
14866             return !v ? v : String(v).replace(this.stripTagsRE, "");
14867         },
14868         
14869         /**
14870          * Size in Mb,Gb etc.
14871          * @param {Number} value The number to be formated
14872          * @param {number} decimals how many decimal places
14873          * @return {String} the formated string
14874          */
14875         size : function(value, decimals)
14876         {
14877             var sizes = ['b', 'k', 'M', 'G', 'T'];
14878             if (value == 0) {
14879                 return 0;
14880             }
14881             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14882             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14883         }
14884         
14885         
14886         
14887     };
14888 }();
14889 Roo.util.Format.defaults = {
14890     date : 'd/M/Y'
14891 };/*
14892  * Based on:
14893  * Ext JS Library 1.1.1
14894  * Copyright(c) 2006-2007, Ext JS, LLC.
14895  *
14896  * Originally Released Under LGPL - original licence link has changed is not relivant.
14897  *
14898  * Fork - LGPL
14899  * <script type="text/javascript">
14900  */
14901
14902
14903  
14904
14905 /**
14906  * @class Roo.MasterTemplate
14907  * @extends Roo.Template
14908  * Provides a template that can have child templates. The syntax is:
14909 <pre><code>
14910 var t = new Roo.MasterTemplate(
14911         '&lt;select name="{name}"&gt;',
14912                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14913         '&lt;/select&gt;'
14914 );
14915 t.add('options', {value: 'foo', text: 'bar'});
14916 // or you can add multiple child elements in one shot
14917 t.addAll('options', [
14918     {value: 'foo', text: 'bar'},
14919     {value: 'foo2', text: 'bar2'},
14920     {value: 'foo3', text: 'bar3'}
14921 ]);
14922 // then append, applying the master template values
14923 t.append('my-form', {name: 'my-select'});
14924 </code></pre>
14925 * A name attribute for the child template is not required if you have only one child
14926 * template or you want to refer to them by index.
14927  */
14928 Roo.MasterTemplate = function(){
14929     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14930     this.originalHtml = this.html;
14931     var st = {};
14932     var m, re = this.subTemplateRe;
14933     re.lastIndex = 0;
14934     var subIndex = 0;
14935     while(m = re.exec(this.html)){
14936         var name = m[1], content = m[2];
14937         st[subIndex] = {
14938             name: name,
14939             index: subIndex,
14940             buffer: [],
14941             tpl : new Roo.Template(content)
14942         };
14943         if(name){
14944             st[name] = st[subIndex];
14945         }
14946         st[subIndex].tpl.compile();
14947         st[subIndex].tpl.call = this.call.createDelegate(this);
14948         subIndex++;
14949     }
14950     this.subCount = subIndex;
14951     this.subs = st;
14952 };
14953 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14954     /**
14955     * The regular expression used to match sub templates
14956     * @type RegExp
14957     * @property
14958     */
14959     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14960
14961     /**
14962      * Applies the passed values to a child template.
14963      * @param {String/Number} name (optional) The name or index of the child template
14964      * @param {Array/Object} values The values to be applied to the template
14965      * @return {MasterTemplate} this
14966      */
14967      add : function(name, values){
14968         if(arguments.length == 1){
14969             values = arguments[0];
14970             name = 0;
14971         }
14972         var s = this.subs[name];
14973         s.buffer[s.buffer.length] = s.tpl.apply(values);
14974         return this;
14975     },
14976
14977     /**
14978      * Applies all the passed values to a child template.
14979      * @param {String/Number} name (optional) The name or index of the child template
14980      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14981      * @param {Boolean} reset (optional) True to reset the template first
14982      * @return {MasterTemplate} this
14983      */
14984     fill : function(name, values, reset){
14985         var a = arguments;
14986         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14987             values = a[0];
14988             name = 0;
14989             reset = a[1];
14990         }
14991         if(reset){
14992             this.reset();
14993         }
14994         for(var i = 0, len = values.length; i < len; i++){
14995             this.add(name, values[i]);
14996         }
14997         return this;
14998     },
14999
15000     /**
15001      * Resets the template for reuse
15002      * @return {MasterTemplate} this
15003      */
15004      reset : function(){
15005         var s = this.subs;
15006         for(var i = 0; i < this.subCount; i++){
15007             s[i].buffer = [];
15008         }
15009         return this;
15010     },
15011
15012     applyTemplate : function(values){
15013         var s = this.subs;
15014         var replaceIndex = -1;
15015         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15016             return s[++replaceIndex].buffer.join("");
15017         });
15018         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15019     },
15020
15021     apply : function(){
15022         return this.applyTemplate.apply(this, arguments);
15023     },
15024
15025     compile : function(){return this;}
15026 });
15027
15028 /**
15029  * Alias for fill().
15030  * @method
15031  */
15032 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15033  /**
15034  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15035  * var tpl = Roo.MasterTemplate.from('element-id');
15036  * @param {String/HTMLElement} el
15037  * @param {Object} config
15038  * @static
15039  */
15040 Roo.MasterTemplate.from = function(el, config){
15041     el = Roo.getDom(el);
15042     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15043 };/*
15044  * Based on:
15045  * Ext JS Library 1.1.1
15046  * Copyright(c) 2006-2007, Ext JS, LLC.
15047  *
15048  * Originally Released Under LGPL - original licence link has changed is not relivant.
15049  *
15050  * Fork - LGPL
15051  * <script type="text/javascript">
15052  */
15053
15054  
15055 /**
15056  * @class Roo.util.CSS
15057  * Utility class for manipulating CSS rules
15058  * @static
15059
15060  */
15061 Roo.util.CSS = function(){
15062         var rules = null;
15063         var doc = document;
15064
15065     var camelRe = /(-[a-z])/gi;
15066     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15067
15068    return {
15069    /**
15070     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15071     * tag and appended to the HEAD of the document.
15072     * @param {String|Object} cssText The text containing the css rules
15073     * @param {String} id An id to add to the stylesheet for later removal
15074     * @return {StyleSheet}
15075     */
15076     createStyleSheet : function(cssText, id){
15077         var ss;
15078         var head = doc.getElementsByTagName("head")[0];
15079         var nrules = doc.createElement("style");
15080         nrules.setAttribute("type", "text/css");
15081         if(id){
15082             nrules.setAttribute("id", id);
15083         }
15084         if (typeof(cssText) != 'string') {
15085             // support object maps..
15086             // not sure if this a good idea.. 
15087             // perhaps it should be merged with the general css handling
15088             // and handle js style props.
15089             var cssTextNew = [];
15090             for(var n in cssText) {
15091                 var citems = [];
15092                 for(var k in cssText[n]) {
15093                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15094                 }
15095                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15096                 
15097             }
15098             cssText = cssTextNew.join("\n");
15099             
15100         }
15101        
15102        
15103        if(Roo.isIE){
15104            head.appendChild(nrules);
15105            ss = nrules.styleSheet;
15106            ss.cssText = cssText;
15107        }else{
15108            try{
15109                 nrules.appendChild(doc.createTextNode(cssText));
15110            }catch(e){
15111                nrules.cssText = cssText; 
15112            }
15113            head.appendChild(nrules);
15114            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15115        }
15116        this.cacheStyleSheet(ss);
15117        return ss;
15118    },
15119
15120    /**
15121     * Removes a style or link tag by id
15122     * @param {String} id The id of the tag
15123     */
15124    removeStyleSheet : function(id){
15125        var existing = doc.getElementById(id);
15126        if(existing){
15127            existing.parentNode.removeChild(existing);
15128        }
15129    },
15130
15131    /**
15132     * Dynamically swaps an existing stylesheet reference for a new one
15133     * @param {String} id The id of an existing link tag to remove
15134     * @param {String} url The href of the new stylesheet to include
15135     */
15136    swapStyleSheet : function(id, url){
15137        this.removeStyleSheet(id);
15138        var ss = doc.createElement("link");
15139        ss.setAttribute("rel", "stylesheet");
15140        ss.setAttribute("type", "text/css");
15141        ss.setAttribute("id", id);
15142        ss.setAttribute("href", url);
15143        doc.getElementsByTagName("head")[0].appendChild(ss);
15144    },
15145    
15146    /**
15147     * Refresh the rule cache if you have dynamically added stylesheets
15148     * @return {Object} An object (hash) of rules indexed by selector
15149     */
15150    refreshCache : function(){
15151        return this.getRules(true);
15152    },
15153
15154    // private
15155    cacheStyleSheet : function(stylesheet){
15156        if(!rules){
15157            rules = {};
15158        }
15159        try{// try catch for cross domain access issue
15160            var ssRules = stylesheet.cssRules || stylesheet.rules;
15161            for(var j = ssRules.length-1; j >= 0; --j){
15162                rules[ssRules[j].selectorText] = ssRules[j];
15163            }
15164        }catch(e){}
15165    },
15166    
15167    /**
15168     * Gets all css rules for the document
15169     * @param {Boolean} refreshCache true to refresh the internal cache
15170     * @return {Object} An object (hash) of rules indexed by selector
15171     */
15172    getRules : function(refreshCache){
15173                 if(rules == null || refreshCache){
15174                         rules = {};
15175                         var ds = doc.styleSheets;
15176                         for(var i =0, len = ds.length; i < len; i++){
15177                             try{
15178                         this.cacheStyleSheet(ds[i]);
15179                     }catch(e){} 
15180                 }
15181                 }
15182                 return rules;
15183         },
15184         
15185         /**
15186     * Gets an an individual CSS rule by selector(s)
15187     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15188     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15189     * @return {CSSRule} The CSS rule or null if one is not found
15190     */
15191    getRule : function(selector, refreshCache){
15192                 var rs = this.getRules(refreshCache);
15193                 if(!(selector instanceof Array)){
15194                     return rs[selector];
15195                 }
15196                 for(var i = 0; i < selector.length; i++){
15197                         if(rs[selector[i]]){
15198                                 return rs[selector[i]];
15199                         }
15200                 }
15201                 return null;
15202         },
15203         
15204         
15205         /**
15206     * Updates a rule property
15207     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15208     * @param {String} property The css property
15209     * @param {String} value The new value for the property
15210     * @return {Boolean} true If a rule was found and updated
15211     */
15212    updateRule : function(selector, property, value){
15213                 if(!(selector instanceof Array)){
15214                         var rule = this.getRule(selector);
15215                         if(rule){
15216                                 rule.style[property.replace(camelRe, camelFn)] = value;
15217                                 return true;
15218                         }
15219                 }else{
15220                         for(var i = 0; i < selector.length; i++){
15221                                 if(this.updateRule(selector[i], property, value)){
15222                                         return true;
15223                                 }
15224                         }
15225                 }
15226                 return false;
15227         }
15228    };   
15229 }();/*
15230  * Based on:
15231  * Ext JS Library 1.1.1
15232  * Copyright(c) 2006-2007, Ext JS, LLC.
15233  *
15234  * Originally Released Under LGPL - original licence link has changed is not relivant.
15235  *
15236  * Fork - LGPL
15237  * <script type="text/javascript">
15238  */
15239
15240  
15241
15242 /**
15243  * @class Roo.util.ClickRepeater
15244  * @extends Roo.util.Observable
15245  * 
15246  * A wrapper class which can be applied to any element. Fires a "click" event while the
15247  * mouse is pressed. The interval between firings may be specified in the config but
15248  * defaults to 10 milliseconds.
15249  * 
15250  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15251  * 
15252  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15253  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15254  * Similar to an autorepeat key delay.
15255  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15256  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15257  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15258  *           "interval" and "delay" are ignored. "immediate" is honored.
15259  * @cfg {Boolean} preventDefault True to prevent the default click event
15260  * @cfg {Boolean} stopDefault True to stop the default click event
15261  * 
15262  * @history
15263  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15264  *     2007-02-02 jvs Renamed to ClickRepeater
15265  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15266  *
15267  *  @constructor
15268  * @param {String/HTMLElement/Element} el The element to listen on
15269  * @param {Object} config
15270  **/
15271 Roo.util.ClickRepeater = function(el, config)
15272 {
15273     this.el = Roo.get(el);
15274     this.el.unselectable();
15275
15276     Roo.apply(this, config);
15277
15278     this.addEvents({
15279     /**
15280      * @event mousedown
15281      * Fires when the mouse button is depressed.
15282      * @param {Roo.util.ClickRepeater} this
15283      */
15284         "mousedown" : true,
15285     /**
15286      * @event click
15287      * Fires on a specified interval during the time the element is pressed.
15288      * @param {Roo.util.ClickRepeater} this
15289      */
15290         "click" : true,
15291     /**
15292      * @event mouseup
15293      * Fires when the mouse key is released.
15294      * @param {Roo.util.ClickRepeater} this
15295      */
15296         "mouseup" : true
15297     });
15298
15299     this.el.on("mousedown", this.handleMouseDown, this);
15300     if(this.preventDefault || this.stopDefault){
15301         this.el.on("click", function(e){
15302             if(this.preventDefault){
15303                 e.preventDefault();
15304             }
15305             if(this.stopDefault){
15306                 e.stopEvent();
15307             }
15308         }, this);
15309     }
15310
15311     // allow inline handler
15312     if(this.handler){
15313         this.on("click", this.handler,  this.scope || this);
15314     }
15315
15316     Roo.util.ClickRepeater.superclass.constructor.call(this);
15317 };
15318
15319 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15320     interval : 20,
15321     delay: 250,
15322     preventDefault : true,
15323     stopDefault : false,
15324     timer : 0,
15325
15326     // private
15327     handleMouseDown : function(){
15328         clearTimeout(this.timer);
15329         this.el.blur();
15330         if(this.pressClass){
15331             this.el.addClass(this.pressClass);
15332         }
15333         this.mousedownTime = new Date();
15334
15335         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15336         this.el.on("mouseout", this.handleMouseOut, this);
15337
15338         this.fireEvent("mousedown", this);
15339         this.fireEvent("click", this);
15340         
15341         this.timer = this.click.defer(this.delay || this.interval, this);
15342     },
15343
15344     // private
15345     click : function(){
15346         this.fireEvent("click", this);
15347         this.timer = this.click.defer(this.getInterval(), this);
15348     },
15349
15350     // private
15351     getInterval: function(){
15352         if(!this.accelerate){
15353             return this.interval;
15354         }
15355         var pressTime = this.mousedownTime.getElapsed();
15356         if(pressTime < 500){
15357             return 400;
15358         }else if(pressTime < 1700){
15359             return 320;
15360         }else if(pressTime < 2600){
15361             return 250;
15362         }else if(pressTime < 3500){
15363             return 180;
15364         }else if(pressTime < 4400){
15365             return 140;
15366         }else if(pressTime < 5300){
15367             return 80;
15368         }else if(pressTime < 6200){
15369             return 50;
15370         }else{
15371             return 10;
15372         }
15373     },
15374
15375     // private
15376     handleMouseOut : function(){
15377         clearTimeout(this.timer);
15378         if(this.pressClass){
15379             this.el.removeClass(this.pressClass);
15380         }
15381         this.el.on("mouseover", this.handleMouseReturn, this);
15382     },
15383
15384     // private
15385     handleMouseReturn : function(){
15386         this.el.un("mouseover", this.handleMouseReturn);
15387         if(this.pressClass){
15388             this.el.addClass(this.pressClass);
15389         }
15390         this.click();
15391     },
15392
15393     // private
15394     handleMouseUp : function(){
15395         clearTimeout(this.timer);
15396         this.el.un("mouseover", this.handleMouseReturn);
15397         this.el.un("mouseout", this.handleMouseOut);
15398         Roo.get(document).un("mouseup", this.handleMouseUp);
15399         this.el.removeClass(this.pressClass);
15400         this.fireEvent("mouseup", this);
15401     }
15402 });/**
15403  * @class Roo.util.Clipboard
15404  * @static
15405  * 
15406  * Clipboard UTILS
15407  * 
15408  **/
15409 Roo.util.Clipboard = {
15410     /**
15411      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15412      * @param {String} text to copy to clipboard
15413      */
15414     write : function(text) {
15415         // navigator clipboard api needs a secure context (https)
15416         if (navigator.clipboard && window.isSecureContext) {
15417             // navigator clipboard api method'
15418             navigator.clipboard.writeText(text);
15419             return ;
15420         } 
15421         // text area method
15422         var ta = document.createElement("textarea");
15423         ta.value = text;
15424         // make the textarea out of viewport
15425         ta.style.position = "fixed";
15426         ta.style.left = "-999999px";
15427         ta.style.top = "-999999px";
15428         document.body.appendChild(ta);
15429         ta.focus();
15430         ta.select();
15431         document.execCommand('copy');
15432         (function() {
15433             ta.remove();
15434         }).defer(100);
15435         
15436     }
15437         
15438 }
15439     /*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450  
15451 /**
15452  * @class Roo.KeyNav
15453  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15454  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15455  * way to implement custom navigation schemes for any UI component.</p>
15456  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15457  * pageUp, pageDown, del, home, end.  Usage:</p>
15458  <pre><code>
15459 var nav = new Roo.KeyNav("my-element", {
15460     "left" : function(e){
15461         this.moveLeft(e.ctrlKey);
15462     },
15463     "right" : function(e){
15464         this.moveRight(e.ctrlKey);
15465     },
15466     "enter" : function(e){
15467         this.save();
15468     },
15469     scope : this
15470 });
15471 </code></pre>
15472  * @constructor
15473  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15474  * @param {Object} config The config
15475  */
15476 Roo.KeyNav = function(el, config){
15477     this.el = Roo.get(el);
15478     Roo.apply(this, config);
15479     if(!this.disabled){
15480         this.disabled = true;
15481         this.enable();
15482     }
15483 };
15484
15485 Roo.KeyNav.prototype = {
15486     /**
15487      * @cfg {Boolean} disabled
15488      * True to disable this KeyNav instance (defaults to false)
15489      */
15490     disabled : false,
15491     /**
15492      * @cfg {String} defaultEventAction
15493      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15494      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15495      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15496      */
15497     defaultEventAction: "stopEvent",
15498     /**
15499      * @cfg {Boolean} forceKeyDown
15500      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15501      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15502      * handle keydown instead of keypress.
15503      */
15504     forceKeyDown : false,
15505
15506     // private
15507     prepareEvent : function(e){
15508         var k = e.getKey();
15509         var h = this.keyToHandler[k];
15510         //if(h && this[h]){
15511         //    e.stopPropagation();
15512         //}
15513         if(Roo.isSafari && h && k >= 37 && k <= 40){
15514             e.stopEvent();
15515         }
15516     },
15517
15518     // private
15519     relay : function(e){
15520         var k = e.getKey();
15521         var h = this.keyToHandler[k];
15522         if(h && this[h]){
15523             if(this.doRelay(e, this[h], h) !== true){
15524                 e[this.defaultEventAction]();
15525             }
15526         }
15527     },
15528
15529     // private
15530     doRelay : function(e, h, hname){
15531         return h.call(this.scope || this, e);
15532     },
15533
15534     // possible handlers
15535     enter : false,
15536     left : false,
15537     right : false,
15538     up : false,
15539     down : false,
15540     tab : false,
15541     esc : false,
15542     pageUp : false,
15543     pageDown : false,
15544     del : false,
15545     home : false,
15546     end : false,
15547
15548     // quick lookup hash
15549     keyToHandler : {
15550         37 : "left",
15551         39 : "right",
15552         38 : "up",
15553         40 : "down",
15554         33 : "pageUp",
15555         34 : "pageDown",
15556         46 : "del",
15557         36 : "home",
15558         35 : "end",
15559         13 : "enter",
15560         27 : "esc",
15561         9  : "tab"
15562     },
15563
15564         /**
15565          * Enable this KeyNav
15566          */
15567         enable: function(){
15568                 if(this.disabled){
15569             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15570             // the EventObject will normalize Safari automatically
15571             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15572                 this.el.on("keydown", this.relay,  this);
15573             }else{
15574                 this.el.on("keydown", this.prepareEvent,  this);
15575                 this.el.on("keypress", this.relay,  this);
15576             }
15577                     this.disabled = false;
15578                 }
15579         },
15580
15581         /**
15582          * Disable this KeyNav
15583          */
15584         disable: function(){
15585                 if(!this.disabled){
15586                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15587                 this.el.un("keydown", this.relay);
15588             }else{
15589                 this.el.un("keydown", this.prepareEvent);
15590                 this.el.un("keypress", this.relay);
15591             }
15592                     this.disabled = true;
15593                 }
15594         }
15595 };/*
15596  * Based on:
15597  * Ext JS Library 1.1.1
15598  * Copyright(c) 2006-2007, Ext JS, LLC.
15599  *
15600  * Originally Released Under LGPL - original licence link has changed is not relivant.
15601  *
15602  * Fork - LGPL
15603  * <script type="text/javascript">
15604  */
15605
15606  
15607 /**
15608  * @class Roo.KeyMap
15609  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15610  * The constructor accepts the same config object as defined by {@link #addBinding}.
15611  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15612  * combination it will call the function with this signature (if the match is a multi-key
15613  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15614  * A KeyMap can also handle a string representation of keys.<br />
15615  * Usage:
15616  <pre><code>
15617 // map one key by key code
15618 var map = new Roo.KeyMap("my-element", {
15619     key: 13, // or Roo.EventObject.ENTER
15620     fn: myHandler,
15621     scope: myObject
15622 });
15623
15624 // map multiple keys to one action by string
15625 var map = new Roo.KeyMap("my-element", {
15626     key: "a\r\n\t",
15627     fn: myHandler,
15628     scope: myObject
15629 });
15630
15631 // map multiple keys to multiple actions by strings and array of codes
15632 var map = new Roo.KeyMap("my-element", [
15633     {
15634         key: [10,13],
15635         fn: function(){ alert("Return was pressed"); }
15636     }, {
15637         key: "abc",
15638         fn: function(){ alert('a, b or c was pressed'); }
15639     }, {
15640         key: "\t",
15641         ctrl:true,
15642         shift:true,
15643         fn: function(){ alert('Control + shift + tab was pressed.'); }
15644     }
15645 ]);
15646 </code></pre>
15647  * <b>Note: A KeyMap starts enabled</b>
15648  * @constructor
15649  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15650  * @param {Object} config The config (see {@link #addBinding})
15651  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15652  */
15653 Roo.KeyMap = function(el, config, eventName){
15654     this.el  = Roo.get(el);
15655     this.eventName = eventName || "keydown";
15656     this.bindings = [];
15657     if(config){
15658         this.addBinding(config);
15659     }
15660     this.enable();
15661 };
15662
15663 Roo.KeyMap.prototype = {
15664     /**
15665      * True to stop the event from bubbling and prevent the default browser action if the
15666      * key was handled by the KeyMap (defaults to false)
15667      * @type Boolean
15668      */
15669     stopEvent : false,
15670
15671     /**
15672      * Add a new binding to this KeyMap. The following config object properties are supported:
15673      * <pre>
15674 Property    Type             Description
15675 ----------  ---------------  ----------------------------------------------------------------------
15676 key         String/Array     A single keycode or an array of keycodes to handle
15677 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15678 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15679 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15680 fn          Function         The function to call when KeyMap finds the expected key combination
15681 scope       Object           The scope of the callback function
15682 </pre>
15683      *
15684      * Usage:
15685      * <pre><code>
15686 // Create a KeyMap
15687 var map = new Roo.KeyMap(document, {
15688     key: Roo.EventObject.ENTER,
15689     fn: handleKey,
15690     scope: this
15691 });
15692
15693 //Add a new binding to the existing KeyMap later
15694 map.addBinding({
15695     key: 'abc',
15696     shift: true,
15697     fn: handleKey,
15698     scope: this
15699 });
15700 </code></pre>
15701      * @param {Object/Array} config A single KeyMap config or an array of configs
15702      */
15703         addBinding : function(config){
15704         if(config instanceof Array){
15705             for(var i = 0, len = config.length; i < len; i++){
15706                 this.addBinding(config[i]);
15707             }
15708             return;
15709         }
15710         var keyCode = config.key,
15711             shift = config.shift, 
15712             ctrl = config.ctrl, 
15713             alt = config.alt,
15714             fn = config.fn,
15715             scope = config.scope;
15716         if(typeof keyCode == "string"){
15717             var ks = [];
15718             var keyString = keyCode.toUpperCase();
15719             for(var j = 0, len = keyString.length; j < len; j++){
15720                 ks.push(keyString.charCodeAt(j));
15721             }
15722             keyCode = ks;
15723         }
15724         var keyArray = keyCode instanceof Array;
15725         var handler = function(e){
15726             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15727                 var k = e.getKey();
15728                 if(keyArray){
15729                     for(var i = 0, len = keyCode.length; i < len; i++){
15730                         if(keyCode[i] == k){
15731                           if(this.stopEvent){
15732                               e.stopEvent();
15733                           }
15734                           fn.call(scope || window, k, e);
15735                           return;
15736                         }
15737                     }
15738                 }else{
15739                     if(k == keyCode){
15740                         if(this.stopEvent){
15741                            e.stopEvent();
15742                         }
15743                         fn.call(scope || window, k, e);
15744                     }
15745                 }
15746             }
15747         };
15748         this.bindings.push(handler);  
15749         },
15750
15751     /**
15752      * Shorthand for adding a single key listener
15753      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15754      * following options:
15755      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15756      * @param {Function} fn The function to call
15757      * @param {Object} scope (optional) The scope of the function
15758      */
15759     on : function(key, fn, scope){
15760         var keyCode, shift, ctrl, alt;
15761         if(typeof key == "object" && !(key instanceof Array)){
15762             keyCode = key.key;
15763             shift = key.shift;
15764             ctrl = key.ctrl;
15765             alt = key.alt;
15766         }else{
15767             keyCode = key;
15768         }
15769         this.addBinding({
15770             key: keyCode,
15771             shift: shift,
15772             ctrl: ctrl,
15773             alt: alt,
15774             fn: fn,
15775             scope: scope
15776         })
15777     },
15778
15779     // private
15780     handleKeyDown : function(e){
15781             if(this.enabled){ //just in case
15782             var b = this.bindings;
15783             for(var i = 0, len = b.length; i < len; i++){
15784                 b[i].call(this, e);
15785             }
15786             }
15787         },
15788         
15789         /**
15790          * Returns true if this KeyMap is enabled
15791          * @return {Boolean} 
15792          */
15793         isEnabled : function(){
15794             return this.enabled;  
15795         },
15796         
15797         /**
15798          * Enables this KeyMap
15799          */
15800         enable: function(){
15801                 if(!this.enabled){
15802                     this.el.on(this.eventName, this.handleKeyDown, this);
15803                     this.enabled = true;
15804                 }
15805         },
15806
15807         /**
15808          * Disable this KeyMap
15809          */
15810         disable: function(){
15811                 if(this.enabled){
15812                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15813                     this.enabled = false;
15814                 }
15815         }
15816 };/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826
15827  
15828 /**
15829  * @class Roo.util.TextMetrics
15830  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15831  * wide, in pixels, a given block of text will be.
15832  * @static
15833  */
15834 Roo.util.TextMetrics = function(){
15835     var shared;
15836     return {
15837         /**
15838          * Measures the size of the specified text
15839          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15840          * that can affect the size of the rendered text
15841          * @param {String} text The text to measure
15842          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15843          * in order to accurately measure the text height
15844          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15845          */
15846         measure : function(el, text, fixedWidth){
15847             if(!shared){
15848                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15849             }
15850             shared.bind(el);
15851             shared.setFixedWidth(fixedWidth || 'auto');
15852             return shared.getSize(text);
15853         },
15854
15855         /**
15856          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15857          * the overhead of multiple calls to initialize the style properties on each measurement.
15858          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15859          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15860          * in order to accurately measure the text height
15861          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15862          */
15863         createInstance : function(el, fixedWidth){
15864             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15865         }
15866     };
15867 }();
15868
15869 /**
15870  * @class Roo.util.TextMetrics.Instance
15871  * Instance of  TextMetrics Calcuation
15872  * @constructor
15873  * Create a new TextMetrics Instance
15874  * @param {Object} bindto
15875  * @param {Boolean} fixedWidth
15876  */
15877
15878 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15879 {
15880     var ml = new Roo.Element(document.createElement('div'));
15881     document.body.appendChild(ml.dom);
15882     ml.position('absolute');
15883     ml.setLeftTop(-1000, -1000);
15884     ml.hide();
15885
15886     if(fixedWidth){
15887         ml.setWidth(fixedWidth);
15888     }
15889      
15890     var instance = {
15891         /**
15892          * Returns the size of the specified text based on the internal element's style and width properties
15893          * @param {String} text The text to measure
15894          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15895          */
15896         getSize : function(text){
15897             ml.update(text);
15898             var s = ml.getSize();
15899             ml.update('');
15900             return s;
15901         },
15902
15903         /**
15904          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15905          * that can affect the size of the rendered text
15906          * @param {String/HTMLElement} el The element, dom node or id
15907          */
15908         bind : function(el){
15909             ml.setStyle(
15910                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15911             );
15912         },
15913
15914         /**
15915          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15916          * to set a fixed width in order to accurately measure the text height.
15917          * @param {Number} width The width to set on the element
15918          */
15919         setFixedWidth : function(width){
15920             ml.setWidth(width);
15921         },
15922
15923         /**
15924          * Returns the measured width of the specified text
15925          * @param {String} text The text to measure
15926          * @return {Number} width The width in pixels
15927          */
15928         getWidth : function(text){
15929             ml.dom.style.width = 'auto';
15930             return this.getSize(text).width;
15931         },
15932
15933         /**
15934          * Returns the measured height of the specified text.  For multiline text, be sure to call
15935          * {@link #setFixedWidth} if necessary.
15936          * @param {String} text The text to measure
15937          * @return {Number} height The height in pixels
15938          */
15939         getHeight : function(text){
15940             return this.getSize(text).height;
15941         }
15942     };
15943
15944     instance.bind(bindTo);
15945
15946     return instance;
15947 };
15948
15949 // backwards compat
15950 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15951  * Based on:
15952  * Ext JS Library 1.1.1
15953  * Copyright(c) 2006-2007, Ext JS, LLC.
15954  *
15955  * Originally Released Under LGPL - original licence link has changed is not relivant.
15956  *
15957  * Fork - LGPL
15958  * <script type="text/javascript">
15959  */
15960
15961 /**
15962  * @class Roo.state.Provider
15963  * Abstract base class for state provider implementations. This class provides methods
15964  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15965  * Provider interface.
15966  */
15967 Roo.state.Provider = function(){
15968     /**
15969      * @event statechange
15970      * Fires when a state change occurs.
15971      * @param {Provider} this This state provider
15972      * @param {String} key The state key which was changed
15973      * @param {String} value The encoded value for the state
15974      */
15975     this.addEvents({
15976         "statechange": true
15977     });
15978     this.state = {};
15979     Roo.state.Provider.superclass.constructor.call(this);
15980 };
15981 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15982     /**
15983      * Returns the current value for a key
15984      * @param {String} name The key name
15985      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15986      * @return {Mixed} The state data
15987      */
15988     get : function(name, defaultValue){
15989         return typeof this.state[name] == "undefined" ?
15990             defaultValue : this.state[name];
15991     },
15992     
15993     /**
15994      * Clears a value from the state
15995      * @param {String} name The key name
15996      */
15997     clear : function(name){
15998         delete this.state[name];
15999         this.fireEvent("statechange", this, name, null);
16000     },
16001     
16002     /**
16003      * Sets the value for a key
16004      * @param {String} name The key name
16005      * @param {Mixed} value The value to set
16006      */
16007     set : function(name, value){
16008         this.state[name] = value;
16009         this.fireEvent("statechange", this, name, value);
16010     },
16011     
16012     /**
16013      * Decodes a string previously encoded with {@link #encodeValue}.
16014      * @param {String} value The value to decode
16015      * @return {Mixed} The decoded value
16016      */
16017     decodeValue : function(cookie){
16018         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16019         var matches = re.exec(unescape(cookie));
16020         if(!matches || !matches[1]) {
16021             return; // non state cookie
16022         }
16023         var type = matches[1];
16024         var v = matches[2];
16025         switch(type){
16026             case "n":
16027                 return parseFloat(v);
16028             case "d":
16029                 return new Date(Date.parse(v));
16030             case "b":
16031                 return (v == "1");
16032             case "a":
16033                 var all = [];
16034                 var values = v.split("^");
16035                 for(var i = 0, len = values.length; i < len; i++){
16036                     all.push(this.decodeValue(values[i]));
16037                 }
16038                 return all;
16039            case "o":
16040                 var all = {};
16041                 var values = v.split("^");
16042                 for(var i = 0, len = values.length; i < len; i++){
16043                     var kv = values[i].split("=");
16044                     all[kv[0]] = this.decodeValue(kv[1]);
16045                 }
16046                 return all;
16047            default:
16048                 return v;
16049         }
16050     },
16051     
16052     /**
16053      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16054      * @param {Mixed} value The value to encode
16055      * @return {String} The encoded value
16056      */
16057     encodeValue : function(v){
16058         var enc;
16059         if(typeof v == "number"){
16060             enc = "n:" + v;
16061         }else if(typeof v == "boolean"){
16062             enc = "b:" + (v ? "1" : "0");
16063         }else if(v instanceof Date){
16064             enc = "d:" + v.toGMTString();
16065         }else if(v instanceof Array){
16066             var flat = "";
16067             for(var i = 0, len = v.length; i < len; i++){
16068                 flat += this.encodeValue(v[i]);
16069                 if(i != len-1) {
16070                     flat += "^";
16071                 }
16072             }
16073             enc = "a:" + flat;
16074         }else if(typeof v == "object"){
16075             var flat = "";
16076             for(var key in v){
16077                 if(typeof v[key] != "function"){
16078                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16079                 }
16080             }
16081             enc = "o:" + flat.substring(0, flat.length-1);
16082         }else{
16083             enc = "s:" + v;
16084         }
16085         return escape(enc);        
16086     }
16087 });
16088
16089 /*
16090  * Based on:
16091  * Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  *
16094  * Originally Released Under LGPL - original licence link has changed is not relivant.
16095  *
16096  * Fork - LGPL
16097  * <script type="text/javascript">
16098  */
16099 /**
16100  * @class Roo.state.Manager
16101  * This is the global state manager. By default all components that are "state aware" check this class
16102  * for state information if you don't pass them a custom state provider. In order for this class
16103  * to be useful, it must be initialized with a provider when your application initializes.
16104  <pre><code>
16105 // in your initialization function
16106 init : function(){
16107    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16108    ...
16109    // supposed you have a {@link Roo.BorderLayout}
16110    var layout = new Roo.BorderLayout(...);
16111    layout.restoreState();
16112    // or a {Roo.BasicDialog}
16113    var dialog = new Roo.BasicDialog(...);
16114    dialog.restoreState();
16115  </code></pre>
16116  * @static
16117  */
16118 Roo.state.Manager = function(){
16119     var provider = new Roo.state.Provider();
16120     
16121     return {
16122         /**
16123          * Configures the default state provider for your application
16124          * @param {Provider} stateProvider The state provider to set
16125          */
16126         setProvider : function(stateProvider){
16127             provider = stateProvider;
16128         },
16129         
16130         /**
16131          * Returns the current value for a key
16132          * @param {String} name The key name
16133          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16134          * @return {Mixed} The state data
16135          */
16136         get : function(key, defaultValue){
16137             return provider.get(key, defaultValue);
16138         },
16139         
16140         /**
16141          * Sets the value for a key
16142          * @param {String} name The key name
16143          * @param {Mixed} value The state data
16144          */
16145          set : function(key, value){
16146             provider.set(key, value);
16147         },
16148         
16149         /**
16150          * Clears a value from the state
16151          * @param {String} name The key name
16152          */
16153         clear : function(key){
16154             provider.clear(key);
16155         },
16156         
16157         /**
16158          * Gets the currently configured state provider
16159          * @return {Provider} The state provider
16160          */
16161         getProvider : function(){
16162             return provider;
16163         }
16164     };
16165 }();
16166 /*
16167  * Based on:
16168  * Ext JS Library 1.1.1
16169  * Copyright(c) 2006-2007, Ext JS, LLC.
16170  *
16171  * Originally Released Under LGPL - original licence link has changed is not relivant.
16172  *
16173  * Fork - LGPL
16174  * <script type="text/javascript">
16175  */
16176 /**
16177  * @class Roo.state.CookieProvider
16178  * @extends Roo.state.Provider
16179  * The default Provider implementation which saves state via cookies.
16180  * <br />Usage:
16181  <pre><code>
16182    var cp = new Roo.state.CookieProvider({
16183        path: "/cgi-bin/",
16184        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16185        domain: "roojs.com"
16186    })
16187    Roo.state.Manager.setProvider(cp);
16188  </code></pre>
16189  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16190  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16191  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16192  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16193  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16194  * domain the page is running on including the 'www' like 'www.roojs.com')
16195  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16196  * @constructor
16197  * Create a new CookieProvider
16198  * @param {Object} config The configuration object
16199  */
16200 Roo.state.CookieProvider = function(config){
16201     Roo.state.CookieProvider.superclass.constructor.call(this);
16202     this.path = "/";
16203     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16204     this.domain = null;
16205     this.secure = false;
16206     Roo.apply(this, config);
16207     this.state = this.readCookies();
16208 };
16209
16210 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16211     // private
16212     set : function(name, value){
16213         if(typeof value == "undefined" || value === null){
16214             this.clear(name);
16215             return;
16216         }
16217         this.setCookie(name, value);
16218         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16219     },
16220
16221     // private
16222     clear : function(name){
16223         this.clearCookie(name);
16224         Roo.state.CookieProvider.superclass.clear.call(this, name);
16225     },
16226
16227     // private
16228     readCookies : function(){
16229         var cookies = {};
16230         var c = document.cookie + ";";
16231         var re = /\s?(.*?)=(.*?);/g;
16232         var matches;
16233         while((matches = re.exec(c)) != null){
16234             var name = matches[1];
16235             var value = matches[2];
16236             if(name && name.substring(0,3) == "ys-"){
16237                 cookies[name.substr(3)] = this.decodeValue(value);
16238             }
16239         }
16240         return cookies;
16241     },
16242
16243     // private
16244     setCookie : function(name, value){
16245         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16246            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16247            ((this.path == null) ? "" : ("; path=" + this.path)) +
16248            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16249            ((this.secure == true) ? "; secure" : "");
16250     },
16251
16252     // private
16253     clearCookie : function(name){
16254         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16255            ((this.path == null) ? "" : ("; path=" + this.path)) +
16256            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16257            ((this.secure == true) ? "; secure" : "");
16258     }
16259 });/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269  
16270
16271 /**
16272  * @class Roo.ComponentMgr
16273  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16274  * @static
16275  */
16276 Roo.ComponentMgr = function(){
16277     var all = new Roo.util.MixedCollection();
16278
16279     return {
16280         /**
16281          * Registers a component.
16282          * @param {Roo.Component} c The component
16283          */
16284         register : function(c){
16285             all.add(c);
16286         },
16287
16288         /**
16289          * Unregisters a component.
16290          * @param {Roo.Component} c The component
16291          */
16292         unregister : function(c){
16293             all.remove(c);
16294         },
16295
16296         /**
16297          * Returns a component by id
16298          * @param {String} id The component id
16299          */
16300         get : function(id){
16301             return all.get(id);
16302         },
16303
16304         /**
16305          * Registers a function that will be called when a specified component is added to ComponentMgr
16306          * @param {String} id The component id
16307          * @param {Funtction} fn The callback function
16308          * @param {Object} scope The scope of the callback
16309          */
16310         onAvailable : function(id, fn, scope){
16311             all.on("add", function(index, o){
16312                 if(o.id == id){
16313                     fn.call(scope || o, o);
16314                     all.un("add", fn, scope);
16315                 }
16316             });
16317         }
16318     };
16319 }();/*
16320  * Based on:
16321  * Ext JS Library 1.1.1
16322  * Copyright(c) 2006-2007, Ext JS, LLC.
16323  *
16324  * Originally Released Under LGPL - original licence link has changed is not relivant.
16325  *
16326  * Fork - LGPL
16327  * <script type="text/javascript">
16328  */
16329  
16330 /**
16331  * @class Roo.Component
16332  * @extends Roo.util.Observable
16333  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16334  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16335  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16336  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16337  * All visual components (widgets) that require rendering into a layout should subclass Component.
16338  * @constructor
16339  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16340  * 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
16341  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16342  */
16343 Roo.Component = function(config){
16344     config = config || {};
16345     if(config.tagName || config.dom || typeof config == "string"){ // element object
16346         config = {el: config, id: config.id || config};
16347     }
16348     this.initialConfig = config;
16349
16350     Roo.apply(this, config);
16351     this.addEvents({
16352         /**
16353          * @event disable
16354          * Fires after the component is disabled.
16355              * @param {Roo.Component} this
16356              */
16357         disable : true,
16358         /**
16359          * @event enable
16360          * Fires after the component is enabled.
16361              * @param {Roo.Component} this
16362              */
16363         enable : true,
16364         /**
16365          * @event beforeshow
16366          * Fires before the component is shown.  Return false to stop the show.
16367              * @param {Roo.Component} this
16368              */
16369         beforeshow : true,
16370         /**
16371          * @event show
16372          * Fires after the component is shown.
16373              * @param {Roo.Component} this
16374              */
16375         show : true,
16376         /**
16377          * @event beforehide
16378          * Fires before the component is hidden. Return false to stop the hide.
16379              * @param {Roo.Component} this
16380              */
16381         beforehide : true,
16382         /**
16383          * @event hide
16384          * Fires after the component is hidden.
16385              * @param {Roo.Component} this
16386              */
16387         hide : true,
16388         /**
16389          * @event beforerender
16390          * Fires before the component is rendered. Return false to stop the render.
16391              * @param {Roo.Component} this
16392              */
16393         beforerender : true,
16394         /**
16395          * @event render
16396          * Fires after the component is rendered.
16397              * @param {Roo.Component} this
16398              */
16399         render : true,
16400         /**
16401          * @event beforedestroy
16402          * Fires before the component is destroyed. Return false to stop the destroy.
16403              * @param {Roo.Component} this
16404              */
16405         beforedestroy : true,
16406         /**
16407          * @event destroy
16408          * Fires after the component is destroyed.
16409              * @param {Roo.Component} this
16410              */
16411         destroy : true
16412     });
16413     if(!this.id){
16414         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16415     }
16416     Roo.ComponentMgr.register(this);
16417     Roo.Component.superclass.constructor.call(this);
16418     this.initComponent();
16419     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16420         this.render(this.renderTo);
16421         delete this.renderTo;
16422     }
16423 };
16424
16425 /** @private */
16426 Roo.Component.AUTO_ID = 1000;
16427
16428 Roo.extend(Roo.Component, Roo.util.Observable, {
16429     /**
16430      * @scope Roo.Component.prototype
16431      * @type {Boolean}
16432      * true if this component is hidden. Read-only.
16433      */
16434     hidden : false,
16435     /**
16436      * @type {Boolean}
16437      * true if this component is disabled. Read-only.
16438      */
16439     disabled : false,
16440     /**
16441      * @type {Boolean}
16442      * true if this component has been rendered. Read-only.
16443      */
16444     rendered : false,
16445     
16446     /** @cfg {String} disableClass
16447      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16448      */
16449     disabledClass : "x-item-disabled",
16450         /** @cfg {Boolean} allowDomMove
16451          * Whether the component can move the Dom node when rendering (defaults to true).
16452          */
16453     allowDomMove : true,
16454     /** @cfg {String} hideMode (display|visibility)
16455      * How this component should hidden. Supported values are
16456      * "visibility" (css visibility), "offsets" (negative offset position) and
16457      * "display" (css display) - defaults to "display".
16458      */
16459     hideMode: 'display',
16460
16461     /** @private */
16462     ctype : "Roo.Component",
16463
16464     /**
16465      * @cfg {String} actionMode 
16466      * which property holds the element that used for  hide() / show() / disable() / enable()
16467      * default is 'el' for forms you probably want to set this to fieldEl 
16468      */
16469     actionMode : "el",
16470
16471     /** @private */
16472     getActionEl : function(){
16473         return this[this.actionMode];
16474     },
16475
16476     initComponent : Roo.emptyFn,
16477     /**
16478      * If this is a lazy rendering component, render it to its container element.
16479      * @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.
16480      */
16481     render : function(container, position){
16482         
16483         if(this.rendered){
16484             return this;
16485         }
16486         
16487         if(this.fireEvent("beforerender", this) === false){
16488             return false;
16489         }
16490         
16491         if(!container && this.el){
16492             this.el = Roo.get(this.el);
16493             container = this.el.dom.parentNode;
16494             this.allowDomMove = false;
16495         }
16496         this.container = Roo.get(container);
16497         this.rendered = true;
16498         if(position !== undefined){
16499             if(typeof position == 'number'){
16500                 position = this.container.dom.childNodes[position];
16501             }else{
16502                 position = Roo.getDom(position);
16503             }
16504         }
16505         this.onRender(this.container, position || null);
16506         if(this.cls){
16507             this.el.addClass(this.cls);
16508             delete this.cls;
16509         }
16510         if(this.style){
16511             this.el.applyStyles(this.style);
16512             delete this.style;
16513         }
16514         this.fireEvent("render", this);
16515         this.afterRender(this.container);
16516         if(this.hidden){
16517             this.hide();
16518         }
16519         if(this.disabled){
16520             this.disable();
16521         }
16522
16523         return this;
16524         
16525     },
16526
16527     /** @private */
16528     // default function is not really useful
16529     onRender : function(ct, position){
16530         if(this.el){
16531             this.el = Roo.get(this.el);
16532             if(this.allowDomMove !== false){
16533                 ct.dom.insertBefore(this.el.dom, position);
16534             }
16535         }
16536     },
16537
16538     /** @private */
16539     getAutoCreate : function(){
16540         var cfg = typeof this.autoCreate == "object" ?
16541                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16542         if(this.id && !cfg.id){
16543             cfg.id = this.id;
16544         }
16545         return cfg;
16546     },
16547
16548     /** @private */
16549     afterRender : Roo.emptyFn,
16550
16551     /**
16552      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16553      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16554      */
16555     destroy : function(){
16556         if(this.fireEvent("beforedestroy", this) !== false){
16557             this.purgeListeners();
16558             this.beforeDestroy();
16559             if(this.rendered){
16560                 this.el.removeAllListeners();
16561                 this.el.remove();
16562                 if(this.actionMode == "container"){
16563                     this.container.remove();
16564                 }
16565             }
16566             this.onDestroy();
16567             Roo.ComponentMgr.unregister(this);
16568             this.fireEvent("destroy", this);
16569         }
16570     },
16571
16572         /** @private */
16573     beforeDestroy : function(){
16574
16575     },
16576
16577         /** @private */
16578         onDestroy : function(){
16579
16580     },
16581
16582     /**
16583      * Returns the underlying {@link Roo.Element}.
16584      * @return {Roo.Element} The element
16585      */
16586     getEl : function(){
16587         return this.el;
16588     },
16589
16590     /**
16591      * Returns the id of this component.
16592      * @return {String}
16593      */
16594     getId : function(){
16595         return this.id;
16596     },
16597
16598     /**
16599      * Try to focus this component.
16600      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16601      * @return {Roo.Component} this
16602      */
16603     focus : function(selectText){
16604         if(this.rendered){
16605             this.el.focus();
16606             if(selectText === true){
16607                 this.el.dom.select();
16608             }
16609         }
16610         return this;
16611     },
16612
16613     /** @private */
16614     blur : function(){
16615         if(this.rendered){
16616             this.el.blur();
16617         }
16618         return this;
16619     },
16620
16621     /**
16622      * Disable this component.
16623      * @return {Roo.Component} this
16624      */
16625     disable : function(){
16626         if(this.rendered){
16627             this.onDisable();
16628         }
16629         this.disabled = true;
16630         this.fireEvent("disable", this);
16631         return this;
16632     },
16633
16634         // private
16635     onDisable : function(){
16636         this.getActionEl().addClass(this.disabledClass);
16637         this.el.dom.disabled = true;
16638     },
16639
16640     /**
16641      * Enable this component.
16642      * @return {Roo.Component} this
16643      */
16644     enable : function(){
16645         if(this.rendered){
16646             this.onEnable();
16647         }
16648         this.disabled = false;
16649         this.fireEvent("enable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onEnable : function(){
16655         this.getActionEl().removeClass(this.disabledClass);
16656         this.el.dom.disabled = false;
16657     },
16658
16659     /**
16660      * Convenience function for setting disabled/enabled by boolean.
16661      * @param {Boolean} disabled
16662      */
16663     setDisabled : function(disabled){
16664         this[disabled ? "disable" : "enable"]();
16665     },
16666
16667     /**
16668      * Show this component.
16669      * @return {Roo.Component} this
16670      */
16671     show: function(){
16672         if(this.fireEvent("beforeshow", this) !== false){
16673             this.hidden = false;
16674             if(this.rendered){
16675                 this.onShow();
16676             }
16677             this.fireEvent("show", this);
16678         }
16679         return this;
16680     },
16681
16682     // private
16683     onShow : function(){
16684         var ae = this.getActionEl();
16685         if(this.hideMode == 'visibility'){
16686             ae.dom.style.visibility = "visible";
16687         }else if(this.hideMode == 'offsets'){
16688             ae.removeClass('x-hidden');
16689         }else{
16690             ae.dom.style.display = "";
16691         }
16692     },
16693
16694     /**
16695      * Hide this component.
16696      * @return {Roo.Component} this
16697      */
16698     hide: function(){
16699         if(this.fireEvent("beforehide", this) !== false){
16700             this.hidden = true;
16701             if(this.rendered){
16702                 this.onHide();
16703             }
16704             this.fireEvent("hide", this);
16705         }
16706         return this;
16707     },
16708
16709     // private
16710     onHide : function(){
16711         var ae = this.getActionEl();
16712         if(this.hideMode == 'visibility'){
16713             ae.dom.style.visibility = "hidden";
16714         }else if(this.hideMode == 'offsets'){
16715             ae.addClass('x-hidden');
16716         }else{
16717             ae.dom.style.display = "none";
16718         }
16719     },
16720
16721     /**
16722      * Convenience function to hide or show this component by boolean.
16723      * @param {Boolean} visible True to show, false to hide
16724      * @return {Roo.Component} this
16725      */
16726     setVisible: function(visible){
16727         if(visible) {
16728             this.show();
16729         }else{
16730             this.hide();
16731         }
16732         return this;
16733     },
16734
16735     /**
16736      * Returns true if this component is visible.
16737      */
16738     isVisible : function(){
16739         return this.getActionEl().isVisible();
16740     },
16741
16742     cloneConfig : function(overrides){
16743         overrides = overrides || {};
16744         var id = overrides.id || Roo.id();
16745         var cfg = Roo.applyIf(overrides, this.initialConfig);
16746         cfg.id = id; // prevent dup id
16747         return new this.constructor(cfg);
16748     }
16749 });/*
16750  * Based on:
16751  * Ext JS Library 1.1.1
16752  * Copyright(c) 2006-2007, Ext JS, LLC.
16753  *
16754  * Originally Released Under LGPL - original licence link has changed is not relivant.
16755  *
16756  * Fork - LGPL
16757  * <script type="text/javascript">
16758  */
16759
16760 /**
16761  * @class Roo.BoxComponent
16762  * @extends Roo.Component
16763  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16764  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16765  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16766  * layout containers.
16767  * @constructor
16768  * @param {Roo.Element/String/Object} config The configuration options.
16769  */
16770 Roo.BoxComponent = function(config){
16771     Roo.Component.call(this, config);
16772     this.addEvents({
16773         /**
16774          * @event resize
16775          * Fires after the component is resized.
16776              * @param {Roo.Component} this
16777              * @param {Number} adjWidth The box-adjusted width that was set
16778              * @param {Number} adjHeight The box-adjusted height that was set
16779              * @param {Number} rawWidth The width that was originally specified
16780              * @param {Number} rawHeight The height that was originally specified
16781              */
16782         resize : true,
16783         /**
16784          * @event move
16785          * Fires after the component is moved.
16786              * @param {Roo.Component} this
16787              * @param {Number} x The new x position
16788              * @param {Number} y The new y position
16789              */
16790         move : true
16791     });
16792 };
16793
16794 Roo.extend(Roo.BoxComponent, Roo.Component, {
16795     // private, set in afterRender to signify that the component has been rendered
16796     boxReady : false,
16797     // private, used to defer height settings to subclasses
16798     deferHeight: false,
16799     /** @cfg {Number} width
16800      * width (optional) size of component
16801      */
16802      /** @cfg {Number} height
16803      * height (optional) size of component
16804      */
16805      
16806     /**
16807      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16808      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16809      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16810      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16811      * @return {Roo.BoxComponent} this
16812      */
16813     setSize : function(w, h){
16814         // support for standard size objects
16815         if(typeof w == 'object'){
16816             h = w.height;
16817             w = w.width;
16818         }
16819         // not rendered
16820         if(!this.boxReady){
16821             this.width = w;
16822             this.height = h;
16823             return this;
16824         }
16825
16826         // prevent recalcs when not needed
16827         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16828             return this;
16829         }
16830         this.lastSize = {width: w, height: h};
16831
16832         var adj = this.adjustSize(w, h);
16833         var aw = adj.width, ah = adj.height;
16834         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16835             var rz = this.getResizeEl();
16836             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16837                 rz.setSize(aw, ah);
16838             }else if(!this.deferHeight && ah !== undefined){
16839                 rz.setHeight(ah);
16840             }else if(aw !== undefined){
16841                 rz.setWidth(aw);
16842             }
16843             this.onResize(aw, ah, w, h);
16844             this.fireEvent('resize', this, aw, ah, w, h);
16845         }
16846         return this;
16847     },
16848
16849     /**
16850      * Gets the current size of the component's underlying element.
16851      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16852      */
16853     getSize : function(){
16854         return this.el.getSize();
16855     },
16856
16857     /**
16858      * Gets the current XY position of the component's underlying element.
16859      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16860      * @return {Array} The XY position of the element (e.g., [100, 200])
16861      */
16862     getPosition : function(local){
16863         if(local === true){
16864             return [this.el.getLeft(true), this.el.getTop(true)];
16865         }
16866         return this.xy || this.el.getXY();
16867     },
16868
16869     /**
16870      * Gets the current box measurements of the component's underlying element.
16871      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16872      * @returns {Object} box An object in the format {x, y, width, height}
16873      */
16874     getBox : function(local){
16875         var s = this.el.getSize();
16876         if(local){
16877             s.x = this.el.getLeft(true);
16878             s.y = this.el.getTop(true);
16879         }else{
16880             var xy = this.xy || this.el.getXY();
16881             s.x = xy[0];
16882             s.y = xy[1];
16883         }
16884         return s;
16885     },
16886
16887     /**
16888      * Sets the current box measurements of the component's underlying element.
16889      * @param {Object} box An object in the format {x, y, width, height}
16890      * @returns {Roo.BoxComponent} this
16891      */
16892     updateBox : function(box){
16893         this.setSize(box.width, box.height);
16894         this.setPagePosition(box.x, box.y);
16895         return this;
16896     },
16897
16898     // protected
16899     getResizeEl : function(){
16900         return this.resizeEl || this.el;
16901     },
16902
16903     // protected
16904     getPositionEl : function(){
16905         return this.positionEl || this.el;
16906     },
16907
16908     /**
16909      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16910      * This method fires the move event.
16911      * @param {Number} left The new left
16912      * @param {Number} top The new top
16913      * @returns {Roo.BoxComponent} this
16914      */
16915     setPosition : function(x, y){
16916         this.x = x;
16917         this.y = y;
16918         if(!this.boxReady){
16919             return this;
16920         }
16921         var adj = this.adjustPosition(x, y);
16922         var ax = adj.x, ay = adj.y;
16923
16924         var el = this.getPositionEl();
16925         if(ax !== undefined || ay !== undefined){
16926             if(ax !== undefined && ay !== undefined){
16927                 el.setLeftTop(ax, ay);
16928             }else if(ax !== undefined){
16929                 el.setLeft(ax);
16930             }else if(ay !== undefined){
16931                 el.setTop(ay);
16932             }
16933             this.onPosition(ax, ay);
16934             this.fireEvent('move', this, ax, ay);
16935         }
16936         return this;
16937     },
16938
16939     /**
16940      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16941      * This method fires the move event.
16942      * @param {Number} x The new x position
16943      * @param {Number} y The new y position
16944      * @returns {Roo.BoxComponent} this
16945      */
16946     setPagePosition : function(x, y){
16947         this.pageX = x;
16948         this.pageY = y;
16949         if(!this.boxReady){
16950             return;
16951         }
16952         if(x === undefined || y === undefined){ // cannot translate undefined points
16953             return;
16954         }
16955         var p = this.el.translatePoints(x, y);
16956         this.setPosition(p.left, p.top);
16957         return this;
16958     },
16959
16960     // private
16961     onRender : function(ct, position){
16962         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16963         if(this.resizeEl){
16964             this.resizeEl = Roo.get(this.resizeEl);
16965         }
16966         if(this.positionEl){
16967             this.positionEl = Roo.get(this.positionEl);
16968         }
16969     },
16970
16971     // private
16972     afterRender : function(){
16973         Roo.BoxComponent.superclass.afterRender.call(this);
16974         this.boxReady = true;
16975         this.setSize(this.width, this.height);
16976         if(this.x || this.y){
16977             this.setPosition(this.x, this.y);
16978         }
16979         if(this.pageX || this.pageY){
16980             this.setPagePosition(this.pageX, this.pageY);
16981         }
16982     },
16983
16984     /**
16985      * Force the component's size to recalculate based on the underlying element's current height and width.
16986      * @returns {Roo.BoxComponent} this
16987      */
16988     syncSize : function(){
16989         delete this.lastSize;
16990         this.setSize(this.el.getWidth(), this.el.getHeight());
16991         return this;
16992     },
16993
16994     /**
16995      * Called after the component is resized, this method is empty by default but can be implemented by any
16996      * subclass that needs to perform custom logic after a resize occurs.
16997      * @param {Number} adjWidth The box-adjusted width that was set
16998      * @param {Number} adjHeight The box-adjusted height that was set
16999      * @param {Number} rawWidth The width that was originally specified
17000      * @param {Number} rawHeight The height that was originally specified
17001      */
17002     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17003
17004     },
17005
17006     /**
17007      * Called after the component is moved, this method is empty by default but can be implemented by any
17008      * subclass that needs to perform custom logic after a move occurs.
17009      * @param {Number} x The new x position
17010      * @param {Number} y The new y position
17011      */
17012     onPosition : function(x, y){
17013
17014     },
17015
17016     // private
17017     adjustSize : function(w, h){
17018         if(this.autoWidth){
17019             w = 'auto';
17020         }
17021         if(this.autoHeight){
17022             h = 'auto';
17023         }
17024         return {width : w, height: h};
17025     },
17026
17027     // private
17028     adjustPosition : function(x, y){
17029         return {x : x, y: y};
17030     }
17031 });/*
17032  * Based on:
17033  * Ext JS Library 1.1.1
17034  * Copyright(c) 2006-2007, Ext JS, LLC.
17035  *
17036  * Originally Released Under LGPL - original licence link has changed is not relivant.
17037  *
17038  * Fork - LGPL
17039  * <script type="text/javascript">
17040  */
17041  (function(){ 
17042 /**
17043  * @class Roo.Layer
17044  * @extends Roo.Element
17045  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17046  * automatic maintaining of shadow/shim positions.
17047  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17048  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17049  * you can pass a string with a CSS class name. False turns off the shadow.
17050  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17051  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17052  * @cfg {String} cls CSS class to add to the element
17053  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17054  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17055  * @constructor
17056  * @param {Object} config An object with config options.
17057  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17058  */
17059
17060 Roo.Layer = function(config, existingEl){
17061     config = config || {};
17062     var dh = Roo.DomHelper;
17063     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17064     if(existingEl){
17065         this.dom = Roo.getDom(existingEl);
17066     }
17067     if(!this.dom){
17068         var o = config.dh || {tag: "div", cls: "x-layer"};
17069         this.dom = dh.append(pel, o);
17070     }
17071     if(config.cls){
17072         this.addClass(config.cls);
17073     }
17074     this.constrain = config.constrain !== false;
17075     this.visibilityMode = Roo.Element.VISIBILITY;
17076     if(config.id){
17077         this.id = this.dom.id = config.id;
17078     }else{
17079         this.id = Roo.id(this.dom);
17080     }
17081     this.zindex = config.zindex || this.getZIndex();
17082     this.position("absolute", this.zindex);
17083     if(config.shadow){
17084         this.shadowOffset = config.shadowOffset || 4;
17085         this.shadow = new Roo.Shadow({
17086             offset : this.shadowOffset,
17087             mode : config.shadow
17088         });
17089     }else{
17090         this.shadowOffset = 0;
17091     }
17092     this.useShim = config.shim !== false && Roo.useShims;
17093     this.useDisplay = config.useDisplay;
17094     this.hide();
17095 };
17096
17097 var supr = Roo.Element.prototype;
17098
17099 // shims are shared among layer to keep from having 100 iframes
17100 var shims = [];
17101
17102 Roo.extend(Roo.Layer, Roo.Element, {
17103
17104     getZIndex : function(){
17105         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17106     },
17107
17108     getShim : function(){
17109         if(!this.useShim){
17110             return null;
17111         }
17112         if(this.shim){
17113             return this.shim;
17114         }
17115         var shim = shims.shift();
17116         if(!shim){
17117             shim = this.createShim();
17118             shim.enableDisplayMode('block');
17119             shim.dom.style.display = 'none';
17120             shim.dom.style.visibility = 'visible';
17121         }
17122         var pn = this.dom.parentNode;
17123         if(shim.dom.parentNode != pn){
17124             pn.insertBefore(shim.dom, this.dom);
17125         }
17126         shim.setStyle('z-index', this.getZIndex()-2);
17127         this.shim = shim;
17128         return shim;
17129     },
17130
17131     hideShim : function(){
17132         if(this.shim){
17133             this.shim.setDisplayed(false);
17134             shims.push(this.shim);
17135             delete this.shim;
17136         }
17137     },
17138
17139     disableShadow : function(){
17140         if(this.shadow){
17141             this.shadowDisabled = true;
17142             this.shadow.hide();
17143             this.lastShadowOffset = this.shadowOffset;
17144             this.shadowOffset = 0;
17145         }
17146     },
17147
17148     enableShadow : function(show){
17149         if(this.shadow){
17150             this.shadowDisabled = false;
17151             this.shadowOffset = this.lastShadowOffset;
17152             delete this.lastShadowOffset;
17153             if(show){
17154                 this.sync(true);
17155             }
17156         }
17157     },
17158
17159     // private
17160     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17161     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17162     sync : function(doShow){
17163         var sw = this.shadow;
17164         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17165             var sh = this.getShim();
17166
17167             var w = this.getWidth(),
17168                 h = this.getHeight();
17169
17170             var l = this.getLeft(true),
17171                 t = this.getTop(true);
17172
17173             if(sw && !this.shadowDisabled){
17174                 if(doShow && !sw.isVisible()){
17175                     sw.show(this);
17176                 }else{
17177                     sw.realign(l, t, w, h);
17178                 }
17179                 if(sh){
17180                     if(doShow){
17181                        sh.show();
17182                     }
17183                     // fit the shim behind the shadow, so it is shimmed too
17184                     var a = sw.adjusts, s = sh.dom.style;
17185                     s.left = (Math.min(l, l+a.l))+"px";
17186                     s.top = (Math.min(t, t+a.t))+"px";
17187                     s.width = (w+a.w)+"px";
17188                     s.height = (h+a.h)+"px";
17189                 }
17190             }else if(sh){
17191                 if(doShow){
17192                    sh.show();
17193                 }
17194                 sh.setSize(w, h);
17195                 sh.setLeftTop(l, t);
17196             }
17197             
17198         }
17199     },
17200
17201     // private
17202     destroy : function(){
17203         this.hideShim();
17204         if(this.shadow){
17205             this.shadow.hide();
17206         }
17207         this.removeAllListeners();
17208         var pn = this.dom.parentNode;
17209         if(pn){
17210             pn.removeChild(this.dom);
17211         }
17212         Roo.Element.uncache(this.id);
17213     },
17214
17215     remove : function(){
17216         this.destroy();
17217     },
17218
17219     // private
17220     beginUpdate : function(){
17221         this.updating = true;
17222     },
17223
17224     // private
17225     endUpdate : function(){
17226         this.updating = false;
17227         this.sync(true);
17228     },
17229
17230     // private
17231     hideUnders : function(negOffset){
17232         if(this.shadow){
17233             this.shadow.hide();
17234         }
17235         this.hideShim();
17236     },
17237
17238     // private
17239     constrainXY : function(){
17240         if(this.constrain){
17241             var vw = Roo.lib.Dom.getViewWidth(),
17242                 vh = Roo.lib.Dom.getViewHeight();
17243             var s = Roo.get(document).getScroll();
17244
17245             var xy = this.getXY();
17246             var x = xy[0], y = xy[1];   
17247             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17248             // only move it if it needs it
17249             var moved = false;
17250             // first validate right/bottom
17251             if((x + w) > vw+s.left){
17252                 x = vw - w - this.shadowOffset;
17253                 moved = true;
17254             }
17255             if((y + h) > vh+s.top){
17256                 y = vh - h - this.shadowOffset;
17257                 moved = true;
17258             }
17259             // then make sure top/left isn't negative
17260             if(x < s.left){
17261                 x = s.left;
17262                 moved = true;
17263             }
17264             if(y < s.top){
17265                 y = s.top;
17266                 moved = true;
17267             }
17268             if(moved){
17269                 if(this.avoidY){
17270                     var ay = this.avoidY;
17271                     if(y <= ay && (y+h) >= ay){
17272                         y = ay-h-5;   
17273                     }
17274                 }
17275                 xy = [x, y];
17276                 this.storeXY(xy);
17277                 supr.setXY.call(this, xy);
17278                 this.sync();
17279             }
17280         }
17281     },
17282
17283     isVisible : function(){
17284         return this.visible;    
17285     },
17286
17287     // private
17288     showAction : function(){
17289         this.visible = true; // track visibility to prevent getStyle calls
17290         if(this.useDisplay === true){
17291             this.setDisplayed("");
17292         }else if(this.lastXY){
17293             supr.setXY.call(this, this.lastXY);
17294         }else if(this.lastLT){
17295             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17296         }
17297     },
17298
17299     // private
17300     hideAction : function(){
17301         this.visible = false;
17302         if(this.useDisplay === true){
17303             this.setDisplayed(false);
17304         }else{
17305             this.setLeftTop(-10000,-10000);
17306         }
17307     },
17308
17309     // overridden Element method
17310     setVisible : function(v, a, d, c, e){
17311         if(v){
17312             this.showAction();
17313         }
17314         if(a && v){
17315             var cb = function(){
17316                 this.sync(true);
17317                 if(c){
17318                     c();
17319                 }
17320             }.createDelegate(this);
17321             supr.setVisible.call(this, true, true, d, cb, e);
17322         }else{
17323             if(!v){
17324                 this.hideUnders(true);
17325             }
17326             var cb = c;
17327             if(a){
17328                 cb = function(){
17329                     this.hideAction();
17330                     if(c){
17331                         c();
17332                     }
17333                 }.createDelegate(this);
17334             }
17335             supr.setVisible.call(this, v, a, d, cb, e);
17336             if(v){
17337                 this.sync(true);
17338             }else if(!a){
17339                 this.hideAction();
17340             }
17341         }
17342     },
17343
17344     storeXY : function(xy){
17345         delete this.lastLT;
17346         this.lastXY = xy;
17347     },
17348
17349     storeLeftTop : function(left, top){
17350         delete this.lastXY;
17351         this.lastLT = [left, top];
17352     },
17353
17354     // private
17355     beforeFx : function(){
17356         this.beforeAction();
17357         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17358     },
17359
17360     // private
17361     afterFx : function(){
17362         Roo.Layer.superclass.afterFx.apply(this, arguments);
17363         this.sync(this.isVisible());
17364     },
17365
17366     // private
17367     beforeAction : function(){
17368         if(!this.updating && this.shadow){
17369             this.shadow.hide();
17370         }
17371     },
17372
17373     // overridden Element method
17374     setLeft : function(left){
17375         this.storeLeftTop(left, this.getTop(true));
17376         supr.setLeft.apply(this, arguments);
17377         this.sync();
17378     },
17379
17380     setTop : function(top){
17381         this.storeLeftTop(this.getLeft(true), top);
17382         supr.setTop.apply(this, arguments);
17383         this.sync();
17384     },
17385
17386     setLeftTop : function(left, top){
17387         this.storeLeftTop(left, top);
17388         supr.setLeftTop.apply(this, arguments);
17389         this.sync();
17390     },
17391
17392     setXY : function(xy, a, d, c, e){
17393         this.fixDisplay();
17394         this.beforeAction();
17395         this.storeXY(xy);
17396         var cb = this.createCB(c);
17397         supr.setXY.call(this, xy, a, d, cb, e);
17398         if(!a){
17399             cb();
17400         }
17401     },
17402
17403     // private
17404     createCB : function(c){
17405         var el = this;
17406         return function(){
17407             el.constrainXY();
17408             el.sync(true);
17409             if(c){
17410                 c();
17411             }
17412         };
17413     },
17414
17415     // overridden Element method
17416     setX : function(x, a, d, c, e){
17417         this.setXY([x, this.getY()], a, d, c, e);
17418     },
17419
17420     // overridden Element method
17421     setY : function(y, a, d, c, e){
17422         this.setXY([this.getX(), y], a, d, c, e);
17423     },
17424
17425     // overridden Element method
17426     setSize : function(w, h, a, d, c, e){
17427         this.beforeAction();
17428         var cb = this.createCB(c);
17429         supr.setSize.call(this, w, h, a, d, cb, e);
17430         if(!a){
17431             cb();
17432         }
17433     },
17434
17435     // overridden Element method
17436     setWidth : function(w, a, d, c, e){
17437         this.beforeAction();
17438         var cb = this.createCB(c);
17439         supr.setWidth.call(this, w, a, d, cb, e);
17440         if(!a){
17441             cb();
17442         }
17443     },
17444
17445     // overridden Element method
17446     setHeight : function(h, a, d, c, e){
17447         this.beforeAction();
17448         var cb = this.createCB(c);
17449         supr.setHeight.call(this, h, a, d, cb, e);
17450         if(!a){
17451             cb();
17452         }
17453     },
17454
17455     // overridden Element method
17456     setBounds : function(x, y, w, h, a, d, c, e){
17457         this.beforeAction();
17458         var cb = this.createCB(c);
17459         if(!a){
17460             this.storeXY([x, y]);
17461             supr.setXY.call(this, [x, y]);
17462             supr.setSize.call(this, w, h, a, d, cb, e);
17463             cb();
17464         }else{
17465             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17466         }
17467         return this;
17468     },
17469     
17470     /**
17471      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17472      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17473      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17474      * @param {Number} zindex The new z-index to set
17475      * @return {this} The Layer
17476      */
17477     setZIndex : function(zindex){
17478         this.zindex = zindex;
17479         this.setStyle("z-index", zindex + 2);
17480         if(this.shadow){
17481             this.shadow.setZIndex(zindex + 1);
17482         }
17483         if(this.shim){
17484             this.shim.setStyle("z-index", zindex);
17485         }
17486     }
17487 });
17488 })();/*
17489  * Original code for Roojs - LGPL
17490  * <script type="text/javascript">
17491  */
17492  
17493 /**
17494  * @class Roo.XComponent
17495  * A delayed Element creator...
17496  * Or a way to group chunks of interface together.
17497  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17498  *  used in conjunction with XComponent.build() it will create an instance of each element,
17499  *  then call addxtype() to build the User interface.
17500  * 
17501  * Mypart.xyx = new Roo.XComponent({
17502
17503     parent : 'Mypart.xyz', // empty == document.element.!!
17504     order : '001',
17505     name : 'xxxx'
17506     region : 'xxxx'
17507     disabled : function() {} 
17508      
17509     tree : function() { // return an tree of xtype declared components
17510         var MODULE = this;
17511         return 
17512         {
17513             xtype : 'NestedLayoutPanel',
17514             // technicall
17515         }
17516      ]
17517  *})
17518  *
17519  *
17520  * It can be used to build a big heiracy, with parent etc.
17521  * or you can just use this to render a single compoent to a dom element
17522  * MYPART.render(Roo.Element | String(id) | dom_element )
17523  *
17524  *
17525  * Usage patterns.
17526  *
17527  * Classic Roo
17528  *
17529  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17530  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17531  *
17532  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17533  *
17534  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17535  * - if mulitple topModules exist, the last one is defined as the top module.
17536  *
17537  * Embeded Roo
17538  * 
17539  * When the top level or multiple modules are to embedded into a existing HTML page,
17540  * the parent element can container '#id' of the element where the module will be drawn.
17541  *
17542  * Bootstrap Roo
17543  *
17544  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17545  * it relies more on a include mechanism, where sub modules are included into an outer page.
17546  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17547  * 
17548  * Bootstrap Roo Included elements
17549  *
17550  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17551  * hence confusing the component builder as it thinks there are multiple top level elements. 
17552  *
17553  * String Over-ride & Translations
17554  *
17555  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17556  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17557  * are needed. @see Roo.XComponent.overlayString  
17558  * 
17559  * 
17560  * 
17561  * @extends Roo.util.Observable
17562  * @constructor
17563  * @param cfg {Object} configuration of component
17564  * 
17565  */
17566 Roo.XComponent = function(cfg) {
17567     Roo.apply(this, cfg);
17568     this.addEvents({ 
17569         /**
17570              * @event built
17571              * Fires when this the componnt is built
17572              * @param {Roo.XComponent} c the component
17573              */
17574         'built' : true
17575         
17576     });
17577     this.region = this.region || 'center'; // default..
17578     Roo.XComponent.register(this);
17579     this.modules = false;
17580     this.el = false; // where the layout goes..
17581     
17582     
17583 }
17584 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17585     /**
17586      * @property el
17587      * The created element (with Roo.factory())
17588      * @type {Roo.Layout}
17589      */
17590     el  : false,
17591     
17592     /**
17593      * @property el
17594      * for BC  - use el in new code
17595      * @type {Roo.Layout}
17596      */
17597     panel : false,
17598     
17599     /**
17600      * @property layout
17601      * for BC  - use el in new code
17602      * @type {Roo.Layout}
17603      */
17604     layout : false,
17605     
17606      /**
17607      * @cfg {Function|boolean} disabled
17608      * If this module is disabled by some rule, return true from the funtion
17609      */
17610     disabled : false,
17611     
17612     /**
17613      * @cfg {String} parent 
17614      * Name of parent element which it get xtype added to..
17615      */
17616     parent: false,
17617     
17618     /**
17619      * @cfg {String} order
17620      * Used to set the order in which elements are created (usefull for multiple tabs)
17621      */
17622     
17623     order : false,
17624     /**
17625      * @cfg {String} name
17626      * String to display while loading.
17627      */
17628     name : false,
17629     /**
17630      * @cfg {String} region
17631      * Region to render component to (defaults to center)
17632      */
17633     region : 'center',
17634     
17635     /**
17636      * @cfg {Array} items
17637      * A single item array - the first element is the root of the tree..
17638      * It's done this way to stay compatible with the Xtype system...
17639      */
17640     items : false,
17641     
17642     /**
17643      * @property _tree
17644      * The method that retuns the tree of parts that make up this compoennt 
17645      * @type {function}
17646      */
17647     _tree  : false,
17648     
17649      /**
17650      * render
17651      * render element to dom or tree
17652      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17653      */
17654     
17655     render : function(el)
17656     {
17657         
17658         el = el || false;
17659         var hp = this.parent ? 1 : 0;
17660         Roo.debug &&  Roo.log(this);
17661         
17662         var tree = this._tree ? this._tree() : this.tree();
17663
17664         
17665         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17666             // if parent is a '#.....' string, then let's use that..
17667             var ename = this.parent.substr(1);
17668             this.parent = false;
17669             Roo.debug && Roo.log(ename);
17670             switch (ename) {
17671                 case 'bootstrap-body':
17672                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17673                         // this is the BorderLayout standard?
17674                        this.parent = { el : true };
17675                        break;
17676                     }
17677                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17678                         // need to insert stuff...
17679                         this.parent =  {
17680                              el : new Roo.bootstrap.layout.Border({
17681                                  el : document.body, 
17682                      
17683                                  center: {
17684                                     titlebar: false,
17685                                     autoScroll:false,
17686                                     closeOnTab: true,
17687                                     tabPosition: 'top',
17688                                       //resizeTabs: true,
17689                                     alwaysShowTabs: true,
17690                                     hideTabs: false
17691                                      //minTabWidth: 140
17692                                  }
17693                              })
17694                         
17695                          };
17696                          break;
17697                     }
17698                          
17699                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17700                         this.parent = { el :  new  Roo.bootstrap.Body() };
17701                         Roo.debug && Roo.log("setting el to doc body");
17702                          
17703                     } else {
17704                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17705                     }
17706                     break;
17707                 case 'bootstrap':
17708                     this.parent = { el : true};
17709                     // fall through
17710                 default:
17711                     el = Roo.get(ename);
17712                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17713                         this.parent = { el : true};
17714                     }
17715                     
17716                     break;
17717             }
17718                 
17719             
17720             if (!el && !this.parent) {
17721                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17722                 return;
17723             }
17724         }
17725         
17726         Roo.debug && Roo.log("EL:");
17727         Roo.debug && Roo.log(el);
17728         Roo.debug && Roo.log("this.parent.el:");
17729         Roo.debug && Roo.log(this.parent.el);
17730         
17731
17732         // altertive root elements ??? - we need a better way to indicate these.
17733         var is_alt = Roo.XComponent.is_alt ||
17734                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17735                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17736                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17737         
17738         
17739         
17740         if (!this.parent && is_alt) {
17741             //el = Roo.get(document.body);
17742             this.parent = { el : true };
17743         }
17744             
17745             
17746         
17747         if (!this.parent) {
17748             
17749             Roo.debug && Roo.log("no parent - creating one");
17750             
17751             el = el ? Roo.get(el) : false;      
17752             
17753             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17754                 
17755                 this.parent =  {
17756                     el : new Roo.bootstrap.layout.Border({
17757                         el: el || document.body,
17758                     
17759                         center: {
17760                             titlebar: false,
17761                             autoScroll:false,
17762                             closeOnTab: true,
17763                             tabPosition: 'top',
17764                              //resizeTabs: true,
17765                             alwaysShowTabs: false,
17766                             hideTabs: true,
17767                             minTabWidth: 140,
17768                             overflow: 'visible'
17769                          }
17770                      })
17771                 };
17772             } else {
17773             
17774                 // it's a top level one..
17775                 this.parent =  {
17776                     el : new Roo.BorderLayout(el || document.body, {
17777                         center: {
17778                             titlebar: false,
17779                             autoScroll:false,
17780                             closeOnTab: true,
17781                             tabPosition: 'top',
17782                              //resizeTabs: true,
17783                             alwaysShowTabs: el && hp? false :  true,
17784                             hideTabs: el || !hp ? true :  false,
17785                             minTabWidth: 140
17786                          }
17787                     })
17788                 };
17789             }
17790         }
17791         
17792         if (!this.parent.el) {
17793                 // probably an old style ctor, which has been disabled.
17794                 return;
17795
17796         }
17797                 // The 'tree' method is  '_tree now' 
17798             
17799         tree.region = tree.region || this.region;
17800         var is_body = false;
17801         if (this.parent.el === true) {
17802             // bootstrap... - body..
17803             if (el) {
17804                 tree.el = el;
17805             }
17806             this.parent.el = Roo.factory(tree);
17807             is_body = true;
17808         }
17809         
17810         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17811         this.fireEvent('built', this);
17812         
17813         this.panel = this.el;
17814         this.layout = this.panel.layout;
17815         this.parentLayout = this.parent.layout  || false;  
17816          
17817     }
17818     
17819 });
17820
17821 Roo.apply(Roo.XComponent, {
17822     /**
17823      * @property  hideProgress
17824      * true to disable the building progress bar.. usefull on single page renders.
17825      * @type Boolean
17826      */
17827     hideProgress : false,
17828     /**
17829      * @property  buildCompleted
17830      * True when the builder has completed building the interface.
17831      * @type Boolean
17832      */
17833     buildCompleted : false,
17834      
17835     /**
17836      * @property  topModule
17837      * the upper most module - uses document.element as it's constructor.
17838      * @type Object
17839      */
17840      
17841     topModule  : false,
17842       
17843     /**
17844      * @property  modules
17845      * array of modules to be created by registration system.
17846      * @type {Array} of Roo.XComponent
17847      */
17848     
17849     modules : [],
17850     /**
17851      * @property  elmodules
17852      * array of modules to be created by which use #ID 
17853      * @type {Array} of Roo.XComponent
17854      */
17855      
17856     elmodules : [],
17857
17858      /**
17859      * @property  is_alt
17860      * Is an alternative Root - normally used by bootstrap or other systems,
17861      *    where the top element in the tree can wrap 'body' 
17862      * @type {boolean}  (default false)
17863      */
17864      
17865     is_alt : false,
17866     /**
17867      * @property  build_from_html
17868      * Build elements from html - used by bootstrap HTML stuff 
17869      *    - this is cleared after build is completed
17870      * @type {boolean}    (default false)
17871      */
17872      
17873     build_from_html : false,
17874     /**
17875      * Register components to be built later.
17876      *
17877      * This solves the following issues
17878      * - Building is not done on page load, but after an authentication process has occured.
17879      * - Interface elements are registered on page load
17880      * - Parent Interface elements may not be loaded before child, so this handles that..
17881      * 
17882      *
17883      * example:
17884      * 
17885      * MyApp.register({
17886           order : '000001',
17887           module : 'Pman.Tab.projectMgr',
17888           region : 'center',
17889           parent : 'Pman.layout',
17890           disabled : false,  // or use a function..
17891         })
17892      
17893      * * @param {Object} details about module
17894      */
17895     register : function(obj) {
17896                 
17897         Roo.XComponent.event.fireEvent('register', obj);
17898         switch(typeof(obj.disabled) ) {
17899                 
17900             case 'undefined':
17901                 break;
17902             
17903             case 'function':
17904                 if ( obj.disabled() ) {
17905                         return;
17906                 }
17907                 break;
17908             
17909             default:
17910                 if (obj.disabled || obj.region == '#disabled') {
17911                         return;
17912                 }
17913                 break;
17914         }
17915                 
17916         this.modules.push(obj);
17917          
17918     },
17919     /**
17920      * convert a string to an object..
17921      * eg. 'AAA.BBB' -> finds AAA.BBB
17922
17923      */
17924     
17925     toObject : function(str)
17926     {
17927         if (!str || typeof(str) == 'object') {
17928             return str;
17929         }
17930         if (str.substring(0,1) == '#') {
17931             return str;
17932         }
17933
17934         var ar = str.split('.');
17935         var rt, o;
17936         rt = ar.shift();
17937             /** eval:var:o */
17938         try {
17939             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17940         } catch (e) {
17941             throw "Module not found : " + str;
17942         }
17943         
17944         if (o === false) {
17945             throw "Module not found : " + str;
17946         }
17947         Roo.each(ar, function(e) {
17948             if (typeof(o[e]) == 'undefined') {
17949                 throw "Module not found : " + str;
17950             }
17951             o = o[e];
17952         });
17953         
17954         return o;
17955         
17956     },
17957     
17958     
17959     /**
17960      * move modules into their correct place in the tree..
17961      * 
17962      */
17963     preBuild : function ()
17964     {
17965         var _t = this;
17966         Roo.each(this.modules , function (obj)
17967         {
17968             Roo.XComponent.event.fireEvent('beforebuild', obj);
17969             
17970             var opar = obj.parent;
17971             try { 
17972                 obj.parent = this.toObject(opar);
17973             } catch(e) {
17974                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17975                 return;
17976             }
17977             
17978             if (!obj.parent) {
17979                 Roo.debug && Roo.log("GOT top level module");
17980                 Roo.debug && Roo.log(obj);
17981                 obj.modules = new Roo.util.MixedCollection(false, 
17982                     function(o) { return o.order + '' }
17983                 );
17984                 this.topModule = obj;
17985                 return;
17986             }
17987                         // parent is a string (usually a dom element name..)
17988             if (typeof(obj.parent) == 'string') {
17989                 this.elmodules.push(obj);
17990                 return;
17991             }
17992             if (obj.parent.constructor != Roo.XComponent) {
17993                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17994             }
17995             if (!obj.parent.modules) {
17996                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17997                     function(o) { return o.order + '' }
17998                 );
17999             }
18000             if (obj.parent.disabled) {
18001                 obj.disabled = true;
18002             }
18003             obj.parent.modules.add(obj);
18004         }, this);
18005     },
18006     
18007      /**
18008      * make a list of modules to build.
18009      * @return {Array} list of modules. 
18010      */ 
18011     
18012     buildOrder : function()
18013     {
18014         var _this = this;
18015         var cmp = function(a,b) {   
18016             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18017         };
18018         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18019             throw "No top level modules to build";
18020         }
18021         
18022         // make a flat list in order of modules to build.
18023         var mods = this.topModule ? [ this.topModule ] : [];
18024                 
18025         
18026         // elmodules (is a list of DOM based modules )
18027         Roo.each(this.elmodules, function(e) {
18028             mods.push(e);
18029             if (!this.topModule &&
18030                 typeof(e.parent) == 'string' &&
18031                 e.parent.substring(0,1) == '#' &&
18032                 Roo.get(e.parent.substr(1))
18033                ) {
18034                 
18035                 _this.topModule = e;
18036             }
18037             
18038         });
18039
18040         
18041         // add modules to their parents..
18042         var addMod = function(m) {
18043             Roo.debug && Roo.log("build Order: add: " + m.name);
18044                 
18045             mods.push(m);
18046             if (m.modules && !m.disabled) {
18047                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18048                 m.modules.keySort('ASC',  cmp );
18049                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18050     
18051                 m.modules.each(addMod);
18052             } else {
18053                 Roo.debug && Roo.log("build Order: no child modules");
18054             }
18055             // not sure if this is used any more..
18056             if (m.finalize) {
18057                 m.finalize.name = m.name + " (clean up) ";
18058                 mods.push(m.finalize);
18059             }
18060             
18061         }
18062         if (this.topModule && this.topModule.modules) { 
18063             this.topModule.modules.keySort('ASC',  cmp );
18064             this.topModule.modules.each(addMod);
18065         } 
18066         return mods;
18067     },
18068     
18069      /**
18070      * Build the registered modules.
18071      * @param {Object} parent element.
18072      * @param {Function} optional method to call after module has been added.
18073      * 
18074      */ 
18075    
18076     build : function(opts) 
18077     {
18078         
18079         if (typeof(opts) != 'undefined') {
18080             Roo.apply(this,opts);
18081         }
18082         
18083         this.preBuild();
18084         var mods = this.buildOrder();
18085       
18086         //this.allmods = mods;
18087         //Roo.debug && Roo.log(mods);
18088         //return;
18089         if (!mods.length) { // should not happen
18090             throw "NO modules!!!";
18091         }
18092         
18093         
18094         var msg = "Building Interface...";
18095         // flash it up as modal - so we store the mask!?
18096         if (!this.hideProgress && Roo.MessageBox) {
18097             Roo.MessageBox.show({ title: 'loading' });
18098             Roo.MessageBox.show({
18099                title: "Please wait...",
18100                msg: msg,
18101                width:450,
18102                progress:true,
18103                buttons : false,
18104                closable:false,
18105                modal: false
18106               
18107             });
18108         }
18109         var total = mods.length;
18110         
18111         var _this = this;
18112         var progressRun = function() {
18113             if (!mods.length) {
18114                 Roo.debug && Roo.log('hide?');
18115                 if (!this.hideProgress && Roo.MessageBox) {
18116                     Roo.MessageBox.hide();
18117                 }
18118                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18119                 
18120                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18121                 
18122                 // THE END...
18123                 return false;   
18124             }
18125             
18126             var m = mods.shift();
18127             
18128             
18129             Roo.debug && Roo.log(m);
18130             // not sure if this is supported any more.. - modules that are are just function
18131             if (typeof(m) == 'function') { 
18132                 m.call(this);
18133                 return progressRun.defer(10, _this);
18134             } 
18135             
18136             
18137             msg = "Building Interface " + (total  - mods.length) + 
18138                     " of " + total + 
18139                     (m.name ? (' - ' + m.name) : '');
18140                         Roo.debug && Roo.log(msg);
18141             if (!_this.hideProgress &&  Roo.MessageBox) { 
18142                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18143             }
18144             
18145          
18146             // is the module disabled?
18147             var disabled = (typeof(m.disabled) == 'function') ?
18148                 m.disabled.call(m.module.disabled) : m.disabled;    
18149             
18150             
18151             if (disabled) {
18152                 return progressRun(); // we do not update the display!
18153             }
18154             
18155             // now build 
18156             
18157                         
18158                         
18159             m.render();
18160             // it's 10 on top level, and 1 on others??? why...
18161             return progressRun.defer(10, _this);
18162              
18163         }
18164         progressRun.defer(1, _this);
18165      
18166         
18167         
18168     },
18169     /**
18170      * Overlay a set of modified strings onto a component
18171      * This is dependant on our builder exporting the strings and 'named strings' elements.
18172      * 
18173      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18174      * @param {Object} associative array of 'named' string and it's new value.
18175      * 
18176      */
18177         overlayStrings : function( component, strings )
18178     {
18179         if (typeof(component['_named_strings']) == 'undefined') {
18180             throw "ERROR: component does not have _named_strings";
18181         }
18182         for ( var k in strings ) {
18183             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18184             if (md !== false) {
18185                 component['_strings'][md] = strings[k];
18186             } else {
18187                 Roo.log('could not find named string: ' + k + ' in');
18188                 Roo.log(component);
18189             }
18190             
18191         }
18192         
18193     },
18194     
18195         
18196         /**
18197          * Event Object.
18198          *
18199          *
18200          */
18201         event: false, 
18202     /**
18203          * wrapper for event.on - aliased later..  
18204          * Typically use to register a event handler for register:
18205          *
18206          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18207          *
18208          */
18209     on : false
18210    
18211     
18212     
18213 });
18214
18215 Roo.XComponent.event = new Roo.util.Observable({
18216                 events : { 
18217                         /**
18218                          * @event register
18219                          * Fires when an Component is registered,
18220                          * set the disable property on the Component to stop registration.
18221                          * @param {Roo.XComponent} c the component being registerd.
18222                          * 
18223                          */
18224                         'register' : true,
18225             /**
18226                          * @event beforebuild
18227                          * Fires before each Component is built
18228                          * can be used to apply permissions.
18229                          * @param {Roo.XComponent} c the component being registerd.
18230                          * 
18231                          */
18232                         'beforebuild' : true,
18233                         /**
18234                          * @event buildcomplete
18235                          * Fires on the top level element when all elements have been built
18236                          * @param {Roo.XComponent} the top level component.
18237                          */
18238                         'buildcomplete' : true
18239                         
18240                 }
18241 });
18242
18243 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18244  //
18245  /**
18246  * marked - a markdown parser
18247  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18248  * https://github.com/chjj/marked
18249  */
18250
18251
18252 /**
18253  *
18254  * Roo.Markdown - is a very crude wrapper around marked..
18255  *
18256  * usage:
18257  * 
18258  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18259  * 
18260  * Note: move the sample code to the bottom of this
18261  * file before uncommenting it.
18262  *
18263  */
18264
18265 Roo.Markdown = {};
18266 Roo.Markdown.toHtml = function(text) {
18267     
18268     var c = new Roo.Markdown.marked.setOptions({
18269             renderer: new Roo.Markdown.marked.Renderer(),
18270             gfm: true,
18271             tables: true,
18272             breaks: false,
18273             pedantic: false,
18274             sanitize: false,
18275             smartLists: true,
18276             smartypants: false
18277           });
18278     // A FEW HACKS!!?
18279     
18280     text = text.replace(/\\\n/g,' ');
18281     return Roo.Markdown.marked(text);
18282 };
18283 //
18284 // converter
18285 //
18286 // Wraps all "globals" so that the only thing
18287 // exposed is makeHtml().
18288 //
18289 (function() {
18290     
18291      /**
18292          * eval:var:escape
18293          * eval:var:unescape
18294          * eval:var:replace
18295          */
18296       
18297     /**
18298      * Helpers
18299      */
18300     
18301     var escape = function (html, encode) {
18302       return html
18303         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18304         .replace(/</g, '&lt;')
18305         .replace(/>/g, '&gt;')
18306         .replace(/"/g, '&quot;')
18307         .replace(/'/g, '&#39;');
18308     }
18309     
18310     var unescape = function (html) {
18311         // explicitly match decimal, hex, and named HTML entities 
18312       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18313         n = n.toLowerCase();
18314         if (n === 'colon') { return ':'; }
18315         if (n.charAt(0) === '#') {
18316           return n.charAt(1) === 'x'
18317             ? String.fromCharCode(parseInt(n.substring(2), 16))
18318             : String.fromCharCode(+n.substring(1));
18319         }
18320         return '';
18321       });
18322     }
18323     
18324     var replace = function (regex, opt) {
18325       regex = regex.source;
18326       opt = opt || '';
18327       return function self(name, val) {
18328         if (!name) { return new RegExp(regex, opt); }
18329         val = val.source || val;
18330         val = val.replace(/(^|[^\[])\^/g, '$1');
18331         regex = regex.replace(name, val);
18332         return self;
18333       };
18334     }
18335
18336
18337          /**
18338          * eval:var:noop
18339     */
18340     var noop = function () {}
18341     noop.exec = noop;
18342     
18343          /**
18344          * eval:var:merge
18345     */
18346     var merge = function (obj) {
18347       var i = 1
18348         , target
18349         , key;
18350     
18351       for (; i < arguments.length; i++) {
18352         target = arguments[i];
18353         for (key in target) {
18354           if (Object.prototype.hasOwnProperty.call(target, key)) {
18355             obj[key] = target[key];
18356           }
18357         }
18358       }
18359     
18360       return obj;
18361     }
18362     
18363     
18364     /**
18365      * Block-Level Grammar
18366      */
18367     
18368     
18369     
18370     
18371     var block = {
18372       newline: /^\n+/,
18373       code: /^( {4}[^\n]+\n*)+/,
18374       fences: noop,
18375       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18376       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18377       nptable: noop,
18378       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18379       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18380       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18381       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18382       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18383       table: noop,
18384       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18385       text: /^[^\n]+/
18386     };
18387     
18388     block.bullet = /(?:[*+-]|\d+\.)/;
18389     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18390     block.item = replace(block.item, 'gm')
18391       (/bull/g, block.bullet)
18392       ();
18393     
18394     block.list = replace(block.list)
18395       (/bull/g, block.bullet)
18396       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18397       ('def', '\\n+(?=' + block.def.source + ')')
18398       ();
18399     
18400     block.blockquote = replace(block.blockquote)
18401       ('def', block.def)
18402       ();
18403     
18404     block._tag = '(?!(?:'
18405       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18406       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18407       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18408     
18409     block.html = replace(block.html)
18410       ('comment', /<!--[\s\S]*?-->/)
18411       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18412       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18413       (/tag/g, block._tag)
18414       ();
18415     
18416     block.paragraph = replace(block.paragraph)
18417       ('hr', block.hr)
18418       ('heading', block.heading)
18419       ('lheading', block.lheading)
18420       ('blockquote', block.blockquote)
18421       ('tag', '<' + block._tag)
18422       ('def', block.def)
18423       ();
18424     
18425     /**
18426      * Normal Block Grammar
18427      */
18428     
18429     block.normal = merge({}, block);
18430     
18431     /**
18432      * GFM Block Grammar
18433      */
18434     
18435     block.gfm = merge({}, block.normal, {
18436       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18437       paragraph: /^/,
18438       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18439     });
18440     
18441     block.gfm.paragraph = replace(block.paragraph)
18442       ('(?!', '(?!'
18443         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18444         + block.list.source.replace('\\1', '\\3') + '|')
18445       ();
18446     
18447     /**
18448      * GFM + Tables Block Grammar
18449      */
18450     
18451     block.tables = merge({}, block.gfm, {
18452       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18453       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18454     });
18455     
18456     /**
18457      * Block Lexer
18458      */
18459     
18460     var Lexer = function (options) {
18461       this.tokens = [];
18462       this.tokens.links = {};
18463       this.options = options || marked.defaults;
18464       this.rules = block.normal;
18465     
18466       if (this.options.gfm) {
18467         if (this.options.tables) {
18468           this.rules = block.tables;
18469         } else {
18470           this.rules = block.gfm;
18471         }
18472       }
18473     }
18474     
18475     /**
18476      * Expose Block Rules
18477      */
18478     
18479     Lexer.rules = block;
18480     
18481     /**
18482      * Static Lex Method
18483      */
18484     
18485     Lexer.lex = function(src, options) {
18486       var lexer = new Lexer(options);
18487       return lexer.lex(src);
18488     };
18489     
18490     /**
18491      * Preprocessing
18492      */
18493     
18494     Lexer.prototype.lex = function(src) {
18495       src = src
18496         .replace(/\r\n|\r/g, '\n')
18497         .replace(/\t/g, '    ')
18498         .replace(/\u00a0/g, ' ')
18499         .replace(/\u2424/g, '\n');
18500     
18501       return this.token(src, true);
18502     };
18503     
18504     /**
18505      * Lexing
18506      */
18507     
18508     Lexer.prototype.token = function(src, top, bq) {
18509       var src = src.replace(/^ +$/gm, '')
18510         , next
18511         , loose
18512         , cap
18513         , bull
18514         , b
18515         , item
18516         , space
18517         , i
18518         , l;
18519     
18520       while (src) {
18521         // newline
18522         if (cap = this.rules.newline.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           if (cap[0].length > 1) {
18525             this.tokens.push({
18526               type: 'space'
18527             });
18528           }
18529         }
18530     
18531         // code
18532         if (cap = this.rules.code.exec(src)) {
18533           src = src.substring(cap[0].length);
18534           cap = cap[0].replace(/^ {4}/gm, '');
18535           this.tokens.push({
18536             type: 'code',
18537             text: !this.options.pedantic
18538               ? cap.replace(/\n+$/, '')
18539               : cap
18540           });
18541           continue;
18542         }
18543     
18544         // fences (gfm)
18545         if (cap = this.rules.fences.exec(src)) {
18546           src = src.substring(cap[0].length);
18547           this.tokens.push({
18548             type: 'code',
18549             lang: cap[2],
18550             text: cap[3] || ''
18551           });
18552           continue;
18553         }
18554     
18555         // heading
18556         if (cap = this.rules.heading.exec(src)) {
18557           src = src.substring(cap[0].length);
18558           this.tokens.push({
18559             type: 'heading',
18560             depth: cap[1].length,
18561             text: cap[2]
18562           });
18563           continue;
18564         }
18565     
18566         // table no leading pipe (gfm)
18567         if (top && (cap = this.rules.nptable.exec(src))) {
18568           src = src.substring(cap[0].length);
18569     
18570           item = {
18571             type: 'table',
18572             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18573             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18574             cells: cap[3].replace(/\n$/, '').split('\n')
18575           };
18576     
18577           for (i = 0; i < item.align.length; i++) {
18578             if (/^ *-+: *$/.test(item.align[i])) {
18579               item.align[i] = 'right';
18580             } else if (/^ *:-+: *$/.test(item.align[i])) {
18581               item.align[i] = 'center';
18582             } else if (/^ *:-+ *$/.test(item.align[i])) {
18583               item.align[i] = 'left';
18584             } else {
18585               item.align[i] = null;
18586             }
18587           }
18588     
18589           for (i = 0; i < item.cells.length; i++) {
18590             item.cells[i] = item.cells[i].split(/ *\| */);
18591           }
18592     
18593           this.tokens.push(item);
18594     
18595           continue;
18596         }
18597     
18598         // lheading
18599         if (cap = this.rules.lheading.exec(src)) {
18600           src = src.substring(cap[0].length);
18601           this.tokens.push({
18602             type: 'heading',
18603             depth: cap[2] === '=' ? 1 : 2,
18604             text: cap[1]
18605           });
18606           continue;
18607         }
18608     
18609         // hr
18610         if (cap = this.rules.hr.exec(src)) {
18611           src = src.substring(cap[0].length);
18612           this.tokens.push({
18613             type: 'hr'
18614           });
18615           continue;
18616         }
18617     
18618         // blockquote
18619         if (cap = this.rules.blockquote.exec(src)) {
18620           src = src.substring(cap[0].length);
18621     
18622           this.tokens.push({
18623             type: 'blockquote_start'
18624           });
18625     
18626           cap = cap[0].replace(/^ *> ?/gm, '');
18627     
18628           // Pass `top` to keep the current
18629           // "toplevel" state. This is exactly
18630           // how markdown.pl works.
18631           this.token(cap, top, true);
18632     
18633           this.tokens.push({
18634             type: 'blockquote_end'
18635           });
18636     
18637           continue;
18638         }
18639     
18640         // list
18641         if (cap = this.rules.list.exec(src)) {
18642           src = src.substring(cap[0].length);
18643           bull = cap[2];
18644     
18645           this.tokens.push({
18646             type: 'list_start',
18647             ordered: bull.length > 1
18648           });
18649     
18650           // Get each top-level item.
18651           cap = cap[0].match(this.rules.item);
18652     
18653           next = false;
18654           l = cap.length;
18655           i = 0;
18656     
18657           for (; i < l; i++) {
18658             item = cap[i];
18659     
18660             // Remove the list item's bullet
18661             // so it is seen as the next token.
18662             space = item.length;
18663             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18664     
18665             // Outdent whatever the
18666             // list item contains. Hacky.
18667             if (~item.indexOf('\n ')) {
18668               space -= item.length;
18669               item = !this.options.pedantic
18670                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18671                 : item.replace(/^ {1,4}/gm, '');
18672             }
18673     
18674             // Determine whether the next list item belongs here.
18675             // Backpedal if it does not belong in this list.
18676             if (this.options.smartLists && i !== l - 1) {
18677               b = block.bullet.exec(cap[i + 1])[0];
18678               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18679                 src = cap.slice(i + 1).join('\n') + src;
18680                 i = l - 1;
18681               }
18682             }
18683     
18684             // Determine whether item is loose or not.
18685             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18686             // for discount behavior.
18687             loose = next || /\n\n(?!\s*$)/.test(item);
18688             if (i !== l - 1) {
18689               next = item.charAt(item.length - 1) === '\n';
18690               if (!loose) { loose = next; }
18691             }
18692     
18693             this.tokens.push({
18694               type: loose
18695                 ? 'loose_item_start'
18696                 : 'list_item_start'
18697             });
18698     
18699             // Recurse.
18700             this.token(item, false, bq);
18701     
18702             this.tokens.push({
18703               type: 'list_item_end'
18704             });
18705           }
18706     
18707           this.tokens.push({
18708             type: 'list_end'
18709           });
18710     
18711           continue;
18712         }
18713     
18714         // html
18715         if (cap = this.rules.html.exec(src)) {
18716           src = src.substring(cap[0].length);
18717           this.tokens.push({
18718             type: this.options.sanitize
18719               ? 'paragraph'
18720               : 'html',
18721             pre: !this.options.sanitizer
18722               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18723             text: cap[0]
18724           });
18725           continue;
18726         }
18727     
18728         // def
18729         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18730           src = src.substring(cap[0].length);
18731           this.tokens.links[cap[1].toLowerCase()] = {
18732             href: cap[2],
18733             title: cap[3]
18734           };
18735           continue;
18736         }
18737     
18738         // table (gfm)
18739         if (top && (cap = this.rules.table.exec(src))) {
18740           src = src.substring(cap[0].length);
18741     
18742           item = {
18743             type: 'table',
18744             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18745             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18746             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18747           };
18748     
18749           for (i = 0; i < item.align.length; i++) {
18750             if (/^ *-+: *$/.test(item.align[i])) {
18751               item.align[i] = 'right';
18752             } else if (/^ *:-+: *$/.test(item.align[i])) {
18753               item.align[i] = 'center';
18754             } else if (/^ *:-+ *$/.test(item.align[i])) {
18755               item.align[i] = 'left';
18756             } else {
18757               item.align[i] = null;
18758             }
18759           }
18760     
18761           for (i = 0; i < item.cells.length; i++) {
18762             item.cells[i] = item.cells[i]
18763               .replace(/^ *\| *| *\| *$/g, '')
18764               .split(/ *\| */);
18765           }
18766     
18767           this.tokens.push(item);
18768     
18769           continue;
18770         }
18771     
18772         // top-level paragraph
18773         if (top && (cap = this.rules.paragraph.exec(src))) {
18774           src = src.substring(cap[0].length);
18775           this.tokens.push({
18776             type: 'paragraph',
18777             text: cap[1].charAt(cap[1].length - 1) === '\n'
18778               ? cap[1].slice(0, -1)
18779               : cap[1]
18780           });
18781           continue;
18782         }
18783     
18784         // text
18785         if (cap = this.rules.text.exec(src)) {
18786           // Top-level should never reach here.
18787           src = src.substring(cap[0].length);
18788           this.tokens.push({
18789             type: 'text',
18790             text: cap[0]
18791           });
18792           continue;
18793         }
18794     
18795         if (src) {
18796           throw new
18797             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18798         }
18799       }
18800     
18801       return this.tokens;
18802     };
18803     
18804     /**
18805      * Inline-Level Grammar
18806      */
18807     
18808     var inline = {
18809       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18810       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18811       url: noop,
18812       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18813       link: /^!?\[(inside)\]\(href\)/,
18814       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18815       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18816       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18817       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18818       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18819       br: /^ {2,}\n(?!\s*$)/,
18820       del: noop,
18821       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18822     };
18823     
18824     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18825     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18826     
18827     inline.link = replace(inline.link)
18828       ('inside', inline._inside)
18829       ('href', inline._href)
18830       ();
18831     
18832     inline.reflink = replace(inline.reflink)
18833       ('inside', inline._inside)
18834       ();
18835     
18836     /**
18837      * Normal Inline Grammar
18838      */
18839     
18840     inline.normal = merge({}, inline);
18841     
18842     /**
18843      * Pedantic Inline Grammar
18844      */
18845     
18846     inline.pedantic = merge({}, inline.normal, {
18847       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18848       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18849     });
18850     
18851     /**
18852      * GFM Inline Grammar
18853      */
18854     
18855     inline.gfm = merge({}, inline.normal, {
18856       escape: replace(inline.escape)('])', '~|])')(),
18857       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18858       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18859       text: replace(inline.text)
18860         (']|', '~]|')
18861         ('|', '|https?://|')
18862         ()
18863     });
18864     
18865     /**
18866      * GFM + Line Breaks Inline Grammar
18867      */
18868     
18869     inline.breaks = merge({}, inline.gfm, {
18870       br: replace(inline.br)('{2,}', '*')(),
18871       text: replace(inline.gfm.text)('{2,}', '*')()
18872     });
18873     
18874     /**
18875      * Inline Lexer & Compiler
18876      */
18877     
18878     var InlineLexer  = function (links, options) {
18879       this.options = options || marked.defaults;
18880       this.links = links;
18881       this.rules = inline.normal;
18882       this.renderer = this.options.renderer || new Renderer;
18883       this.renderer.options = this.options;
18884     
18885       if (!this.links) {
18886         throw new
18887           Error('Tokens array requires a `links` property.');
18888       }
18889     
18890       if (this.options.gfm) {
18891         if (this.options.breaks) {
18892           this.rules = inline.breaks;
18893         } else {
18894           this.rules = inline.gfm;
18895         }
18896       } else if (this.options.pedantic) {
18897         this.rules = inline.pedantic;
18898       }
18899     }
18900     
18901     /**
18902      * Expose Inline Rules
18903      */
18904     
18905     InlineLexer.rules = inline;
18906     
18907     /**
18908      * Static Lexing/Compiling Method
18909      */
18910     
18911     InlineLexer.output = function(src, links, options) {
18912       var inline = new InlineLexer(links, options);
18913       return inline.output(src);
18914     };
18915     
18916     /**
18917      * Lexing/Compiling
18918      */
18919     
18920     InlineLexer.prototype.output = function(src) {
18921       var out = ''
18922         , link
18923         , text
18924         , href
18925         , cap;
18926     
18927       while (src) {
18928         // escape
18929         if (cap = this.rules.escape.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += cap[1];
18932           continue;
18933         }
18934     
18935         // autolink
18936         if (cap = this.rules.autolink.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           if (cap[2] === '@') {
18939             text = cap[1].charAt(6) === ':'
18940               ? this.mangle(cap[1].substring(7))
18941               : this.mangle(cap[1]);
18942             href = this.mangle('mailto:') + text;
18943           } else {
18944             text = escape(cap[1]);
18945             href = text;
18946           }
18947           out += this.renderer.link(href, null, text);
18948           continue;
18949         }
18950     
18951         // url (gfm)
18952         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18953           src = src.substring(cap[0].length);
18954           text = escape(cap[1]);
18955           href = text;
18956           out += this.renderer.link(href, null, text);
18957           continue;
18958         }
18959     
18960         // tag
18961         if (cap = this.rules.tag.exec(src)) {
18962           if (!this.inLink && /^<a /i.test(cap[0])) {
18963             this.inLink = true;
18964           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18965             this.inLink = false;
18966           }
18967           src = src.substring(cap[0].length);
18968           out += this.options.sanitize
18969             ? this.options.sanitizer
18970               ? this.options.sanitizer(cap[0])
18971               : escape(cap[0])
18972             : cap[0];
18973           continue;
18974         }
18975     
18976         // link
18977         if (cap = this.rules.link.exec(src)) {
18978           src = src.substring(cap[0].length);
18979           this.inLink = true;
18980           out += this.outputLink(cap, {
18981             href: cap[2],
18982             title: cap[3]
18983           });
18984           this.inLink = false;
18985           continue;
18986         }
18987     
18988         // reflink, nolink
18989         if ((cap = this.rules.reflink.exec(src))
18990             || (cap = this.rules.nolink.exec(src))) {
18991           src = src.substring(cap[0].length);
18992           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18993           link = this.links[link.toLowerCase()];
18994           if (!link || !link.href) {
18995             out += cap[0].charAt(0);
18996             src = cap[0].substring(1) + src;
18997             continue;
18998           }
18999           this.inLink = true;
19000           out += this.outputLink(cap, link);
19001           this.inLink = false;
19002           continue;
19003         }
19004     
19005         // strong
19006         if (cap = this.rules.strong.exec(src)) {
19007           src = src.substring(cap[0].length);
19008           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19009           continue;
19010         }
19011     
19012         // em
19013         if (cap = this.rules.em.exec(src)) {
19014           src = src.substring(cap[0].length);
19015           out += this.renderer.em(this.output(cap[2] || cap[1]));
19016           continue;
19017         }
19018     
19019         // code
19020         if (cap = this.rules.code.exec(src)) {
19021           src = src.substring(cap[0].length);
19022           out += this.renderer.codespan(escape(cap[2], true));
19023           continue;
19024         }
19025     
19026         // br
19027         if (cap = this.rules.br.exec(src)) {
19028           src = src.substring(cap[0].length);
19029           out += this.renderer.br();
19030           continue;
19031         }
19032     
19033         // del (gfm)
19034         if (cap = this.rules.del.exec(src)) {
19035           src = src.substring(cap[0].length);
19036           out += this.renderer.del(this.output(cap[1]));
19037           continue;
19038         }
19039     
19040         // text
19041         if (cap = this.rules.text.exec(src)) {
19042           src = src.substring(cap[0].length);
19043           out += this.renderer.text(escape(this.smartypants(cap[0])));
19044           continue;
19045         }
19046     
19047         if (src) {
19048           throw new
19049             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19050         }
19051       }
19052     
19053       return out;
19054     };
19055     
19056     /**
19057      * Compile Link
19058      */
19059     
19060     InlineLexer.prototype.outputLink = function(cap, link) {
19061       var href = escape(link.href)
19062         , title = link.title ? escape(link.title) : null;
19063     
19064       return cap[0].charAt(0) !== '!'
19065         ? this.renderer.link(href, title, this.output(cap[1]))
19066         : this.renderer.image(href, title, escape(cap[1]));
19067     };
19068     
19069     /**
19070      * Smartypants Transformations
19071      */
19072     
19073     InlineLexer.prototype.smartypants = function(text) {
19074       if (!this.options.smartypants)  { return text; }
19075       return text
19076         // em-dashes
19077         .replace(/---/g, '\u2014')
19078         // en-dashes
19079         .replace(/--/g, '\u2013')
19080         // opening singles
19081         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19082         // closing singles & apostrophes
19083         .replace(/'/g, '\u2019')
19084         // opening doubles
19085         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19086         // closing doubles
19087         .replace(/"/g, '\u201d')
19088         // ellipses
19089         .replace(/\.{3}/g, '\u2026');
19090     };
19091     
19092     /**
19093      * Mangle Links
19094      */
19095     
19096     InlineLexer.prototype.mangle = function(text) {
19097       if (!this.options.mangle) { return text; }
19098       var out = ''
19099         , l = text.length
19100         , i = 0
19101         , ch;
19102     
19103       for (; i < l; i++) {
19104         ch = text.charCodeAt(i);
19105         if (Math.random() > 0.5) {
19106           ch = 'x' + ch.toString(16);
19107         }
19108         out += '&#' + ch + ';';
19109       }
19110     
19111       return out;
19112     };
19113     
19114     /**
19115      * Renderer
19116      */
19117     
19118      /**
19119          * eval:var:Renderer
19120     */
19121     
19122     var Renderer   = function (options) {
19123       this.options = options || {};
19124     }
19125     
19126     Renderer.prototype.code = function(code, lang, escaped) {
19127       if (this.options.highlight) {
19128         var out = this.options.highlight(code, lang);
19129         if (out != null && out !== code) {
19130           escaped = true;
19131           code = out;
19132         }
19133       } else {
19134             // hack!!! - it's already escapeD?
19135             escaped = true;
19136       }
19137     
19138       if (!lang) {
19139         return '<pre><code>'
19140           + (escaped ? code : escape(code, true))
19141           + '\n</code></pre>';
19142       }
19143     
19144       return '<pre><code class="'
19145         + this.options.langPrefix
19146         + escape(lang, true)
19147         + '">'
19148         + (escaped ? code : escape(code, true))
19149         + '\n</code></pre>\n';
19150     };
19151     
19152     Renderer.prototype.blockquote = function(quote) {
19153       return '<blockquote>\n' + quote + '</blockquote>\n';
19154     };
19155     
19156     Renderer.prototype.html = function(html) {
19157       return html;
19158     };
19159     
19160     Renderer.prototype.heading = function(text, level, raw) {
19161       return '<h'
19162         + level
19163         + ' id="'
19164         + this.options.headerPrefix
19165         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19166         + '">'
19167         + text
19168         + '</h'
19169         + level
19170         + '>\n';
19171     };
19172     
19173     Renderer.prototype.hr = function() {
19174       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19175     };
19176     
19177     Renderer.prototype.list = function(body, ordered) {
19178       var type = ordered ? 'ol' : 'ul';
19179       return '<' + type + '>\n' + body + '</' + type + '>\n';
19180     };
19181     
19182     Renderer.prototype.listitem = function(text) {
19183       return '<li>' + text + '</li>\n';
19184     };
19185     
19186     Renderer.prototype.paragraph = function(text) {
19187       return '<p>' + text + '</p>\n';
19188     };
19189     
19190     Renderer.prototype.table = function(header, body) {
19191       return '<table class="table table-striped">\n'
19192         + '<thead>\n'
19193         + header
19194         + '</thead>\n'
19195         + '<tbody>\n'
19196         + body
19197         + '</tbody>\n'
19198         + '</table>\n';
19199     };
19200     
19201     Renderer.prototype.tablerow = function(content) {
19202       return '<tr>\n' + content + '</tr>\n';
19203     };
19204     
19205     Renderer.prototype.tablecell = function(content, flags) {
19206       var type = flags.header ? 'th' : 'td';
19207       var tag = flags.align
19208         ? '<' + type + ' style="text-align:' + flags.align + '">'
19209         : '<' + type + '>';
19210       return tag + content + '</' + type + '>\n';
19211     };
19212     
19213     // span level renderer
19214     Renderer.prototype.strong = function(text) {
19215       return '<strong>' + text + '</strong>';
19216     };
19217     
19218     Renderer.prototype.em = function(text) {
19219       return '<em>' + text + '</em>';
19220     };
19221     
19222     Renderer.prototype.codespan = function(text) {
19223       return '<code>' + text + '</code>';
19224     };
19225     
19226     Renderer.prototype.br = function() {
19227       return this.options.xhtml ? '<br/>' : '<br>';
19228     };
19229     
19230     Renderer.prototype.del = function(text) {
19231       return '<del>' + text + '</del>';
19232     };
19233     
19234     Renderer.prototype.link = function(href, title, text) {
19235       if (this.options.sanitize) {
19236         try {
19237           var prot = decodeURIComponent(unescape(href))
19238             .replace(/[^\w:]/g, '')
19239             .toLowerCase();
19240         } catch (e) {
19241           return '';
19242         }
19243         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19244           return '';
19245         }
19246       }
19247       var out = '<a href="' + href + '"';
19248       if (title) {
19249         out += ' title="' + title + '"';
19250       }
19251       out += '>' + text + '</a>';
19252       return out;
19253     };
19254     
19255     Renderer.prototype.image = function(href, title, text) {
19256       var out = '<img src="' + href + '" alt="' + text + '"';
19257       if (title) {
19258         out += ' title="' + title + '"';
19259       }
19260       out += this.options.xhtml ? '/>' : '>';
19261       return out;
19262     };
19263     
19264     Renderer.prototype.text = function(text) {
19265       return text;
19266     };
19267     
19268     /**
19269      * Parsing & Compiling
19270      */
19271          /**
19272          * eval:var:Parser
19273     */
19274     
19275     var Parser= function (options) {
19276       this.tokens = [];
19277       this.token = null;
19278       this.options = options || marked.defaults;
19279       this.options.renderer = this.options.renderer || new Renderer;
19280       this.renderer = this.options.renderer;
19281       this.renderer.options = this.options;
19282     }
19283     
19284     /**
19285      * Static Parse Method
19286      */
19287     
19288     Parser.parse = function(src, options, renderer) {
19289       var parser = new Parser(options, renderer);
19290       return parser.parse(src);
19291     };
19292     
19293     /**
19294      * Parse Loop
19295      */
19296     
19297     Parser.prototype.parse = function(src) {
19298       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19299       this.tokens = src.reverse();
19300     
19301       var out = '';
19302       while (this.next()) {
19303         out += this.tok();
19304       }
19305     
19306       return out;
19307     };
19308     
19309     /**
19310      * Next Token
19311      */
19312     
19313     Parser.prototype.next = function() {
19314       return this.token = this.tokens.pop();
19315     };
19316     
19317     /**
19318      * Preview Next Token
19319      */
19320     
19321     Parser.prototype.peek = function() {
19322       return this.tokens[this.tokens.length - 1] || 0;
19323     };
19324     
19325     /**
19326      * Parse Text Tokens
19327      */
19328     
19329     Parser.prototype.parseText = function() {
19330       var body = this.token.text;
19331     
19332       while (this.peek().type === 'text') {
19333         body += '\n' + this.next().text;
19334       }
19335     
19336       return this.inline.output(body);
19337     };
19338     
19339     /**
19340      * Parse Current Token
19341      */
19342     
19343     Parser.prototype.tok = function() {
19344       switch (this.token.type) {
19345         case 'space': {
19346           return '';
19347         }
19348         case 'hr': {
19349           return this.renderer.hr();
19350         }
19351         case 'heading': {
19352           return this.renderer.heading(
19353             this.inline.output(this.token.text),
19354             this.token.depth,
19355             this.token.text);
19356         }
19357         case 'code': {
19358           return this.renderer.code(this.token.text,
19359             this.token.lang,
19360             this.token.escaped);
19361         }
19362         case 'table': {
19363           var header = ''
19364             , body = ''
19365             , i
19366             , row
19367             , cell
19368             , flags
19369             , j;
19370     
19371           // header
19372           cell = '';
19373           for (i = 0; i < this.token.header.length; i++) {
19374             flags = { header: true, align: this.token.align[i] };
19375             cell += this.renderer.tablecell(
19376               this.inline.output(this.token.header[i]),
19377               { header: true, align: this.token.align[i] }
19378             );
19379           }
19380           header += this.renderer.tablerow(cell);
19381     
19382           for (i = 0; i < this.token.cells.length; i++) {
19383             row = this.token.cells[i];
19384     
19385             cell = '';
19386             for (j = 0; j < row.length; j++) {
19387               cell += this.renderer.tablecell(
19388                 this.inline.output(row[j]),
19389                 { header: false, align: this.token.align[j] }
19390               );
19391             }
19392     
19393             body += this.renderer.tablerow(cell);
19394           }
19395           return this.renderer.table(header, body);
19396         }
19397         case 'blockquote_start': {
19398           var body = '';
19399     
19400           while (this.next().type !== 'blockquote_end') {
19401             body += this.tok();
19402           }
19403     
19404           return this.renderer.blockquote(body);
19405         }
19406         case 'list_start': {
19407           var body = ''
19408             , ordered = this.token.ordered;
19409     
19410           while (this.next().type !== 'list_end') {
19411             body += this.tok();
19412           }
19413     
19414           return this.renderer.list(body, ordered);
19415         }
19416         case 'list_item_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'list_item_end') {
19420             body += this.token.type === 'text'
19421               ? this.parseText()
19422               : this.tok();
19423           }
19424     
19425           return this.renderer.listitem(body);
19426         }
19427         case 'loose_item_start': {
19428           var body = '';
19429     
19430           while (this.next().type !== 'list_item_end') {
19431             body += this.tok();
19432           }
19433     
19434           return this.renderer.listitem(body);
19435         }
19436         case 'html': {
19437           var html = !this.token.pre && !this.options.pedantic
19438             ? this.inline.output(this.token.text)
19439             : this.token.text;
19440           return this.renderer.html(html);
19441         }
19442         case 'paragraph': {
19443           return this.renderer.paragraph(this.inline.output(this.token.text));
19444         }
19445         case 'text': {
19446           return this.renderer.paragraph(this.parseText());
19447         }
19448       }
19449     };
19450   
19451     
19452     /**
19453      * Marked
19454      */
19455          /**
19456          * eval:var:marked
19457     */
19458     var marked = function (src, opt, callback) {
19459       if (callback || typeof opt === 'function') {
19460         if (!callback) {
19461           callback = opt;
19462           opt = null;
19463         }
19464     
19465         opt = merge({}, marked.defaults, opt || {});
19466     
19467         var highlight = opt.highlight
19468           , tokens
19469           , pending
19470           , i = 0;
19471     
19472         try {
19473           tokens = Lexer.lex(src, opt)
19474         } catch (e) {
19475           return callback(e);
19476         }
19477     
19478         pending = tokens.length;
19479          /**
19480          * eval:var:done
19481     */
19482         var done = function(err) {
19483           if (err) {
19484             opt.highlight = highlight;
19485             return callback(err);
19486           }
19487     
19488           var out;
19489     
19490           try {
19491             out = Parser.parse(tokens, opt);
19492           } catch (e) {
19493             err = e;
19494           }
19495     
19496           opt.highlight = highlight;
19497     
19498           return err
19499             ? callback(err)
19500             : callback(null, out);
19501         };
19502     
19503         if (!highlight || highlight.length < 3) {
19504           return done();
19505         }
19506     
19507         delete opt.highlight;
19508     
19509         if (!pending) { return done(); }
19510     
19511         for (; i < tokens.length; i++) {
19512           (function(token) {
19513             if (token.type !== 'code') {
19514               return --pending || done();
19515             }
19516             return highlight(token.text, token.lang, function(err, code) {
19517               if (err) { return done(err); }
19518               if (code == null || code === token.text) {
19519                 return --pending || done();
19520               }
19521               token.text = code;
19522               token.escaped = true;
19523               --pending || done();
19524             });
19525           })(tokens[i]);
19526         }
19527     
19528         return;
19529       }
19530       try {
19531         if (opt) { opt = merge({}, marked.defaults, opt); }
19532         return Parser.parse(Lexer.lex(src, opt), opt);
19533       } catch (e) {
19534         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19535         if ((opt || marked.defaults).silent) {
19536           return '<p>An error occured:</p><pre>'
19537             + escape(e.message + '', true)
19538             + '</pre>';
19539         }
19540         throw e;
19541       }
19542     }
19543     
19544     /**
19545      * Options
19546      */
19547     
19548     marked.options =
19549     marked.setOptions = function(opt) {
19550       merge(marked.defaults, opt);
19551       return marked;
19552     };
19553     
19554     marked.defaults = {
19555       gfm: true,
19556       tables: true,
19557       breaks: false,
19558       pedantic: false,
19559       sanitize: false,
19560       sanitizer: null,
19561       mangle: true,
19562       smartLists: false,
19563       silent: false,
19564       highlight: null,
19565       langPrefix: 'lang-',
19566       smartypants: false,
19567       headerPrefix: '',
19568       renderer: new Renderer,
19569       xhtml: false
19570     };
19571     
19572     /**
19573      * Expose
19574      */
19575     
19576     marked.Parser = Parser;
19577     marked.parser = Parser.parse;
19578     
19579     marked.Renderer = Renderer;
19580     
19581     marked.Lexer = Lexer;
19582     marked.lexer = Lexer.lex;
19583     
19584     marked.InlineLexer = InlineLexer;
19585     marked.inlineLexer = InlineLexer.output;
19586     
19587     marked.parse = marked;
19588     
19589     Roo.Markdown.marked = marked;
19590
19591 })();/*
19592  * Based on:
19593  * Ext JS Library 1.1.1
19594  * Copyright(c) 2006-2007, Ext JS, LLC.
19595  *
19596  * Originally Released Under LGPL - original licence link has changed is not relivant.
19597  *
19598  * Fork - LGPL
19599  * <script type="text/javascript">
19600  */
19601
19602
19603
19604 /*
19605  * These classes are derivatives of the similarly named classes in the YUI Library.
19606  * The original license:
19607  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19608  * Code licensed under the BSD License:
19609  * http://developer.yahoo.net/yui/license.txt
19610  */
19611
19612 (function() {
19613
19614 var Event=Roo.EventManager;
19615 var Dom=Roo.lib.Dom;
19616
19617 /**
19618  * @class Roo.dd.DragDrop
19619  * @extends Roo.util.Observable
19620  * Defines the interface and base operation of items that that can be
19621  * dragged or can be drop targets.  It was designed to be extended, overriding
19622  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19623  * Up to three html elements can be associated with a DragDrop instance:
19624  * <ul>
19625  * <li>linked element: the element that is passed into the constructor.
19626  * This is the element which defines the boundaries for interaction with
19627  * other DragDrop objects.</li>
19628  * <li>handle element(s): The drag operation only occurs if the element that
19629  * was clicked matches a handle element.  By default this is the linked
19630  * element, but there are times that you will want only a portion of the
19631  * linked element to initiate the drag operation, and the setHandleElId()
19632  * method provides a way to define this.</li>
19633  * <li>drag element: this represents the element that would be moved along
19634  * with the cursor during a drag operation.  By default, this is the linked
19635  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19636  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19637  * </li>
19638  * </ul>
19639  * This class should not be instantiated until the onload event to ensure that
19640  * the associated elements are available.
19641  * The following would define a DragDrop obj that would interact with any
19642  * other DragDrop obj in the "group1" group:
19643  * <pre>
19644  *  dd = new Roo.dd.DragDrop("div1", "group1");
19645  * </pre>
19646  * Since none of the event handlers have been implemented, nothing would
19647  * actually happen if you were to run the code above.  Normally you would
19648  * override this class or one of the default implementations, but you can
19649  * also override the methods you want on an instance of the class...
19650  * <pre>
19651  *  dd.onDragDrop = function(e, id) {
19652  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19653  *  }
19654  * </pre>
19655  * @constructor
19656  * @param {String} id of the element that is linked to this instance
19657  * @param {String} sGroup the group of related DragDrop objects
19658  * @param {object} config an object containing configurable attributes
19659  *                Valid properties for DragDrop:
19660  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19661  */
19662 Roo.dd.DragDrop = function(id, sGroup, config) {
19663     if (id) {
19664         this.init(id, sGroup, config);
19665     }
19666     
19667 };
19668
19669 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19670
19671     /**
19672      * The id of the element associated with this object.  This is what we
19673      * refer to as the "linked element" because the size and position of
19674      * this element is used to determine when the drag and drop objects have
19675      * interacted.
19676      * @property id
19677      * @type String
19678      */
19679     id: null,
19680
19681     /**
19682      * Configuration attributes passed into the constructor
19683      * @property config
19684      * @type object
19685      */
19686     config: null,
19687
19688     /**
19689      * The id of the element that will be dragged.  By default this is same
19690      * as the linked element , but could be changed to another element. Ex:
19691      * Roo.dd.DDProxy
19692      * @property dragElId
19693      * @type String
19694      * @private
19695      */
19696     dragElId: null,
19697
19698     /**
19699      * the id of the element that initiates the drag operation.  By default
19700      * this is the linked element, but could be changed to be a child of this
19701      * element.  This lets us do things like only starting the drag when the
19702      * header element within the linked html element is clicked.
19703      * @property handleElId
19704      * @type String
19705      * @private
19706      */
19707     handleElId: null,
19708
19709     /**
19710      * An associative array of HTML tags that will be ignored if clicked.
19711      * @property invalidHandleTypes
19712      * @type {string: string}
19713      */
19714     invalidHandleTypes: null,
19715
19716     /**
19717      * An associative array of ids for elements that will be ignored if clicked
19718      * @property invalidHandleIds
19719      * @type {string: string}
19720      */
19721     invalidHandleIds: null,
19722
19723     /**
19724      * An indexted array of css class names for elements that will be ignored
19725      * if clicked.
19726      * @property invalidHandleClasses
19727      * @type string[]
19728      */
19729     invalidHandleClasses: null,
19730
19731     /**
19732      * The linked element's absolute X position at the time the drag was
19733      * started
19734      * @property startPageX
19735      * @type int
19736      * @private
19737      */
19738     startPageX: 0,
19739
19740     /**
19741      * The linked element's absolute X position at the time the drag was
19742      * started
19743      * @property startPageY
19744      * @type int
19745      * @private
19746      */
19747     startPageY: 0,
19748
19749     /**
19750      * The group defines a logical collection of DragDrop objects that are
19751      * related.  Instances only get events when interacting with other
19752      * DragDrop object in the same group.  This lets us define multiple
19753      * groups using a single DragDrop subclass if we want.
19754      * @property groups
19755      * @type {string: string}
19756      */
19757     groups: null,
19758
19759     /**
19760      * Individual drag/drop instances can be locked.  This will prevent
19761      * onmousedown start drag.
19762      * @property locked
19763      * @type boolean
19764      * @private
19765      */
19766     locked: false,
19767
19768     /**
19769      * Lock this instance
19770      * @method lock
19771      */
19772     lock: function() { this.locked = true; },
19773
19774     /**
19775      * Unlock this instace
19776      * @method unlock
19777      */
19778     unlock: function() { this.locked = false; },
19779
19780     /**
19781      * By default, all insances can be a drop target.  This can be disabled by
19782      * setting isTarget to false.
19783      * @method isTarget
19784      * @type boolean
19785      */
19786     isTarget: true,
19787
19788     /**
19789      * The padding configured for this drag and drop object for calculating
19790      * the drop zone intersection with this object.
19791      * @method padding
19792      * @type int[]
19793      */
19794     padding: null,
19795
19796     /**
19797      * Cached reference to the linked element
19798      * @property _domRef
19799      * @private
19800      */
19801     _domRef: null,
19802
19803     /**
19804      * Internal typeof flag
19805      * @property __ygDragDrop
19806      * @private
19807      */
19808     __ygDragDrop: true,
19809
19810     /**
19811      * Set to true when horizontal contraints are applied
19812      * @property constrainX
19813      * @type boolean
19814      * @private
19815      */
19816     constrainX: false,
19817
19818     /**
19819      * Set to true when vertical contraints are applied
19820      * @property constrainY
19821      * @type boolean
19822      * @private
19823      */
19824     constrainY: false,
19825
19826     /**
19827      * The left constraint
19828      * @property minX
19829      * @type int
19830      * @private
19831      */
19832     minX: 0,
19833
19834     /**
19835      * The right constraint
19836      * @property maxX
19837      * @type int
19838      * @private
19839      */
19840     maxX: 0,
19841
19842     /**
19843      * The up constraint
19844      * @property minY
19845      * @type int
19846      * @type int
19847      * @private
19848      */
19849     minY: 0,
19850
19851     /**
19852      * The down constraint
19853      * @property maxY
19854      * @type int
19855      * @private
19856      */
19857     maxY: 0,
19858
19859     /**
19860      * Maintain offsets when we resetconstraints.  Set to true when you want
19861      * the position of the element relative to its parent to stay the same
19862      * when the page changes
19863      *
19864      * @property maintainOffset
19865      * @type boolean
19866      */
19867     maintainOffset: false,
19868
19869     /**
19870      * Array of pixel locations the element will snap to if we specified a
19871      * horizontal graduation/interval.  This array is generated automatically
19872      * when you define a tick interval.
19873      * @property xTicks
19874      * @type int[]
19875      */
19876     xTicks: null,
19877
19878     /**
19879      * Array of pixel locations the element will snap to if we specified a
19880      * vertical graduation/interval.  This array is generated automatically
19881      * when you define a tick interval.
19882      * @property yTicks
19883      * @type int[]
19884      */
19885     yTicks: null,
19886
19887     /**
19888      * By default the drag and drop instance will only respond to the primary
19889      * button click (left button for a right-handed mouse).  Set to true to
19890      * allow drag and drop to start with any mouse click that is propogated
19891      * by the browser
19892      * @property primaryButtonOnly
19893      * @type boolean
19894      */
19895     primaryButtonOnly: true,
19896
19897     /**
19898      * The availabe property is false until the linked dom element is accessible.
19899      * @property available
19900      * @type boolean
19901      */
19902     available: false,
19903
19904     /**
19905      * By default, drags can only be initiated if the mousedown occurs in the
19906      * region the linked element is.  This is done in part to work around a
19907      * bug in some browsers that mis-report the mousedown if the previous
19908      * mouseup happened outside of the window.  This property is set to true
19909      * if outer handles are defined.
19910      *
19911      * @property hasOuterHandles
19912      * @type boolean
19913      * @default false
19914      */
19915     hasOuterHandles: false,
19916
19917     /**
19918      * Code that executes immediately before the startDrag event
19919      * @method b4StartDrag
19920      * @private
19921      */
19922     b4StartDrag: function(x, y) { },
19923
19924     /**
19925      * Abstract method called after a drag/drop object is clicked
19926      * and the drag or mousedown time thresholds have beeen met.
19927      * @method startDrag
19928      * @param {int} X click location
19929      * @param {int} Y click location
19930      */
19931     startDrag: function(x, y) { /* override this */ },
19932
19933     /**
19934      * Code that executes immediately before the onDrag event
19935      * @method b4Drag
19936      * @private
19937      */
19938     b4Drag: function(e) { },
19939
19940     /**
19941      * Abstract method called during the onMouseMove event while dragging an
19942      * object.
19943      * @method onDrag
19944      * @param {Event} e the mousemove event
19945      */
19946     onDrag: function(e) { /* override this */ },
19947
19948     /**
19949      * Abstract method called when this element fist begins hovering over
19950      * another DragDrop obj
19951      * @method onDragEnter
19952      * @param {Event} e the mousemove event
19953      * @param {String|DragDrop[]} id In POINT mode, the element
19954      * id this is hovering over.  In INTERSECT mode, an array of one or more
19955      * dragdrop items being hovered over.
19956      */
19957     onDragEnter: function(e, id) { /* override this */ },
19958
19959     /**
19960      * Code that executes immediately before the onDragOver event
19961      * @method b4DragOver
19962      * @private
19963      */
19964     b4DragOver: function(e) { },
19965
19966     /**
19967      * Abstract method called when this element is hovering over another
19968      * DragDrop obj
19969      * @method onDragOver
19970      * @param {Event} e the mousemove event
19971      * @param {String|DragDrop[]} id In POINT mode, the element
19972      * id this is hovering over.  In INTERSECT mode, an array of dd items
19973      * being hovered over.
19974      */
19975     onDragOver: function(e, id) { /* override this */ },
19976
19977     /**
19978      * Code that executes immediately before the onDragOut event
19979      * @method b4DragOut
19980      * @private
19981      */
19982     b4DragOut: function(e) { },
19983
19984     /**
19985      * Abstract method called when we are no longer hovering over an element
19986      * @method onDragOut
19987      * @param {Event} e the mousemove event
19988      * @param {String|DragDrop[]} id In POINT mode, the element
19989      * id this was hovering over.  In INTERSECT mode, an array of dd items
19990      * that the mouse is no longer over.
19991      */
19992     onDragOut: function(e, id) { /* override this */ },
19993
19994     /**
19995      * Code that executes immediately before the onDragDrop event
19996      * @method b4DragDrop
19997      * @private
19998      */
19999     b4DragDrop: function(e) { },
20000
20001     /**
20002      * Abstract method called when this item is dropped on another DragDrop
20003      * obj
20004      * @method onDragDrop
20005      * @param {Event} e the mouseup event
20006      * @param {String|DragDrop[]} id In POINT mode, the element
20007      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20008      * was dropped on.
20009      */
20010     onDragDrop: function(e, id) { /* override this */ },
20011
20012     /**
20013      * Abstract method called when this item is dropped on an area with no
20014      * drop target
20015      * @method onInvalidDrop
20016      * @param {Event} e the mouseup event
20017      */
20018     onInvalidDrop: function(e) { /* override this */ },
20019
20020     /**
20021      * Code that executes immediately before the endDrag event
20022      * @method b4EndDrag
20023      * @private
20024      */
20025     b4EndDrag: function(e) { },
20026
20027     /**
20028      * Fired when we are done dragging the object
20029      * @method endDrag
20030      * @param {Event} e the mouseup event
20031      */
20032     endDrag: function(e) { /* override this */ },
20033
20034     /**
20035      * Code executed immediately before the onMouseDown event
20036      * @method b4MouseDown
20037      * @param {Event} e the mousedown event
20038      * @private
20039      */
20040     b4MouseDown: function(e) {  },
20041
20042     /**
20043      * Event handler that fires when a drag/drop obj gets a mousedown
20044      * @method onMouseDown
20045      * @param {Event} e the mousedown event
20046      */
20047     onMouseDown: function(e) { /* override this */ },
20048
20049     /**
20050      * Event handler that fires when a drag/drop obj gets a mouseup
20051      * @method onMouseUp
20052      * @param {Event} e the mouseup event
20053      */
20054     onMouseUp: function(e) { /* override this */ },
20055
20056     /**
20057      * Override the onAvailable method to do what is needed after the initial
20058      * position was determined.
20059      * @method onAvailable
20060      */
20061     onAvailable: function () {
20062     },
20063
20064     /*
20065      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20066      * @type Object
20067      */
20068     defaultPadding : {left:0, right:0, top:0, bottom:0},
20069
20070     /*
20071      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20072  *
20073  * Usage:
20074  <pre><code>
20075  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20076                 { dragElId: "existingProxyDiv" });
20077  dd.startDrag = function(){
20078      this.constrainTo("parent-id");
20079  };
20080  </code></pre>
20081  * Or you can initalize it using the {@link Roo.Element} object:
20082  <pre><code>
20083  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20084      startDrag : function(){
20085          this.constrainTo("parent-id");
20086      }
20087  });
20088  </code></pre>
20089      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20090      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20091      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20092      * an object containing the sides to pad. For example: {right:10, bottom:10}
20093      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20094      */
20095     constrainTo : function(constrainTo, pad, inContent){
20096         if(typeof pad == "number"){
20097             pad = {left: pad, right:pad, top:pad, bottom:pad};
20098         }
20099         pad = pad || this.defaultPadding;
20100         var b = Roo.get(this.getEl()).getBox();
20101         var ce = Roo.get(constrainTo);
20102         var s = ce.getScroll();
20103         var c, cd = ce.dom;
20104         if(cd == document.body){
20105             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20106         }else{
20107             xy = ce.getXY();
20108             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20109         }
20110
20111
20112         var topSpace = b.y - c.y;
20113         var leftSpace = b.x - c.x;
20114
20115         this.resetConstraints();
20116         this.setXConstraint(leftSpace - (pad.left||0), // left
20117                 c.width - leftSpace - b.width - (pad.right||0) //right
20118         );
20119         this.setYConstraint(topSpace - (pad.top||0), //top
20120                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20121         );
20122     },
20123
20124     /**
20125      * Returns a reference to the linked element
20126      * @method getEl
20127      * @return {HTMLElement} the html element
20128      */
20129     getEl: function() {
20130         if (!this._domRef) {
20131             this._domRef = Roo.getDom(this.id);
20132         }
20133
20134         return this._domRef;
20135     },
20136
20137     /**
20138      * Returns a reference to the actual element to drag.  By default this is
20139      * the same as the html element, but it can be assigned to another
20140      * element. An example of this can be found in Roo.dd.DDProxy
20141      * @method getDragEl
20142      * @return {HTMLElement} the html element
20143      */
20144     getDragEl: function() {
20145         return Roo.getDom(this.dragElId);
20146     },
20147
20148     /**
20149      * Sets up the DragDrop object.  Must be called in the constructor of any
20150      * Roo.dd.DragDrop subclass
20151      * @method init
20152      * @param id the id of the linked element
20153      * @param {String} sGroup the group of related items
20154      * @param {object} config configuration attributes
20155      */
20156     init: function(id, sGroup, config) {
20157         this.initTarget(id, sGroup, config);
20158         if (!Roo.isTouch) {
20159             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20160         }
20161         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20162         // Event.on(this.id, "selectstart", Event.preventDefault);
20163     },
20164
20165     /**
20166      * Initializes Targeting functionality only... the object does not
20167      * get a mousedown handler.
20168      * @method initTarget
20169      * @param id the id of the linked element
20170      * @param {String} sGroup the group of related items
20171      * @param {object} config configuration attributes
20172      */
20173     initTarget: function(id, sGroup, config) {
20174
20175         // configuration attributes
20176         this.config = config || {};
20177
20178         // create a local reference to the drag and drop manager
20179         this.DDM = Roo.dd.DDM;
20180         // initialize the groups array
20181         this.groups = {};
20182
20183         // assume that we have an element reference instead of an id if the
20184         // parameter is not a string
20185         if (typeof id !== "string") {
20186             id = Roo.id(id);
20187         }
20188
20189         // set the id
20190         this.id = id;
20191
20192         // add to an interaction group
20193         this.addToGroup((sGroup) ? sGroup : "default");
20194
20195         // We don't want to register this as the handle with the manager
20196         // so we just set the id rather than calling the setter.
20197         this.handleElId = id;
20198
20199         // the linked element is the element that gets dragged by default
20200         this.setDragElId(id);
20201
20202         // by default, clicked anchors will not start drag operations.
20203         this.invalidHandleTypes = { A: "A" };
20204         this.invalidHandleIds = {};
20205         this.invalidHandleClasses = [];
20206
20207         this.applyConfig();
20208
20209         this.handleOnAvailable();
20210     },
20211
20212     /**
20213      * Applies the configuration parameters that were passed into the constructor.
20214      * This is supposed to happen at each level through the inheritance chain.  So
20215      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20216      * DragDrop in order to get all of the parameters that are available in
20217      * each object.
20218      * @method applyConfig
20219      */
20220     applyConfig: function() {
20221
20222         // configurable properties:
20223         //    padding, isTarget, maintainOffset, primaryButtonOnly
20224         this.padding           = this.config.padding || [0, 0, 0, 0];
20225         this.isTarget          = (this.config.isTarget !== false);
20226         this.maintainOffset    = (this.config.maintainOffset);
20227         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20228
20229     },
20230
20231     /**
20232      * Executed when the linked element is available
20233      * @method handleOnAvailable
20234      * @private
20235      */
20236     handleOnAvailable: function() {
20237         this.available = true;
20238         this.resetConstraints();
20239         this.onAvailable();
20240     },
20241
20242      /**
20243      * Configures the padding for the target zone in px.  Effectively expands
20244      * (or reduces) the virtual object size for targeting calculations.
20245      * Supports css-style shorthand; if only one parameter is passed, all sides
20246      * will have that padding, and if only two are passed, the top and bottom
20247      * will have the first param, the left and right the second.
20248      * @method setPadding
20249      * @param {int} iTop    Top pad
20250      * @param {int} iRight  Right pad
20251      * @param {int} iBot    Bot pad
20252      * @param {int} iLeft   Left pad
20253      */
20254     setPadding: function(iTop, iRight, iBot, iLeft) {
20255         // this.padding = [iLeft, iRight, iTop, iBot];
20256         if (!iRight && 0 !== iRight) {
20257             this.padding = [iTop, iTop, iTop, iTop];
20258         } else if (!iBot && 0 !== iBot) {
20259             this.padding = [iTop, iRight, iTop, iRight];
20260         } else {
20261             this.padding = [iTop, iRight, iBot, iLeft];
20262         }
20263     },
20264
20265     /**
20266      * Stores the initial placement of the linked element.
20267      * @method setInitialPosition
20268      * @param {int} diffX   the X offset, default 0
20269      * @param {int} diffY   the Y offset, default 0
20270      */
20271     setInitPosition: function(diffX, diffY) {
20272         var el = this.getEl();
20273
20274         if (!this.DDM.verifyEl(el)) {
20275             return;
20276         }
20277
20278         var dx = diffX || 0;
20279         var dy = diffY || 0;
20280
20281         var p = Dom.getXY( el );
20282
20283         this.initPageX = p[0] - dx;
20284         this.initPageY = p[1] - dy;
20285
20286         this.lastPageX = p[0];
20287         this.lastPageY = p[1];
20288
20289
20290         this.setStartPosition(p);
20291     },
20292
20293     /**
20294      * Sets the start position of the element.  This is set when the obj
20295      * is initialized, the reset when a drag is started.
20296      * @method setStartPosition
20297      * @param pos current position (from previous lookup)
20298      * @private
20299      */
20300     setStartPosition: function(pos) {
20301         var p = pos || Dom.getXY( this.getEl() );
20302         this.deltaSetXY = null;
20303
20304         this.startPageX = p[0];
20305         this.startPageY = p[1];
20306     },
20307
20308     /**
20309      * Add this instance to a group of related drag/drop objects.  All
20310      * instances belong to at least one group, and can belong to as many
20311      * groups as needed.
20312      * @method addToGroup
20313      * @param sGroup {string} the name of the group
20314      */
20315     addToGroup: function(sGroup) {
20316         this.groups[sGroup] = true;
20317         this.DDM.regDragDrop(this, sGroup);
20318     },
20319
20320     /**
20321      * Remove's this instance from the supplied interaction group
20322      * @method removeFromGroup
20323      * @param {string}  sGroup  The group to drop
20324      */
20325     removeFromGroup: function(sGroup) {
20326         if (this.groups[sGroup]) {
20327             delete this.groups[sGroup];
20328         }
20329
20330         this.DDM.removeDDFromGroup(this, sGroup);
20331     },
20332
20333     /**
20334      * Allows you to specify that an element other than the linked element
20335      * will be moved with the cursor during a drag
20336      * @method setDragElId
20337      * @param id {string} the id of the element that will be used to initiate the drag
20338      */
20339     setDragElId: function(id) {
20340         this.dragElId = id;
20341     },
20342
20343     /**
20344      * Allows you to specify a child of the linked element that should be
20345      * used to initiate the drag operation.  An example of this would be if
20346      * you have a content div with text and links.  Clicking anywhere in the
20347      * content area would normally start the drag operation.  Use this method
20348      * to specify that an element inside of the content div is the element
20349      * that starts the drag operation.
20350      * @method setHandleElId
20351      * @param id {string} the id of the element that will be used to
20352      * initiate the drag.
20353      */
20354     setHandleElId: function(id) {
20355         if (typeof id !== "string") {
20356             id = Roo.id(id);
20357         }
20358         this.handleElId = id;
20359         this.DDM.regHandle(this.id, id);
20360     },
20361
20362     /**
20363      * Allows you to set an element outside of the linked element as a drag
20364      * handle
20365      * @method setOuterHandleElId
20366      * @param id the id of the element that will be used to initiate the drag
20367      */
20368     setOuterHandleElId: function(id) {
20369         if (typeof id !== "string") {
20370             id = Roo.id(id);
20371         }
20372         Event.on(id, "mousedown",
20373                 this.handleMouseDown, this);
20374         this.setHandleElId(id);
20375
20376         this.hasOuterHandles = true;
20377     },
20378
20379     /**
20380      * Remove all drag and drop hooks for this element
20381      * @method unreg
20382      */
20383     unreg: function() {
20384         Event.un(this.id, "mousedown",
20385                 this.handleMouseDown);
20386         Event.un(this.id, "touchstart",
20387                 this.handleMouseDown);
20388         this._domRef = null;
20389         this.DDM._remove(this);
20390     },
20391
20392     destroy : function(){
20393         this.unreg();
20394     },
20395
20396     /**
20397      * Returns true if this instance is locked, or the drag drop mgr is locked
20398      * (meaning that all drag/drop is disabled on the page.)
20399      * @method isLocked
20400      * @return {boolean} true if this obj or all drag/drop is locked, else
20401      * false
20402      */
20403     isLocked: function() {
20404         return (this.DDM.isLocked() || this.locked);
20405     },
20406
20407     /**
20408      * Fired when this object is clicked
20409      * @method handleMouseDown
20410      * @param {Event} e
20411      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20412      * @private
20413      */
20414     handleMouseDown: function(e, oDD){
20415      
20416         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20417             //Roo.log('not touch/ button !=0');
20418             return;
20419         }
20420         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20421             return; // double touch..
20422         }
20423         
20424
20425         if (this.isLocked()) {
20426             //Roo.log('locked');
20427             return;
20428         }
20429
20430         this.DDM.refreshCache(this.groups);
20431 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20432         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20433         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20434             //Roo.log('no outer handes or not over target');
20435                 // do nothing.
20436         } else {
20437 //            Roo.log('check validator');
20438             if (this.clickValidator(e)) {
20439 //                Roo.log('validate success');
20440                 // set the initial element position
20441                 this.setStartPosition();
20442
20443
20444                 this.b4MouseDown(e);
20445                 this.onMouseDown(e);
20446
20447                 this.DDM.handleMouseDown(e, this);
20448
20449                 this.DDM.stopEvent(e);
20450             } else {
20451
20452
20453             }
20454         }
20455     },
20456
20457     clickValidator: function(e) {
20458         var target = e.getTarget();
20459         return ( this.isValidHandleChild(target) &&
20460                     (this.id == this.handleElId ||
20461                         this.DDM.handleWasClicked(target, this.id)) );
20462     },
20463
20464     /**
20465      * Allows you to specify a tag name that should not start a drag operation
20466      * when clicked.  This is designed to facilitate embedding links within a
20467      * drag handle that do something other than start the drag.
20468      * @method addInvalidHandleType
20469      * @param {string} tagName the type of element to exclude
20470      */
20471     addInvalidHandleType: function(tagName) {
20472         var type = tagName.toUpperCase();
20473         this.invalidHandleTypes[type] = type;
20474     },
20475
20476     /**
20477      * Lets you to specify an element id for a child of a drag handle
20478      * that should not initiate a drag
20479      * @method addInvalidHandleId
20480      * @param {string} id the element id of the element you wish to ignore
20481      */
20482     addInvalidHandleId: function(id) {
20483         if (typeof id !== "string") {
20484             id = Roo.id(id);
20485         }
20486         this.invalidHandleIds[id] = id;
20487     },
20488
20489     /**
20490      * Lets you specify a css class of elements that will not initiate a drag
20491      * @method addInvalidHandleClass
20492      * @param {string} cssClass the class of the elements you wish to ignore
20493      */
20494     addInvalidHandleClass: function(cssClass) {
20495         this.invalidHandleClasses.push(cssClass);
20496     },
20497
20498     /**
20499      * Unsets an excluded tag name set by addInvalidHandleType
20500      * @method removeInvalidHandleType
20501      * @param {string} tagName the type of element to unexclude
20502      */
20503     removeInvalidHandleType: function(tagName) {
20504         var type = tagName.toUpperCase();
20505         // this.invalidHandleTypes[type] = null;
20506         delete this.invalidHandleTypes[type];
20507     },
20508
20509     /**
20510      * Unsets an invalid handle id
20511      * @method removeInvalidHandleId
20512      * @param {string} id the id of the element to re-enable
20513      */
20514     removeInvalidHandleId: function(id) {
20515         if (typeof id !== "string") {
20516             id = Roo.id(id);
20517         }
20518         delete this.invalidHandleIds[id];
20519     },
20520
20521     /**
20522      * Unsets an invalid css class
20523      * @method removeInvalidHandleClass
20524      * @param {string} cssClass the class of the element(s) you wish to
20525      * re-enable
20526      */
20527     removeInvalidHandleClass: function(cssClass) {
20528         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20529             if (this.invalidHandleClasses[i] == cssClass) {
20530                 delete this.invalidHandleClasses[i];
20531             }
20532         }
20533     },
20534
20535     /**
20536      * Checks the tag exclusion list to see if this click should be ignored
20537      * @method isValidHandleChild
20538      * @param {HTMLElement} node the HTMLElement to evaluate
20539      * @return {boolean} true if this is a valid tag type, false if not
20540      */
20541     isValidHandleChild: function(node) {
20542
20543         var valid = true;
20544         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20545         var nodeName;
20546         try {
20547             nodeName = node.nodeName.toUpperCase();
20548         } catch(e) {
20549             nodeName = node.nodeName;
20550         }
20551         valid = valid && !this.invalidHandleTypes[nodeName];
20552         valid = valid && !this.invalidHandleIds[node.id];
20553
20554         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20555             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20556         }
20557
20558
20559         return valid;
20560
20561     },
20562
20563     /**
20564      * Create the array of horizontal tick marks if an interval was specified
20565      * in setXConstraint().
20566      * @method setXTicks
20567      * @private
20568      */
20569     setXTicks: function(iStartX, iTickSize) {
20570         this.xTicks = [];
20571         this.xTickSize = iTickSize;
20572
20573         var tickMap = {};
20574
20575         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20576             if (!tickMap[i]) {
20577                 this.xTicks[this.xTicks.length] = i;
20578                 tickMap[i] = true;
20579             }
20580         }
20581
20582         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20583             if (!tickMap[i]) {
20584                 this.xTicks[this.xTicks.length] = i;
20585                 tickMap[i] = true;
20586             }
20587         }
20588
20589         this.xTicks.sort(this.DDM.numericSort) ;
20590     },
20591
20592     /**
20593      * Create the array of vertical tick marks if an interval was specified in
20594      * setYConstraint().
20595      * @method setYTicks
20596      * @private
20597      */
20598     setYTicks: function(iStartY, iTickSize) {
20599         this.yTicks = [];
20600         this.yTickSize = iTickSize;
20601
20602         var tickMap = {};
20603
20604         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20605             if (!tickMap[i]) {
20606                 this.yTicks[this.yTicks.length] = i;
20607                 tickMap[i] = true;
20608             }
20609         }
20610
20611         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20612             if (!tickMap[i]) {
20613                 this.yTicks[this.yTicks.length] = i;
20614                 tickMap[i] = true;
20615             }
20616         }
20617
20618         this.yTicks.sort(this.DDM.numericSort) ;
20619     },
20620
20621     /**
20622      * By default, the element can be dragged any place on the screen.  Use
20623      * this method to limit the horizontal travel of the element.  Pass in
20624      * 0,0 for the parameters if you want to lock the drag to the y axis.
20625      * @method setXConstraint
20626      * @param {int} iLeft the number of pixels the element can move to the left
20627      * @param {int} iRight the number of pixels the element can move to the
20628      * right
20629      * @param {int} iTickSize optional parameter for specifying that the
20630      * element
20631      * should move iTickSize pixels at a time.
20632      */
20633     setXConstraint: function(iLeft, iRight, iTickSize) {
20634         this.leftConstraint = iLeft;
20635         this.rightConstraint = iRight;
20636
20637         this.minX = this.initPageX - iLeft;
20638         this.maxX = this.initPageX + iRight;
20639         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20640
20641         this.constrainX = true;
20642     },
20643
20644     /**
20645      * Clears any constraints applied to this instance.  Also clears ticks
20646      * since they can't exist independent of a constraint at this time.
20647      * @method clearConstraints
20648      */
20649     clearConstraints: function() {
20650         this.constrainX = false;
20651         this.constrainY = false;
20652         this.clearTicks();
20653     },
20654
20655     /**
20656      * Clears any tick interval defined for this instance
20657      * @method clearTicks
20658      */
20659     clearTicks: function() {
20660         this.xTicks = null;
20661         this.yTicks = null;
20662         this.xTickSize = 0;
20663         this.yTickSize = 0;
20664     },
20665
20666     /**
20667      * By default, the element can be dragged any place on the screen.  Set
20668      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20669      * parameters if you want to lock the drag to the x axis.
20670      * @method setYConstraint
20671      * @param {int} iUp the number of pixels the element can move up
20672      * @param {int} iDown the number of pixels the element can move down
20673      * @param {int} iTickSize optional parameter for specifying that the
20674      * element should move iTickSize pixels at a time.
20675      */
20676     setYConstraint: function(iUp, iDown, iTickSize) {
20677         this.topConstraint = iUp;
20678         this.bottomConstraint = iDown;
20679
20680         this.minY = this.initPageY - iUp;
20681         this.maxY = this.initPageY + iDown;
20682         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20683
20684         this.constrainY = true;
20685
20686     },
20687
20688     /**
20689      * resetConstraints must be called if you manually reposition a dd element.
20690      * @method resetConstraints
20691      * @param {boolean} maintainOffset
20692      */
20693     resetConstraints: function() {
20694
20695
20696         // Maintain offsets if necessary
20697         if (this.initPageX || this.initPageX === 0) {
20698             // figure out how much this thing has moved
20699             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20700             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20701
20702             this.setInitPosition(dx, dy);
20703
20704         // This is the first time we have detected the element's position
20705         } else {
20706             this.setInitPosition();
20707         }
20708
20709         if (this.constrainX) {
20710             this.setXConstraint( this.leftConstraint,
20711                                  this.rightConstraint,
20712                                  this.xTickSize        );
20713         }
20714
20715         if (this.constrainY) {
20716             this.setYConstraint( this.topConstraint,
20717                                  this.bottomConstraint,
20718                                  this.yTickSize         );
20719         }
20720     },
20721
20722     /**
20723      * Normally the drag element is moved pixel by pixel, but we can specify
20724      * that it move a number of pixels at a time.  This method resolves the
20725      * location when we have it set up like this.
20726      * @method getTick
20727      * @param {int} val where we want to place the object
20728      * @param {int[]} tickArray sorted array of valid points
20729      * @return {int} the closest tick
20730      * @private
20731      */
20732     getTick: function(val, tickArray) {
20733
20734         if (!tickArray) {
20735             // If tick interval is not defined, it is effectively 1 pixel,
20736             // so we return the value passed to us.
20737             return val;
20738         } else if (tickArray[0] >= val) {
20739             // The value is lower than the first tick, so we return the first
20740             // tick.
20741             return tickArray[0];
20742         } else {
20743             for (var i=0, len=tickArray.length; i<len; ++i) {
20744                 var next = i + 1;
20745                 if (tickArray[next] && tickArray[next] >= val) {
20746                     var diff1 = val - tickArray[i];
20747                     var diff2 = tickArray[next] - val;
20748                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20749                 }
20750             }
20751
20752             // The value is larger than the last tick, so we return the last
20753             // tick.
20754             return tickArray[tickArray.length - 1];
20755         }
20756     },
20757
20758     /**
20759      * toString method
20760      * @method toString
20761      * @return {string} string representation of the dd obj
20762      */
20763     toString: function() {
20764         return ("DragDrop " + this.id);
20765     }
20766
20767 });
20768
20769 })();
20770 /*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781
20782 /**
20783  * The drag and drop utility provides a framework for building drag and drop
20784  * applications.  In addition to enabling drag and drop for specific elements,
20785  * the drag and drop elements are tracked by the manager class, and the
20786  * interactions between the various elements are tracked during the drag and
20787  * the implementing code is notified about these important moments.
20788  */
20789
20790 // Only load the library once.  Rewriting the manager class would orphan
20791 // existing drag and drop instances.
20792 if (!Roo.dd.DragDropMgr) {
20793
20794 /**
20795  * @class Roo.dd.DragDropMgr
20796  * DragDropMgr is a singleton that tracks the element interaction for
20797  * all DragDrop items in the window.  Generally, you will not call
20798  * this class directly, but it does have helper methods that could
20799  * be useful in your DragDrop implementations.
20800  * @static
20801  */
20802 Roo.dd.DragDropMgr = function() {
20803
20804     var Event = Roo.EventManager;
20805
20806     return {
20807
20808         /**
20809          * Two dimensional Array of registered DragDrop objects.  The first
20810          * dimension is the DragDrop item group, the second the DragDrop
20811          * object.
20812          * @property ids
20813          * @type {string: string}
20814          * @private
20815          * @static
20816          */
20817         ids: {},
20818
20819         /**
20820          * Array of element ids defined as drag handles.  Used to determine
20821          * if the element that generated the mousedown event is actually the
20822          * handle and not the html element itself.
20823          * @property handleIds
20824          * @type {string: string}
20825          * @private
20826          * @static
20827          */
20828         handleIds: {},
20829
20830         /**
20831          * the DragDrop object that is currently being dragged
20832          * @property dragCurrent
20833          * @type DragDrop
20834          * @private
20835          * @static
20836          **/
20837         dragCurrent: null,
20838
20839         /**
20840          * the DragDrop object(s) that are being hovered over
20841          * @property dragOvers
20842          * @type Array
20843          * @private
20844          * @static
20845          */
20846         dragOvers: {},
20847
20848         /**
20849          * the X distance between the cursor and the object being dragged
20850          * @property deltaX
20851          * @type int
20852          * @private
20853          * @static
20854          */
20855         deltaX: 0,
20856
20857         /**
20858          * the Y distance between the cursor and the object being dragged
20859          * @property deltaY
20860          * @type int
20861          * @private
20862          * @static
20863          */
20864         deltaY: 0,
20865
20866         /**
20867          * Flag to determine if we should prevent the default behavior of the
20868          * events we define. By default this is true, but this can be set to
20869          * false if you need the default behavior (not recommended)
20870          * @property preventDefault
20871          * @type boolean
20872          * @static
20873          */
20874         preventDefault: true,
20875
20876         /**
20877          * Flag to determine if we should stop the propagation of the events
20878          * we generate. This is true by default but you may want to set it to
20879          * false if the html element contains other features that require the
20880          * mouse click.
20881          * @property stopPropagation
20882          * @type boolean
20883          * @static
20884          */
20885         stopPropagation: true,
20886
20887         /**
20888          * Internal flag that is set to true when drag and drop has been
20889          * intialized
20890          * @property initialized
20891          * @private
20892          * @static
20893          */
20894         initalized: false,
20895
20896         /**
20897          * All drag and drop can be disabled.
20898          * @property locked
20899          * @private
20900          * @static
20901          */
20902         locked: false,
20903
20904         /**
20905          * Called the first time an element is registered.
20906          * @method init
20907          * @private
20908          * @static
20909          */
20910         init: function() {
20911             this.initialized = true;
20912         },
20913
20914         /**
20915          * In point mode, drag and drop interaction is defined by the
20916          * location of the cursor during the drag/drop
20917          * @property POINT
20918          * @type int
20919          * @static
20920          */
20921         POINT: 0,
20922
20923         /**
20924          * In intersect mode, drag and drop interactio nis defined by the
20925          * overlap of two or more drag and drop objects.
20926          * @property INTERSECT
20927          * @type int
20928          * @static
20929          */
20930         INTERSECT: 1,
20931
20932         /**
20933          * The current drag and drop mode.  Default: POINT
20934          * @property mode
20935          * @type int
20936          * @static
20937          */
20938         mode: 0,
20939
20940         /**
20941          * Runs method on all drag and drop objects
20942          * @method _execOnAll
20943          * @private
20944          * @static
20945          */
20946         _execOnAll: function(sMethod, args) {
20947             for (var i in this.ids) {
20948                 for (var j in this.ids[i]) {
20949                     var oDD = this.ids[i][j];
20950                     if (! this.isTypeOfDD(oDD)) {
20951                         continue;
20952                     }
20953                     oDD[sMethod].apply(oDD, args);
20954                 }
20955             }
20956         },
20957
20958         /**
20959          * Drag and drop initialization.  Sets up the global event handlers
20960          * @method _onLoad
20961          * @private
20962          * @static
20963          */
20964         _onLoad: function() {
20965
20966             this.init();
20967
20968             if (!Roo.isTouch) {
20969                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20970                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20971             }
20972             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20973             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20974             
20975             Event.on(window,   "unload",    this._onUnload, this, true);
20976             Event.on(window,   "resize",    this._onResize, this, true);
20977             // Event.on(window,   "mouseout",    this._test);
20978
20979         },
20980
20981         /**
20982          * Reset constraints on all drag and drop objs
20983          * @method _onResize
20984          * @private
20985          * @static
20986          */
20987         _onResize: function(e) {
20988             this._execOnAll("resetConstraints", []);
20989         },
20990
20991         /**
20992          * Lock all drag and drop functionality
20993          * @method lock
20994          * @static
20995          */
20996         lock: function() { this.locked = true; },
20997
20998         /**
20999          * Unlock all drag and drop functionality
21000          * @method unlock
21001          * @static
21002          */
21003         unlock: function() { this.locked = false; },
21004
21005         /**
21006          * Is drag and drop locked?
21007          * @method isLocked
21008          * @return {boolean} True if drag and drop is locked, false otherwise.
21009          * @static
21010          */
21011         isLocked: function() { return this.locked; },
21012
21013         /**
21014          * Location cache that is set for all drag drop objects when a drag is
21015          * initiated, cleared when the drag is finished.
21016          * @property locationCache
21017          * @private
21018          * @static
21019          */
21020         locationCache: {},
21021
21022         /**
21023          * Set useCache to false if you want to force object the lookup of each
21024          * drag and drop linked element constantly during a drag.
21025          * @property useCache
21026          * @type boolean
21027          * @static
21028          */
21029         useCache: true,
21030
21031         /**
21032          * The number of pixels that the mouse needs to move after the
21033          * mousedown before the drag is initiated.  Default=3;
21034          * @property clickPixelThresh
21035          * @type int
21036          * @static
21037          */
21038         clickPixelThresh: 3,
21039
21040         /**
21041          * The number of milliseconds after the mousedown event to initiate the
21042          * drag if we don't get a mouseup event. Default=1000
21043          * @property clickTimeThresh
21044          * @type int
21045          * @static
21046          */
21047         clickTimeThresh: 350,
21048
21049         /**
21050          * Flag that indicates that either the drag pixel threshold or the
21051          * mousdown time threshold has been met
21052          * @property dragThreshMet
21053          * @type boolean
21054          * @private
21055          * @static
21056          */
21057         dragThreshMet: false,
21058
21059         /**
21060          * Timeout used for the click time threshold
21061          * @property clickTimeout
21062          * @type Object
21063          * @private
21064          * @static
21065          */
21066         clickTimeout: null,
21067
21068         /**
21069          * The X position of the mousedown event stored for later use when a
21070          * drag threshold is met.
21071          * @property startX
21072          * @type int
21073          * @private
21074          * @static
21075          */
21076         startX: 0,
21077
21078         /**
21079          * The Y position of the mousedown event stored for later use when a
21080          * drag threshold is met.
21081          * @property startY
21082          * @type int
21083          * @private
21084          * @static
21085          */
21086         startY: 0,
21087
21088         /**
21089          * Each DragDrop instance must be registered with the DragDropMgr.
21090          * This is executed in DragDrop.init()
21091          * @method regDragDrop
21092          * @param {DragDrop} oDD the DragDrop object to register
21093          * @param {String} sGroup the name of the group this element belongs to
21094          * @static
21095          */
21096         regDragDrop: function(oDD, sGroup) {
21097             if (!this.initialized) { this.init(); }
21098
21099             if (!this.ids[sGroup]) {
21100                 this.ids[sGroup] = {};
21101             }
21102             this.ids[sGroup][oDD.id] = oDD;
21103         },
21104
21105         /**
21106          * Removes the supplied dd instance from the supplied group. Executed
21107          * by DragDrop.removeFromGroup, so don't call this function directly.
21108          * @method removeDDFromGroup
21109          * @private
21110          * @static
21111          */
21112         removeDDFromGroup: function(oDD, sGroup) {
21113             if (!this.ids[sGroup]) {
21114                 this.ids[sGroup] = {};
21115             }
21116
21117             var obj = this.ids[sGroup];
21118             if (obj && obj[oDD.id]) {
21119                 delete obj[oDD.id];
21120             }
21121         },
21122
21123         /**
21124          * Unregisters a drag and drop item.  This is executed in
21125          * DragDrop.unreg, use that method instead of calling this directly.
21126          * @method _remove
21127          * @private
21128          * @static
21129          */
21130         _remove: function(oDD) {
21131             for (var g in oDD.groups) {
21132                 if (g && this.ids[g][oDD.id]) {
21133                     delete this.ids[g][oDD.id];
21134                 }
21135             }
21136             delete this.handleIds[oDD.id];
21137         },
21138
21139         /**
21140          * Each DragDrop handle element must be registered.  This is done
21141          * automatically when executing DragDrop.setHandleElId()
21142          * @method regHandle
21143          * @param {String} sDDId the DragDrop id this element is a handle for
21144          * @param {String} sHandleId the id of the element that is the drag
21145          * handle
21146          * @static
21147          */
21148         regHandle: function(sDDId, sHandleId) {
21149             if (!this.handleIds[sDDId]) {
21150                 this.handleIds[sDDId] = {};
21151             }
21152             this.handleIds[sDDId][sHandleId] = sHandleId;
21153         },
21154
21155         /**
21156          * Utility function to determine if a given element has been
21157          * registered as a drag drop item.
21158          * @method isDragDrop
21159          * @param {String} id the element id to check
21160          * @return {boolean} true if this element is a DragDrop item,
21161          * false otherwise
21162          * @static
21163          */
21164         isDragDrop: function(id) {
21165             return ( this.getDDById(id) ) ? true : false;
21166         },
21167
21168         /**
21169          * Returns the drag and drop instances that are in all groups the
21170          * passed in instance belongs to.
21171          * @method getRelated
21172          * @param {DragDrop} p_oDD the obj to get related data for
21173          * @param {boolean} bTargetsOnly if true, only return targetable objs
21174          * @return {DragDrop[]} the related instances
21175          * @static
21176          */
21177         getRelated: function(p_oDD, bTargetsOnly) {
21178             var oDDs = [];
21179             for (var i in p_oDD.groups) {
21180                 for (j in this.ids[i]) {
21181                     var dd = this.ids[i][j];
21182                     if (! this.isTypeOfDD(dd)) {
21183                         continue;
21184                     }
21185                     if (!bTargetsOnly || dd.isTarget) {
21186                         oDDs[oDDs.length] = dd;
21187                     }
21188                 }
21189             }
21190
21191             return oDDs;
21192         },
21193
21194         /**
21195          * Returns true if the specified dd target is a legal target for
21196          * the specifice drag obj
21197          * @method isLegalTarget
21198          * @param {DragDrop} the drag obj
21199          * @param {DragDrop} the target
21200          * @return {boolean} true if the target is a legal target for the
21201          * dd obj
21202          * @static
21203          */
21204         isLegalTarget: function (oDD, oTargetDD) {
21205             var targets = this.getRelated(oDD, true);
21206             for (var i=0, len=targets.length;i<len;++i) {
21207                 if (targets[i].id == oTargetDD.id) {
21208                     return true;
21209                 }
21210             }
21211
21212             return false;
21213         },
21214
21215         /**
21216          * My goal is to be able to transparently determine if an object is
21217          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21218          * returns "object", oDD.constructor.toString() always returns
21219          * "DragDrop" and not the name of the subclass.  So for now it just
21220          * evaluates a well-known variable in DragDrop.
21221          * @method isTypeOfDD
21222          * @param {Object} the object to evaluate
21223          * @return {boolean} true if typeof oDD = DragDrop
21224          * @static
21225          */
21226         isTypeOfDD: function (oDD) {
21227             return (oDD && oDD.__ygDragDrop);
21228         },
21229
21230         /**
21231          * Utility function to determine if a given element has been
21232          * registered as a drag drop handle for the given Drag Drop object.
21233          * @method isHandle
21234          * @param {String} id the element id to check
21235          * @return {boolean} true if this element is a DragDrop handle, false
21236          * otherwise
21237          * @static
21238          */
21239         isHandle: function(sDDId, sHandleId) {
21240             return ( this.handleIds[sDDId] &&
21241                             this.handleIds[sDDId][sHandleId] );
21242         },
21243
21244         /**
21245          * Returns the DragDrop instance for a given id
21246          * @method getDDById
21247          * @param {String} id the id of the DragDrop object
21248          * @return {DragDrop} the drag drop object, null if it is not found
21249          * @static
21250          */
21251         getDDById: function(id) {
21252             for (var i in this.ids) {
21253                 if (this.ids[i][id]) {
21254                     return this.ids[i][id];
21255                 }
21256             }
21257             return null;
21258         },
21259
21260         /**
21261          * Fired after a registered DragDrop object gets the mousedown event.
21262          * Sets up the events required to track the object being dragged
21263          * @method handleMouseDown
21264          * @param {Event} e the event
21265          * @param oDD the DragDrop object being dragged
21266          * @private
21267          * @static
21268          */
21269         handleMouseDown: function(e, oDD) {
21270             if(Roo.QuickTips){
21271                 Roo.QuickTips.disable();
21272             }
21273             this.currentTarget = e.getTarget();
21274
21275             this.dragCurrent = oDD;
21276
21277             var el = oDD.getEl();
21278
21279             // track start position
21280             this.startX = e.getPageX();
21281             this.startY = e.getPageY();
21282
21283             this.deltaX = this.startX - el.offsetLeft;
21284             this.deltaY = this.startY - el.offsetTop;
21285
21286             this.dragThreshMet = false;
21287
21288             this.clickTimeout = setTimeout(
21289                     function() {
21290                         var DDM = Roo.dd.DDM;
21291                         DDM.startDrag(DDM.startX, DDM.startY);
21292                     },
21293                     this.clickTimeThresh );
21294         },
21295
21296         /**
21297          * Fired when either the drag pixel threshol or the mousedown hold
21298          * time threshold has been met.
21299          * @method startDrag
21300          * @param x {int} the X position of the original mousedown
21301          * @param y {int} the Y position of the original mousedown
21302          * @static
21303          */
21304         startDrag: function(x, y) {
21305             clearTimeout(this.clickTimeout);
21306             if (this.dragCurrent) {
21307                 this.dragCurrent.b4StartDrag(x, y);
21308                 this.dragCurrent.startDrag(x, y);
21309             }
21310             this.dragThreshMet = true;
21311         },
21312
21313         /**
21314          * Internal function to handle the mouseup event.  Will be invoked
21315          * from the context of the document.
21316          * @method handleMouseUp
21317          * @param {Event} e the event
21318          * @private
21319          * @static
21320          */
21321         handleMouseUp: function(e) {
21322
21323             if(Roo.QuickTips){
21324                 Roo.QuickTips.enable();
21325             }
21326             if (! this.dragCurrent) {
21327                 return;
21328             }
21329
21330             clearTimeout(this.clickTimeout);
21331
21332             if (this.dragThreshMet) {
21333                 this.fireEvents(e, true);
21334             } else {
21335             }
21336
21337             this.stopDrag(e);
21338
21339             this.stopEvent(e);
21340         },
21341
21342         /**
21343          * Utility to stop event propagation and event default, if these
21344          * features are turned on.
21345          * @method stopEvent
21346          * @param {Event} e the event as returned by this.getEvent()
21347          * @static
21348          */
21349         stopEvent: function(e){
21350             if(this.stopPropagation) {
21351                 e.stopPropagation();
21352             }
21353
21354             if (this.preventDefault) {
21355                 e.preventDefault();
21356             }
21357         },
21358
21359         /**
21360          * Internal function to clean up event handlers after the drag
21361          * operation is complete
21362          * @method stopDrag
21363          * @param {Event} e the event
21364          * @private
21365          * @static
21366          */
21367         stopDrag: function(e) {
21368             // Fire the drag end event for the item that was dragged
21369             if (this.dragCurrent) {
21370                 if (this.dragThreshMet) {
21371                     this.dragCurrent.b4EndDrag(e);
21372                     this.dragCurrent.endDrag(e);
21373                 }
21374
21375                 this.dragCurrent.onMouseUp(e);
21376             }
21377
21378             this.dragCurrent = null;
21379             this.dragOvers = {};
21380         },
21381
21382         /**
21383          * Internal function to handle the mousemove event.  Will be invoked
21384          * from the context of the html element.
21385          *
21386          * @TODO figure out what we can do about mouse events lost when the
21387          * user drags objects beyond the window boundary.  Currently we can
21388          * detect this in internet explorer by verifying that the mouse is
21389          * down during the mousemove event.  Firefox doesn't give us the
21390          * button state on the mousemove event.
21391          * @method handleMouseMove
21392          * @param {Event} e the event
21393          * @private
21394          * @static
21395          */
21396         handleMouseMove: function(e) {
21397             if (! this.dragCurrent) {
21398                 return true;
21399             }
21400
21401             // var button = e.which || e.button;
21402
21403             // check for IE mouseup outside of page boundary
21404             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21405                 this.stopEvent(e);
21406                 return this.handleMouseUp(e);
21407             }
21408
21409             if (!this.dragThreshMet) {
21410                 var diffX = Math.abs(this.startX - e.getPageX());
21411                 var diffY = Math.abs(this.startY - e.getPageY());
21412                 if (diffX > this.clickPixelThresh ||
21413                             diffY > this.clickPixelThresh) {
21414                     this.startDrag(this.startX, this.startY);
21415                 }
21416             }
21417
21418             if (this.dragThreshMet) {
21419                 this.dragCurrent.b4Drag(e);
21420                 this.dragCurrent.onDrag(e);
21421                 if(!this.dragCurrent.moveOnly){
21422                     this.fireEvents(e, false);
21423                 }
21424             }
21425
21426             this.stopEvent(e);
21427
21428             return true;
21429         },
21430
21431         /**
21432          * Iterates over all of the DragDrop elements to find ones we are
21433          * hovering over or dropping on
21434          * @method fireEvents
21435          * @param {Event} e the event
21436          * @param {boolean} isDrop is this a drop op or a mouseover op?
21437          * @private
21438          * @static
21439          */
21440         fireEvents: function(e, isDrop) {
21441             var dc = this.dragCurrent;
21442
21443             // If the user did the mouse up outside of the window, we could
21444             // get here even though we have ended the drag.
21445             if (!dc || dc.isLocked()) {
21446                 return;
21447             }
21448
21449             var pt = e.getPoint();
21450
21451             // cache the previous dragOver array
21452             var oldOvers = [];
21453
21454             var outEvts   = [];
21455             var overEvts  = [];
21456             var dropEvts  = [];
21457             var enterEvts = [];
21458
21459             // Check to see if the object(s) we were hovering over is no longer
21460             // being hovered over so we can fire the onDragOut event
21461             for (var i in this.dragOvers) {
21462
21463                 var ddo = this.dragOvers[i];
21464
21465                 if (! this.isTypeOfDD(ddo)) {
21466                     continue;
21467                 }
21468
21469                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21470                     outEvts.push( ddo );
21471                 }
21472
21473                 oldOvers[i] = true;
21474                 delete this.dragOvers[i];
21475             }
21476
21477             for (var sGroup in dc.groups) {
21478
21479                 if ("string" != typeof sGroup) {
21480                     continue;
21481                 }
21482
21483                 for (i in this.ids[sGroup]) {
21484                     var oDD = this.ids[sGroup][i];
21485                     if (! this.isTypeOfDD(oDD)) {
21486                         continue;
21487                     }
21488
21489                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21490                         if (this.isOverTarget(pt, oDD, this.mode)) {
21491                             // look for drop interactions
21492                             if (isDrop) {
21493                                 dropEvts.push( oDD );
21494                             // look for drag enter and drag over interactions
21495                             } else {
21496
21497                                 // initial drag over: dragEnter fires
21498                                 if (!oldOvers[oDD.id]) {
21499                                     enterEvts.push( oDD );
21500                                 // subsequent drag overs: dragOver fires
21501                                 } else {
21502                                     overEvts.push( oDD );
21503                                 }
21504
21505                                 this.dragOvers[oDD.id] = oDD;
21506                             }
21507                         }
21508                     }
21509                 }
21510             }
21511
21512             if (this.mode) {
21513                 if (outEvts.length) {
21514                     dc.b4DragOut(e, outEvts);
21515                     dc.onDragOut(e, outEvts);
21516                 }
21517
21518                 if (enterEvts.length) {
21519                     dc.onDragEnter(e, enterEvts);
21520                 }
21521
21522                 if (overEvts.length) {
21523                     dc.b4DragOver(e, overEvts);
21524                     dc.onDragOver(e, overEvts);
21525                 }
21526
21527                 if (dropEvts.length) {
21528                     dc.b4DragDrop(e, dropEvts);
21529                     dc.onDragDrop(e, dropEvts);
21530                 }
21531
21532             } else {
21533                 // fire dragout events
21534                 var len = 0;
21535                 for (i=0, len=outEvts.length; i<len; ++i) {
21536                     dc.b4DragOut(e, outEvts[i].id);
21537                     dc.onDragOut(e, outEvts[i].id);
21538                 }
21539
21540                 // fire enter events
21541                 for (i=0,len=enterEvts.length; i<len; ++i) {
21542                     // dc.b4DragEnter(e, oDD.id);
21543                     dc.onDragEnter(e, enterEvts[i].id);
21544                 }
21545
21546                 // fire over events
21547                 for (i=0,len=overEvts.length; i<len; ++i) {
21548                     dc.b4DragOver(e, overEvts[i].id);
21549                     dc.onDragOver(e, overEvts[i].id);
21550                 }
21551
21552                 // fire drop events
21553                 for (i=0, len=dropEvts.length; i<len; ++i) {
21554                     dc.b4DragDrop(e, dropEvts[i].id);
21555                     dc.onDragDrop(e, dropEvts[i].id);
21556                 }
21557
21558             }
21559
21560             // notify about a drop that did not find a target
21561             if (isDrop && !dropEvts.length) {
21562                 dc.onInvalidDrop(e);
21563             }
21564
21565         },
21566
21567         /**
21568          * Helper function for getting the best match from the list of drag
21569          * and drop objects returned by the drag and drop events when we are
21570          * in INTERSECT mode.  It returns either the first object that the
21571          * cursor is over, or the object that has the greatest overlap with
21572          * the dragged element.
21573          * @method getBestMatch
21574          * @param  {DragDrop[]} dds The array of drag and drop objects
21575          * targeted
21576          * @return {DragDrop}       The best single match
21577          * @static
21578          */
21579         getBestMatch: function(dds) {
21580             var winner = null;
21581             // Return null if the input is not what we expect
21582             //if (!dds || !dds.length || dds.length == 0) {
21583                // winner = null;
21584             // If there is only one item, it wins
21585             //} else if (dds.length == 1) {
21586
21587             var len = dds.length;
21588
21589             if (len == 1) {
21590                 winner = dds[0];
21591             } else {
21592                 // Loop through the targeted items
21593                 for (var i=0; i<len; ++i) {
21594                     var dd = dds[i];
21595                     // If the cursor is over the object, it wins.  If the
21596                     // cursor is over multiple matches, the first one we come
21597                     // to wins.
21598                     if (dd.cursorIsOver) {
21599                         winner = dd;
21600                         break;
21601                     // Otherwise the object with the most overlap wins
21602                     } else {
21603                         if (!winner ||
21604                             winner.overlap.getArea() < dd.overlap.getArea()) {
21605                             winner = dd;
21606                         }
21607                     }
21608                 }
21609             }
21610
21611             return winner;
21612         },
21613
21614         /**
21615          * Refreshes the cache of the top-left and bottom-right points of the
21616          * drag and drop objects in the specified group(s).  This is in the
21617          * format that is stored in the drag and drop instance, so typical
21618          * usage is:
21619          * <code>
21620          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21621          * </code>
21622          * Alternatively:
21623          * <code>
21624          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21625          * </code>
21626          * @TODO this really should be an indexed array.  Alternatively this
21627          * method could accept both.
21628          * @method refreshCache
21629          * @param {Object} groups an associative array of groups to refresh
21630          * @static
21631          */
21632         refreshCache: function(groups) {
21633             for (var sGroup in groups) {
21634                 if ("string" != typeof sGroup) {
21635                     continue;
21636                 }
21637                 for (var i in this.ids[sGroup]) {
21638                     var oDD = this.ids[sGroup][i];
21639
21640                     if (this.isTypeOfDD(oDD)) {
21641                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21642                         var loc = this.getLocation(oDD);
21643                         if (loc) {
21644                             this.locationCache[oDD.id] = loc;
21645                         } else {
21646                             delete this.locationCache[oDD.id];
21647                             // this will unregister the drag and drop object if
21648                             // the element is not in a usable state
21649                             // oDD.unreg();
21650                         }
21651                     }
21652                 }
21653             }
21654         },
21655
21656         /**
21657          * This checks to make sure an element exists and is in the DOM.  The
21658          * main purpose is to handle cases where innerHTML is used to remove
21659          * drag and drop objects from the DOM.  IE provides an 'unspecified
21660          * error' when trying to access the offsetParent of such an element
21661          * @method verifyEl
21662          * @param {HTMLElement} el the element to check
21663          * @return {boolean} true if the element looks usable
21664          * @static
21665          */
21666         verifyEl: function(el) {
21667             if (el) {
21668                 var parent;
21669                 if(Roo.isIE){
21670                     try{
21671                         parent = el.offsetParent;
21672                     }catch(e){}
21673                 }else{
21674                     parent = el.offsetParent;
21675                 }
21676                 if (parent) {
21677                     return true;
21678                 }
21679             }
21680
21681             return false;
21682         },
21683
21684         /**
21685          * Returns a Region object containing the drag and drop element's position
21686          * and size, including the padding configured for it
21687          * @method getLocation
21688          * @param {DragDrop} oDD the drag and drop object to get the
21689          *                       location for
21690          * @return {Roo.lib.Region} a Region object representing the total area
21691          *                             the element occupies, including any padding
21692          *                             the instance is configured for.
21693          * @static
21694          */
21695         getLocation: function(oDD) {
21696             if (! this.isTypeOfDD(oDD)) {
21697                 return null;
21698             }
21699
21700             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21701
21702             try {
21703                 pos= Roo.lib.Dom.getXY(el);
21704             } catch (e) { }
21705
21706             if (!pos) {
21707                 return null;
21708             }
21709
21710             x1 = pos[0];
21711             x2 = x1 + el.offsetWidth;
21712             y1 = pos[1];
21713             y2 = y1 + el.offsetHeight;
21714
21715             t = y1 - oDD.padding[0];
21716             r = x2 + oDD.padding[1];
21717             b = y2 + oDD.padding[2];
21718             l = x1 - oDD.padding[3];
21719
21720             return new Roo.lib.Region( t, r, b, l );
21721         },
21722
21723         /**
21724          * Checks the cursor location to see if it over the target
21725          * @method isOverTarget
21726          * @param {Roo.lib.Point} pt The point to evaluate
21727          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21728          * @return {boolean} true if the mouse is over the target
21729          * @private
21730          * @static
21731          */
21732         isOverTarget: function(pt, oTarget, intersect) {
21733             // use cache if available
21734             var loc = this.locationCache[oTarget.id];
21735             if (!loc || !this.useCache) {
21736                 loc = this.getLocation(oTarget);
21737                 this.locationCache[oTarget.id] = loc;
21738
21739             }
21740
21741             if (!loc) {
21742                 return false;
21743             }
21744
21745             oTarget.cursorIsOver = loc.contains( pt );
21746
21747             // DragDrop is using this as a sanity check for the initial mousedown
21748             // in this case we are done.  In POINT mode, if the drag obj has no
21749             // contraints, we are also done. Otherwise we need to evaluate the
21750             // location of the target as related to the actual location of the
21751             // dragged element.
21752             var dc = this.dragCurrent;
21753             if (!dc || !dc.getTargetCoord ||
21754                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21755                 return oTarget.cursorIsOver;
21756             }
21757
21758             oTarget.overlap = null;
21759
21760             // Get the current location of the drag element, this is the
21761             // location of the mouse event less the delta that represents
21762             // where the original mousedown happened on the element.  We
21763             // need to consider constraints and ticks as well.
21764             var pos = dc.getTargetCoord(pt.x, pt.y);
21765
21766             var el = dc.getDragEl();
21767             var curRegion = new Roo.lib.Region( pos.y,
21768                                                    pos.x + el.offsetWidth,
21769                                                    pos.y + el.offsetHeight,
21770                                                    pos.x );
21771
21772             var overlap = curRegion.intersect(loc);
21773
21774             if (overlap) {
21775                 oTarget.overlap = overlap;
21776                 return (intersect) ? true : oTarget.cursorIsOver;
21777             } else {
21778                 return false;
21779             }
21780         },
21781
21782         /**
21783          * unload event handler
21784          * @method _onUnload
21785          * @private
21786          * @static
21787          */
21788         _onUnload: function(e, me) {
21789             Roo.dd.DragDropMgr.unregAll();
21790         },
21791
21792         /**
21793          * Cleans up the drag and drop events and objects.
21794          * @method unregAll
21795          * @private
21796          * @static
21797          */
21798         unregAll: function() {
21799
21800             if (this.dragCurrent) {
21801                 this.stopDrag();
21802                 this.dragCurrent = null;
21803             }
21804
21805             this._execOnAll("unreg", []);
21806
21807             for (i in this.elementCache) {
21808                 delete this.elementCache[i];
21809             }
21810
21811             this.elementCache = {};
21812             this.ids = {};
21813         },
21814
21815         /**
21816          * A cache of DOM elements
21817          * @property elementCache
21818          * @private
21819          * @static
21820          */
21821         elementCache: {},
21822
21823         /**
21824          * Get the wrapper for the DOM element specified
21825          * @method getElWrapper
21826          * @param {String} id the id of the element to get
21827          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21828          * @private
21829          * @deprecated This wrapper isn't that useful
21830          * @static
21831          */
21832         getElWrapper: function(id) {
21833             var oWrapper = this.elementCache[id];
21834             if (!oWrapper || !oWrapper.el) {
21835                 oWrapper = this.elementCache[id] =
21836                     new this.ElementWrapper(Roo.getDom(id));
21837             }
21838             return oWrapper;
21839         },
21840
21841         /**
21842          * Returns the actual DOM element
21843          * @method getElement
21844          * @param {String} id the id of the elment to get
21845          * @return {Object} The element
21846          * @deprecated use Roo.getDom instead
21847          * @static
21848          */
21849         getElement: function(id) {
21850             return Roo.getDom(id);
21851         },
21852
21853         /**
21854          * Returns the style property for the DOM element (i.e.,
21855          * document.getElById(id).style)
21856          * @method getCss
21857          * @param {String} id the id of the elment to get
21858          * @return {Object} The style property of the element
21859          * @deprecated use Roo.getDom instead
21860          * @static
21861          */
21862         getCss: function(id) {
21863             var el = Roo.getDom(id);
21864             return (el) ? el.style : null;
21865         },
21866
21867         /**
21868          * Inner class for cached elements
21869          * @class DragDropMgr.ElementWrapper
21870          * @for DragDropMgr
21871          * @private
21872          * @deprecated
21873          */
21874         ElementWrapper: function(el) {
21875                 /**
21876                  * The element
21877                  * @property el
21878                  */
21879                 this.el = el || null;
21880                 /**
21881                  * The element id
21882                  * @property id
21883                  */
21884                 this.id = this.el && el.id;
21885                 /**
21886                  * A reference to the style property
21887                  * @property css
21888                  */
21889                 this.css = this.el && el.style;
21890             },
21891
21892         /**
21893          * Returns the X position of an html element
21894          * @method getPosX
21895          * @param el the element for which to get the position
21896          * @return {int} the X coordinate
21897          * @for DragDropMgr
21898          * @deprecated use Roo.lib.Dom.getX instead
21899          * @static
21900          */
21901         getPosX: function(el) {
21902             return Roo.lib.Dom.getX(el);
21903         },
21904
21905         /**
21906          * Returns the Y position of an html element
21907          * @method getPosY
21908          * @param el the element for which to get the position
21909          * @return {int} the Y coordinate
21910          * @deprecated use Roo.lib.Dom.getY instead
21911          * @static
21912          */
21913         getPosY: function(el) {
21914             return Roo.lib.Dom.getY(el);
21915         },
21916
21917         /**
21918          * Swap two nodes.  In IE, we use the native method, for others we
21919          * emulate the IE behavior
21920          * @method swapNode
21921          * @param n1 the first node to swap
21922          * @param n2 the other node to swap
21923          * @static
21924          */
21925         swapNode: function(n1, n2) {
21926             if (n1.swapNode) {
21927                 n1.swapNode(n2);
21928             } else {
21929                 var p = n2.parentNode;
21930                 var s = n2.nextSibling;
21931
21932                 if (s == n1) {
21933                     p.insertBefore(n1, n2);
21934                 } else if (n2 == n1.nextSibling) {
21935                     p.insertBefore(n2, n1);
21936                 } else {
21937                     n1.parentNode.replaceChild(n2, n1);
21938                     p.insertBefore(n1, s);
21939                 }
21940             }
21941         },
21942
21943         /**
21944          * Returns the current scroll position
21945          * @method getScroll
21946          * @private
21947          * @static
21948          */
21949         getScroll: function () {
21950             var t, l, dde=document.documentElement, db=document.body;
21951             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21952                 t = dde.scrollTop;
21953                 l = dde.scrollLeft;
21954             } else if (db) {
21955                 t = db.scrollTop;
21956                 l = db.scrollLeft;
21957             } else {
21958
21959             }
21960             return { top: t, left: l };
21961         },
21962
21963         /**
21964          * Returns the specified element style property
21965          * @method getStyle
21966          * @param {HTMLElement} el          the element
21967          * @param {string}      styleProp   the style property
21968          * @return {string} The value of the style property
21969          * @deprecated use Roo.lib.Dom.getStyle
21970          * @static
21971          */
21972         getStyle: function(el, styleProp) {
21973             return Roo.fly(el).getStyle(styleProp);
21974         },
21975
21976         /**
21977          * Gets the scrollTop
21978          * @method getScrollTop
21979          * @return {int} the document's scrollTop
21980          * @static
21981          */
21982         getScrollTop: function () { return this.getScroll().top; },
21983
21984         /**
21985          * Gets the scrollLeft
21986          * @method getScrollLeft
21987          * @return {int} the document's scrollTop
21988          * @static
21989          */
21990         getScrollLeft: function () { return this.getScroll().left; },
21991
21992         /**
21993          * Sets the x/y position of an element to the location of the
21994          * target element.
21995          * @method moveToEl
21996          * @param {HTMLElement} moveEl      The element to move
21997          * @param {HTMLElement} targetEl    The position reference element
21998          * @static
21999          */
22000         moveToEl: function (moveEl, targetEl) {
22001             var aCoord = Roo.lib.Dom.getXY(targetEl);
22002             Roo.lib.Dom.setXY(moveEl, aCoord);
22003         },
22004
22005         /**
22006          * Numeric array sort function
22007          * @method numericSort
22008          * @static
22009          */
22010         numericSort: function(a, b) { return (a - b); },
22011
22012         /**
22013          * Internal counter
22014          * @property _timeoutCount
22015          * @private
22016          * @static
22017          */
22018         _timeoutCount: 0,
22019
22020         /**
22021          * Trying to make the load order less important.  Without this we get
22022          * an error if this file is loaded before the Event Utility.
22023          * @method _addListeners
22024          * @private
22025          * @static
22026          */
22027         _addListeners: function() {
22028             var DDM = Roo.dd.DDM;
22029             if ( Roo.lib.Event && document ) {
22030                 DDM._onLoad();
22031             } else {
22032                 if (DDM._timeoutCount > 2000) {
22033                 } else {
22034                     setTimeout(DDM._addListeners, 10);
22035                     if (document && document.body) {
22036                         DDM._timeoutCount += 1;
22037                     }
22038                 }
22039             }
22040         },
22041
22042         /**
22043          * Recursively searches the immediate parent and all child nodes for
22044          * the handle element in order to determine wheter or not it was
22045          * clicked.
22046          * @method handleWasClicked
22047          * @param node the html element to inspect
22048          * @static
22049          */
22050         handleWasClicked: function(node, id) {
22051             if (this.isHandle(id, node.id)) {
22052                 return true;
22053             } else {
22054                 // check to see if this is a text node child of the one we want
22055                 var p = node.parentNode;
22056
22057                 while (p) {
22058                     if (this.isHandle(id, p.id)) {
22059                         return true;
22060                     } else {
22061                         p = p.parentNode;
22062                     }
22063                 }
22064             }
22065
22066             return false;
22067         }
22068
22069     };
22070
22071 }();
22072
22073 // shorter alias, save a few bytes
22074 Roo.dd.DDM = Roo.dd.DragDropMgr;
22075 Roo.dd.DDM._addListeners();
22076
22077 }/*
22078  * Based on:
22079  * Ext JS Library 1.1.1
22080  * Copyright(c) 2006-2007, Ext JS, LLC.
22081  *
22082  * Originally Released Under LGPL - original licence link has changed is not relivant.
22083  *
22084  * Fork - LGPL
22085  * <script type="text/javascript">
22086  */
22087
22088 /**
22089  * @class Roo.dd.DD
22090  * A DragDrop implementation where the linked element follows the
22091  * mouse cursor during a drag.
22092  * @extends Roo.dd.DragDrop
22093  * @constructor
22094  * @param {String} id the id of the linked element
22095  * @param {String} sGroup the group of related DragDrop items
22096  * @param {object} config an object containing configurable attributes
22097  *                Valid properties for DD:
22098  *                    scroll
22099  */
22100 Roo.dd.DD = function(id, sGroup, config) {
22101     if (id) {
22102         this.init(id, sGroup, config);
22103     }
22104 };
22105
22106 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22107
22108     /**
22109      * When set to true, the utility automatically tries to scroll the browser
22110      * window wehn a drag and drop element is dragged near the viewport boundary.
22111      * Defaults to true.
22112      * @property scroll
22113      * @type boolean
22114      */
22115     scroll: true,
22116
22117     /**
22118      * Sets the pointer offset to the distance between the linked element's top
22119      * left corner and the location the element was clicked
22120      * @method autoOffset
22121      * @param {int} iPageX the X coordinate of the click
22122      * @param {int} iPageY the Y coordinate of the click
22123      */
22124     autoOffset: function(iPageX, iPageY) {
22125         var x = iPageX - this.startPageX;
22126         var y = iPageY - this.startPageY;
22127         this.setDelta(x, y);
22128     },
22129
22130     /**
22131      * Sets the pointer offset.  You can call this directly to force the
22132      * offset to be in a particular location (e.g., pass in 0,0 to set it
22133      * to the center of the object)
22134      * @method setDelta
22135      * @param {int} iDeltaX the distance from the left
22136      * @param {int} iDeltaY the distance from the top
22137      */
22138     setDelta: function(iDeltaX, iDeltaY) {
22139         this.deltaX = iDeltaX;
22140         this.deltaY = iDeltaY;
22141     },
22142
22143     /**
22144      * Sets the drag element to the location of the mousedown or click event,
22145      * maintaining the cursor location relative to the location on the element
22146      * that was clicked.  Override this if you want to place the element in a
22147      * location other than where the cursor is.
22148      * @method setDragElPos
22149      * @param {int} iPageX the X coordinate of the mousedown or drag event
22150      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22151      */
22152     setDragElPos: function(iPageX, iPageY) {
22153         // the first time we do this, we are going to check to make sure
22154         // the element has css positioning
22155
22156         var el = this.getDragEl();
22157         this.alignElWithMouse(el, iPageX, iPageY);
22158     },
22159
22160     /**
22161      * Sets the element to the location of the mousedown or click event,
22162      * maintaining the cursor location relative to the location on the element
22163      * that was clicked.  Override this if you want to place the element in a
22164      * location other than where the cursor is.
22165      * @method alignElWithMouse
22166      * @param {HTMLElement} el the element to move
22167      * @param {int} iPageX the X coordinate of the mousedown or drag event
22168      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22169      */
22170     alignElWithMouse: function(el, iPageX, iPageY) {
22171         var oCoord = this.getTargetCoord(iPageX, iPageY);
22172         var fly = el.dom ? el : Roo.fly(el);
22173         if (!this.deltaSetXY) {
22174             var aCoord = [oCoord.x, oCoord.y];
22175             fly.setXY(aCoord);
22176             var newLeft = fly.getLeft(true);
22177             var newTop  = fly.getTop(true);
22178             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22179         } else {
22180             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22181         }
22182
22183         this.cachePosition(oCoord.x, oCoord.y);
22184         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22185         return oCoord;
22186     },
22187
22188     /**
22189      * Saves the most recent position so that we can reset the constraints and
22190      * tick marks on-demand.  We need to know this so that we can calculate the
22191      * number of pixels the element is offset from its original position.
22192      * @method cachePosition
22193      * @param iPageX the current x position (optional, this just makes it so we
22194      * don't have to look it up again)
22195      * @param iPageY the current y position (optional, this just makes it so we
22196      * don't have to look it up again)
22197      */
22198     cachePosition: function(iPageX, iPageY) {
22199         if (iPageX) {
22200             this.lastPageX = iPageX;
22201             this.lastPageY = iPageY;
22202         } else {
22203             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22204             this.lastPageX = aCoord[0];
22205             this.lastPageY = aCoord[1];
22206         }
22207     },
22208
22209     /**
22210      * Auto-scroll the window if the dragged object has been moved beyond the
22211      * visible window boundary.
22212      * @method autoScroll
22213      * @param {int} x the drag element's x position
22214      * @param {int} y the drag element's y position
22215      * @param {int} h the height of the drag element
22216      * @param {int} w the width of the drag element
22217      * @private
22218      */
22219     autoScroll: function(x, y, h, w) {
22220
22221         if (this.scroll) {
22222             // The client height
22223             var clientH = Roo.lib.Dom.getViewWidth();
22224
22225             // The client width
22226             var clientW = Roo.lib.Dom.getViewHeight();
22227
22228             // The amt scrolled down
22229             var st = this.DDM.getScrollTop();
22230
22231             // The amt scrolled right
22232             var sl = this.DDM.getScrollLeft();
22233
22234             // Location of the bottom of the element
22235             var bot = h + y;
22236
22237             // Location of the right of the element
22238             var right = w + x;
22239
22240             // The distance from the cursor to the bottom of the visible area,
22241             // adjusted so that we don't scroll if the cursor is beyond the
22242             // element drag constraints
22243             var toBot = (clientH + st - y - this.deltaY);
22244
22245             // The distance from the cursor to the right of the visible area
22246             var toRight = (clientW + sl - x - this.deltaX);
22247
22248
22249             // How close to the edge the cursor must be before we scroll
22250             // var thresh = (document.all) ? 100 : 40;
22251             var thresh = 40;
22252
22253             // How many pixels to scroll per autoscroll op.  This helps to reduce
22254             // clunky scrolling. IE is more sensitive about this ... it needs this
22255             // value to be higher.
22256             var scrAmt = (document.all) ? 80 : 30;
22257
22258             // Scroll down if we are near the bottom of the visible page and the
22259             // obj extends below the crease
22260             if ( bot > clientH && toBot < thresh ) {
22261                 window.scrollTo(sl, st + scrAmt);
22262             }
22263
22264             // Scroll up if the window is scrolled down and the top of the object
22265             // goes above the top border
22266             if ( y < st && st > 0 && y - st < thresh ) {
22267                 window.scrollTo(sl, st - scrAmt);
22268             }
22269
22270             // Scroll right if the obj is beyond the right border and the cursor is
22271             // near the border.
22272             if ( right > clientW && toRight < thresh ) {
22273                 window.scrollTo(sl + scrAmt, st);
22274             }
22275
22276             // Scroll left if the window has been scrolled to the right and the obj
22277             // extends past the left border
22278             if ( x < sl && sl > 0 && x - sl < thresh ) {
22279                 window.scrollTo(sl - scrAmt, st);
22280             }
22281         }
22282     },
22283
22284     /**
22285      * Finds the location the element should be placed if we want to move
22286      * it to where the mouse location less the click offset would place us.
22287      * @method getTargetCoord
22288      * @param {int} iPageX the X coordinate of the click
22289      * @param {int} iPageY the Y coordinate of the click
22290      * @return an object that contains the coordinates (Object.x and Object.y)
22291      * @private
22292      */
22293     getTargetCoord: function(iPageX, iPageY) {
22294
22295
22296         var x = iPageX - this.deltaX;
22297         var y = iPageY - this.deltaY;
22298
22299         if (this.constrainX) {
22300             if (x < this.minX) { x = this.minX; }
22301             if (x > this.maxX) { x = this.maxX; }
22302         }
22303
22304         if (this.constrainY) {
22305             if (y < this.minY) { y = this.minY; }
22306             if (y > this.maxY) { y = this.maxY; }
22307         }
22308
22309         x = this.getTick(x, this.xTicks);
22310         y = this.getTick(y, this.yTicks);
22311
22312
22313         return {x:x, y:y};
22314     },
22315
22316     /*
22317      * Sets up config options specific to this class. Overrides
22318      * Roo.dd.DragDrop, but all versions of this method through the
22319      * inheritance chain are called
22320      */
22321     applyConfig: function() {
22322         Roo.dd.DD.superclass.applyConfig.call(this);
22323         this.scroll = (this.config.scroll !== false);
22324     },
22325
22326     /*
22327      * Event that fires prior to the onMouseDown event.  Overrides
22328      * Roo.dd.DragDrop.
22329      */
22330     b4MouseDown: function(e) {
22331         // this.resetConstraints();
22332         this.autoOffset(e.getPageX(),
22333                             e.getPageY());
22334     },
22335
22336     /*
22337      * Event that fires prior to the onDrag event.  Overrides
22338      * Roo.dd.DragDrop.
22339      */
22340     b4Drag: function(e) {
22341         this.setDragElPos(e.getPageX(),
22342                             e.getPageY());
22343     },
22344
22345     toString: function() {
22346         return ("DD " + this.id);
22347     }
22348
22349     //////////////////////////////////////////////////////////////////////////
22350     // Debugging ygDragDrop events that can be overridden
22351     //////////////////////////////////////////////////////////////////////////
22352     /*
22353     startDrag: function(x, y) {
22354     },
22355
22356     onDrag: function(e) {
22357     },
22358
22359     onDragEnter: function(e, id) {
22360     },
22361
22362     onDragOver: function(e, id) {
22363     },
22364
22365     onDragOut: function(e, id) {
22366     },
22367
22368     onDragDrop: function(e, id) {
22369     },
22370
22371     endDrag: function(e) {
22372     }
22373
22374     */
22375
22376 });/*
22377  * Based on:
22378  * Ext JS Library 1.1.1
22379  * Copyright(c) 2006-2007, Ext JS, LLC.
22380  *
22381  * Originally Released Under LGPL - original licence link has changed is not relivant.
22382  *
22383  * Fork - LGPL
22384  * <script type="text/javascript">
22385  */
22386
22387 /**
22388  * @class Roo.dd.DDProxy
22389  * A DragDrop implementation that inserts an empty, bordered div into
22390  * the document that follows the cursor during drag operations.  At the time of
22391  * the click, the frame div is resized to the dimensions of the linked html
22392  * element, and moved to the exact location of the linked element.
22393  *
22394  * References to the "frame" element refer to the single proxy element that
22395  * was created to be dragged in place of all DDProxy elements on the
22396  * page.
22397  *
22398  * @extends Roo.dd.DD
22399  * @constructor
22400  * @param {String} id the id of the linked html element
22401  * @param {String} sGroup the group of related DragDrop objects
22402  * @param {object} config an object containing configurable attributes
22403  *                Valid properties for DDProxy in addition to those in DragDrop:
22404  *                   resizeFrame, centerFrame, dragElId
22405  */
22406 Roo.dd.DDProxy = function(id, sGroup, config) {
22407     if (id) {
22408         this.init(id, sGroup, config);
22409         this.initFrame();
22410     }
22411 };
22412
22413 /**
22414  * The default drag frame div id
22415  * @property Roo.dd.DDProxy.dragElId
22416  * @type String
22417  * @static
22418  */
22419 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22420
22421 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22422
22423     /**
22424      * By default we resize the drag frame to be the same size as the element
22425      * we want to drag (this is to get the frame effect).  We can turn it off
22426      * if we want a different behavior.
22427      * @property resizeFrame
22428      * @type boolean
22429      */
22430     resizeFrame: true,
22431
22432     /**
22433      * By default the frame is positioned exactly where the drag element is, so
22434      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22435      * you do not have constraints on the obj is to have the drag frame centered
22436      * around the cursor.  Set centerFrame to true for this effect.
22437      * @property centerFrame
22438      * @type boolean
22439      */
22440     centerFrame: false,
22441
22442     /**
22443      * Creates the proxy element if it does not yet exist
22444      * @method createFrame
22445      */
22446     createFrame: function() {
22447         var self = this;
22448         var body = document.body;
22449
22450         if (!body || !body.firstChild) {
22451             setTimeout( function() { self.createFrame(); }, 50 );
22452             return;
22453         }
22454
22455         var div = this.getDragEl();
22456
22457         if (!div) {
22458             div    = document.createElement("div");
22459             div.id = this.dragElId;
22460             var s  = div.style;
22461
22462             s.position   = "absolute";
22463             s.visibility = "hidden";
22464             s.cursor     = "move";
22465             s.border     = "2px solid #aaa";
22466             s.zIndex     = 999;
22467
22468             // appendChild can blow up IE if invoked prior to the window load event
22469             // while rendering a table.  It is possible there are other scenarios
22470             // that would cause this to happen as well.
22471             body.insertBefore(div, body.firstChild);
22472         }
22473     },
22474
22475     /**
22476      * Initialization for the drag frame element.  Must be called in the
22477      * constructor of all subclasses
22478      * @method initFrame
22479      */
22480     initFrame: function() {
22481         this.createFrame();
22482     },
22483
22484     applyConfig: function() {
22485         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22486
22487         this.resizeFrame = (this.config.resizeFrame !== false);
22488         this.centerFrame = (this.config.centerFrame);
22489         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22490     },
22491
22492     /**
22493      * Resizes the drag frame to the dimensions of the clicked object, positions
22494      * it over the object, and finally displays it
22495      * @method showFrame
22496      * @param {int} iPageX X click position
22497      * @param {int} iPageY Y click position
22498      * @private
22499      */
22500     showFrame: function(iPageX, iPageY) {
22501         var el = this.getEl();
22502         var dragEl = this.getDragEl();
22503         var s = dragEl.style;
22504
22505         this._resizeProxy();
22506
22507         if (this.centerFrame) {
22508             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22509                            Math.round(parseInt(s.height, 10)/2) );
22510         }
22511
22512         this.setDragElPos(iPageX, iPageY);
22513
22514         Roo.fly(dragEl).show();
22515     },
22516
22517     /**
22518      * The proxy is automatically resized to the dimensions of the linked
22519      * element when a drag is initiated, unless resizeFrame is set to false
22520      * @method _resizeProxy
22521      * @private
22522      */
22523     _resizeProxy: function() {
22524         if (this.resizeFrame) {
22525             var el = this.getEl();
22526             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22527         }
22528     },
22529
22530     // overrides Roo.dd.DragDrop
22531     b4MouseDown: function(e) {
22532         var x = e.getPageX();
22533         var y = e.getPageY();
22534         this.autoOffset(x, y);
22535         this.setDragElPos(x, y);
22536     },
22537
22538     // overrides Roo.dd.DragDrop
22539     b4StartDrag: function(x, y) {
22540         // show the drag frame
22541         this.showFrame(x, y);
22542     },
22543
22544     // overrides Roo.dd.DragDrop
22545     b4EndDrag: function(e) {
22546         Roo.fly(this.getDragEl()).hide();
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     // By default we try to move the element to the last location of the frame.
22551     // This is so that the default behavior mirrors that of Roo.dd.DD.
22552     endDrag: function(e) {
22553
22554         var lel = this.getEl();
22555         var del = this.getDragEl();
22556
22557         // Show the drag frame briefly so we can get its position
22558         del.style.visibility = "";
22559
22560         this.beforeMove();
22561         // Hide the linked element before the move to get around a Safari
22562         // rendering bug.
22563         lel.style.visibility = "hidden";
22564         Roo.dd.DDM.moveToEl(lel, del);
22565         del.style.visibility = "hidden";
22566         lel.style.visibility = "";
22567
22568         this.afterDrag();
22569     },
22570
22571     beforeMove : function(){
22572
22573     },
22574
22575     afterDrag : function(){
22576
22577     },
22578
22579     toString: function() {
22580         return ("DDProxy " + this.id);
22581     }
22582
22583 });
22584 /*
22585  * Based on:
22586  * Ext JS Library 1.1.1
22587  * Copyright(c) 2006-2007, Ext JS, LLC.
22588  *
22589  * Originally Released Under LGPL - original licence link has changed is not relivant.
22590  *
22591  * Fork - LGPL
22592  * <script type="text/javascript">
22593  */
22594
22595  /**
22596  * @class Roo.dd.DDTarget
22597  * A DragDrop implementation that does not move, but can be a drop
22598  * target.  You would get the same result by simply omitting implementation
22599  * for the event callbacks, but this way we reduce the processing cost of the
22600  * event listener and the callbacks.
22601  * @extends Roo.dd.DragDrop
22602  * @constructor
22603  * @param {String} id the id of the element that is a drop target
22604  * @param {String} sGroup the group of related DragDrop objects
22605  * @param {object} config an object containing configurable attributes
22606  *                 Valid properties for DDTarget in addition to those in
22607  *                 DragDrop:
22608  *                    none
22609  */
22610 Roo.dd.DDTarget = function(id, sGroup, config) {
22611     if (id) {
22612         this.initTarget(id, sGroup, config);
22613     }
22614     if (config && (config.listeners || config.events)) { 
22615         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22616             listeners : config.listeners || {}, 
22617             events : config.events || {} 
22618         });    
22619     }
22620 };
22621
22622 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22623 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22624     toString: function() {
22625         return ("DDTarget " + this.id);
22626     }
22627 });
22628 /*
22629  * Based on:
22630  * Ext JS Library 1.1.1
22631  * Copyright(c) 2006-2007, Ext JS, LLC.
22632  *
22633  * Originally Released Under LGPL - original licence link has changed is not relivant.
22634  *
22635  * Fork - LGPL
22636  * <script type="text/javascript">
22637  */
22638  
22639
22640 /**
22641  * @class Roo.dd.ScrollManager
22642  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22643  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22644  * @static
22645  */
22646 Roo.dd.ScrollManager = function(){
22647     var ddm = Roo.dd.DragDropMgr;
22648     var els = {};
22649     var dragEl = null;
22650     var proc = {};
22651     
22652     
22653     
22654     var onStop = function(e){
22655         dragEl = null;
22656         clearProc();
22657     };
22658     
22659     var triggerRefresh = function(){
22660         if(ddm.dragCurrent){
22661              ddm.refreshCache(ddm.dragCurrent.groups);
22662         }
22663     };
22664     
22665     var doScroll = function(){
22666         if(ddm.dragCurrent){
22667             var dds = Roo.dd.ScrollManager;
22668             if(!dds.animate){
22669                 if(proc.el.scroll(proc.dir, dds.increment)){
22670                     triggerRefresh();
22671                 }
22672             }else{
22673                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22674             }
22675         }
22676     };
22677     
22678     var clearProc = function(){
22679         if(proc.id){
22680             clearInterval(proc.id);
22681         }
22682         proc.id = 0;
22683         proc.el = null;
22684         proc.dir = "";
22685     };
22686     
22687     var startProc = function(el, dir){
22688          Roo.log('scroll startproc');
22689         clearProc();
22690         proc.el = el;
22691         proc.dir = dir;
22692         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22693     };
22694     
22695     var onFire = function(e, isDrop){
22696        
22697         if(isDrop || !ddm.dragCurrent){ return; }
22698         var dds = Roo.dd.ScrollManager;
22699         if(!dragEl || dragEl != ddm.dragCurrent){
22700             dragEl = ddm.dragCurrent;
22701             // refresh regions on drag start
22702             dds.refreshCache();
22703         }
22704         
22705         var xy = Roo.lib.Event.getXY(e);
22706         var pt = new Roo.lib.Point(xy[0], xy[1]);
22707         for(var id in els){
22708             var el = els[id], r = el._region;
22709             if(r && r.contains(pt) && el.isScrollable()){
22710                 if(r.bottom - pt.y <= dds.thresh){
22711                     if(proc.el != el){
22712                         startProc(el, "down");
22713                     }
22714                     return;
22715                 }else if(r.right - pt.x <= dds.thresh){
22716                     if(proc.el != el){
22717                         startProc(el, "left");
22718                     }
22719                     return;
22720                 }else if(pt.y - r.top <= dds.thresh){
22721                     if(proc.el != el){
22722                         startProc(el, "up");
22723                     }
22724                     return;
22725                 }else if(pt.x - r.left <= dds.thresh){
22726                     if(proc.el != el){
22727                         startProc(el, "right");
22728                     }
22729                     return;
22730                 }
22731             }
22732         }
22733         clearProc();
22734     };
22735     
22736     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22737     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22738     
22739     return {
22740         /**
22741          * Registers new overflow element(s) to auto scroll
22742          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22743          */
22744         register : function(el){
22745             if(el instanceof Array){
22746                 for(var i = 0, len = el.length; i < len; i++) {
22747                         this.register(el[i]);
22748                 }
22749             }else{
22750                 el = Roo.get(el);
22751                 els[el.id] = el;
22752             }
22753             Roo.dd.ScrollManager.els = els;
22754         },
22755         
22756         /**
22757          * Unregisters overflow element(s) so they are no longer scrolled
22758          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22759          */
22760         unregister : function(el){
22761             if(el instanceof Array){
22762                 for(var i = 0, len = el.length; i < len; i++) {
22763                         this.unregister(el[i]);
22764                 }
22765             }else{
22766                 el = Roo.get(el);
22767                 delete els[el.id];
22768             }
22769         },
22770         
22771         /**
22772          * The number of pixels from the edge of a container the pointer needs to be to 
22773          * trigger scrolling (defaults to 25)
22774          * @type Number
22775          */
22776         thresh : 25,
22777         
22778         /**
22779          * The number of pixels to scroll in each scroll increment (defaults to 50)
22780          * @type Number
22781          */
22782         increment : 100,
22783         
22784         /**
22785          * The frequency of scrolls in milliseconds (defaults to 500)
22786          * @type Number
22787          */
22788         frequency : 500,
22789         
22790         /**
22791          * True to animate the scroll (defaults to true)
22792          * @type Boolean
22793          */
22794         animate: true,
22795         
22796         /**
22797          * The animation duration in seconds - 
22798          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22799          * @type Number
22800          */
22801         animDuration: .4,
22802         
22803         /**
22804          * Manually trigger a cache refresh.
22805          */
22806         refreshCache : function(){
22807             for(var id in els){
22808                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22809                     els[id]._region = els[id].getRegion();
22810                 }
22811             }
22812         }
22813     };
22814 }();/*
22815  * Based on:
22816  * Ext JS Library 1.1.1
22817  * Copyright(c) 2006-2007, Ext JS, LLC.
22818  *
22819  * Originally Released Under LGPL - original licence link has changed is not relivant.
22820  *
22821  * Fork - LGPL
22822  * <script type="text/javascript">
22823  */
22824  
22825
22826 /**
22827  * @class Roo.dd.Registry
22828  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22829  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22830  * @static
22831  */
22832 Roo.dd.Registry = function(){
22833     var elements = {}; 
22834     var handles = {}; 
22835     var autoIdSeed = 0;
22836
22837     var getId = function(el, autogen){
22838         if(typeof el == "string"){
22839             return el;
22840         }
22841         var id = el.id;
22842         if(!id && autogen !== false){
22843             id = "roodd-" + (++autoIdSeed);
22844             el.id = id;
22845         }
22846         return id;
22847     };
22848     
22849     return {
22850     /**
22851      * Register a drag drop element
22852      * @param {String|HTMLElement} element The id or DOM node to register
22853      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22854      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22855      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22856      * populated in the data object (if applicable):
22857      * <pre>
22858 Value      Description<br />
22859 ---------  ------------------------------------------<br />
22860 handles    Array of DOM nodes that trigger dragging<br />
22861            for the element being registered<br />
22862 isHandle   True if the element passed in triggers<br />
22863            dragging itself, else false
22864 </pre>
22865      */
22866         register : function(el, data){
22867             data = data || {};
22868             if(typeof el == "string"){
22869                 el = document.getElementById(el);
22870             }
22871             data.ddel = el;
22872             elements[getId(el)] = data;
22873             if(data.isHandle !== false){
22874                 handles[data.ddel.id] = data;
22875             }
22876             if(data.handles){
22877                 var hs = data.handles;
22878                 for(var i = 0, len = hs.length; i < len; i++){
22879                         handles[getId(hs[i])] = data;
22880                 }
22881             }
22882         },
22883
22884     /**
22885      * Unregister a drag drop element
22886      * @param {String|HTMLElement}  element The id or DOM node to unregister
22887      */
22888         unregister : function(el){
22889             var id = getId(el, false);
22890             var data = elements[id];
22891             if(data){
22892                 delete elements[id];
22893                 if(data.handles){
22894                     var hs = data.handles;
22895                     for(var i = 0, len = hs.length; i < len; i++){
22896                         delete handles[getId(hs[i], false)];
22897                     }
22898                 }
22899             }
22900         },
22901
22902     /**
22903      * Returns the handle registered for a DOM Node by id
22904      * @param {String|HTMLElement} id The DOM node or id to look up
22905      * @return {Object} handle The custom handle data
22906      */
22907         getHandle : function(id){
22908             if(typeof id != "string"){ // must be element?
22909                 id = id.id;
22910             }
22911             return handles[id];
22912         },
22913
22914     /**
22915      * Returns the handle that is registered for the DOM node that is the target of the event
22916      * @param {Event} e The event
22917      * @return {Object} handle The custom handle data
22918      */
22919         getHandleFromEvent : function(e){
22920             var t = Roo.lib.Event.getTarget(e);
22921             return t ? handles[t.id] : null;
22922         },
22923
22924     /**
22925      * Returns a custom data object that is registered for a DOM node by id
22926      * @param {String|HTMLElement} id The DOM node or id to look up
22927      * @return {Object} data The custom data
22928      */
22929         getTarget : function(id){
22930             if(typeof id != "string"){ // must be element?
22931                 id = id.id;
22932             }
22933             return elements[id];
22934         },
22935
22936     /**
22937      * Returns a custom data object that is registered for the DOM node that is the target of the event
22938      * @param {Event} e The event
22939      * @return {Object} data The custom data
22940      */
22941         getTargetFromEvent : function(e){
22942             var t = Roo.lib.Event.getTarget(e);
22943             return t ? elements[t.id] || handles[t.id] : null;
22944         }
22945     };
22946 }();/*
22947  * Based on:
22948  * Ext JS Library 1.1.1
22949  * Copyright(c) 2006-2007, Ext JS, LLC.
22950  *
22951  * Originally Released Under LGPL - original licence link has changed is not relivant.
22952  *
22953  * Fork - LGPL
22954  * <script type="text/javascript">
22955  */
22956  
22957
22958 /**
22959  * @class Roo.dd.StatusProxy
22960  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22961  * default drag proxy used by all Roo.dd components.
22962  * @constructor
22963  * @param {Object} config
22964  */
22965 Roo.dd.StatusProxy = function(config){
22966     Roo.apply(this, config);
22967     this.id = this.id || Roo.id();
22968     this.el = new Roo.Layer({
22969         dh: {
22970             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22971                 {tag: "div", cls: "x-dd-drop-icon"},
22972                 {tag: "div", cls: "x-dd-drag-ghost"}
22973             ]
22974         }, 
22975         shadow: !config || config.shadow !== false
22976     });
22977     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22978     this.dropStatus = this.dropNotAllowed;
22979 };
22980
22981 Roo.dd.StatusProxy.prototype = {
22982     /**
22983      * @cfg {String} dropAllowed
22984      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22985      */
22986     dropAllowed : "x-dd-drop-ok",
22987     /**
22988      * @cfg {String} dropNotAllowed
22989      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22990      */
22991     dropNotAllowed : "x-dd-drop-nodrop",
22992
22993     /**
22994      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22995      * over the current target element.
22996      * @param {String} cssClass The css class for the new drop status indicator image
22997      */
22998     setStatus : function(cssClass){
22999         cssClass = cssClass || this.dropNotAllowed;
23000         if(this.dropStatus != cssClass){
23001             this.el.replaceClass(this.dropStatus, cssClass);
23002             this.dropStatus = cssClass;
23003         }
23004     },
23005
23006     /**
23007      * Resets the status indicator to the default dropNotAllowed value
23008      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23009      */
23010     reset : function(clearGhost){
23011         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23012         this.dropStatus = this.dropNotAllowed;
23013         if(clearGhost){
23014             this.ghost.update("");
23015         }
23016     },
23017
23018     /**
23019      * Updates the contents of the ghost element
23020      * @param {String} html The html that will replace the current innerHTML of the ghost element
23021      */
23022     update : function(html){
23023         if(typeof html == "string"){
23024             this.ghost.update(html);
23025         }else{
23026             this.ghost.update("");
23027             html.style.margin = "0";
23028             this.ghost.dom.appendChild(html);
23029         }
23030         // ensure float = none set?? cant remember why though.
23031         var el = this.ghost.dom.firstChild;
23032                 if(el){
23033                         Roo.fly(el).setStyle('float', 'none');
23034                 }
23035     },
23036     
23037     /**
23038      * Returns the underlying proxy {@link Roo.Layer}
23039      * @return {Roo.Layer} el
23040     */
23041     getEl : function(){
23042         return this.el;
23043     },
23044
23045     /**
23046      * Returns the ghost element
23047      * @return {Roo.Element} el
23048      */
23049     getGhost : function(){
23050         return this.ghost;
23051     },
23052
23053     /**
23054      * Hides the proxy
23055      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23056      */
23057     hide : function(clear){
23058         this.el.hide();
23059         if(clear){
23060             this.reset(true);
23061         }
23062     },
23063
23064     /**
23065      * Stops the repair animation if it's currently running
23066      */
23067     stop : function(){
23068         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23069             this.anim.stop();
23070         }
23071     },
23072
23073     /**
23074      * Displays this proxy
23075      */
23076     show : function(){
23077         this.el.show();
23078     },
23079
23080     /**
23081      * Force the Layer to sync its shadow and shim positions to the element
23082      */
23083     sync : function(){
23084         this.el.sync();
23085     },
23086
23087     /**
23088      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23089      * invalid drop operation by the item being dragged.
23090      * @param {Array} xy The XY position of the element ([x, y])
23091      * @param {Function} callback The function to call after the repair is complete
23092      * @param {Object} scope The scope in which to execute the callback
23093      */
23094     repair : function(xy, callback, scope){
23095         this.callback = callback;
23096         this.scope = scope;
23097         if(xy && this.animRepair !== false){
23098             this.el.addClass("x-dd-drag-repair");
23099             this.el.hideUnders(true);
23100             this.anim = this.el.shift({
23101                 duration: this.repairDuration || .5,
23102                 easing: 'easeOut',
23103                 xy: xy,
23104                 stopFx: true,
23105                 callback: this.afterRepair,
23106                 scope: this
23107             });
23108         }else{
23109             this.afterRepair();
23110         }
23111     },
23112
23113     // private
23114     afterRepair : function(){
23115         this.hide(true);
23116         if(typeof this.callback == "function"){
23117             this.callback.call(this.scope || this);
23118         }
23119         this.callback = null;
23120         this.scope = null;
23121     }
23122 };/*
23123  * Based on:
23124  * Ext JS Library 1.1.1
23125  * Copyright(c) 2006-2007, Ext JS, LLC.
23126  *
23127  * Originally Released Under LGPL - original licence link has changed is not relivant.
23128  *
23129  * Fork - LGPL
23130  * <script type="text/javascript">
23131  */
23132
23133 /**
23134  * @class Roo.dd.DragSource
23135  * @extends Roo.dd.DDProxy
23136  * A simple class that provides the basic implementation needed to make any element draggable.
23137  * @constructor
23138  * @param {String/HTMLElement/Element} el The container element
23139  * @param {Object} config
23140  */
23141 Roo.dd.DragSource = function(el, config){
23142     this.el = Roo.get(el);
23143     this.dragData = {};
23144     
23145     Roo.apply(this, config);
23146     
23147     if(!this.proxy){
23148         this.proxy = new Roo.dd.StatusProxy();
23149     }
23150
23151     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23152           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23153     
23154     this.dragging = false;
23155 };
23156
23157 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23158     /**
23159      * @cfg {String} dropAllowed
23160      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23161      */
23162     dropAllowed : "x-dd-drop-ok",
23163     /**
23164      * @cfg {String} dropNotAllowed
23165      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23166      */
23167     dropNotAllowed : "x-dd-drop-nodrop",
23168
23169     /**
23170      * Returns the data object associated with this drag source
23171      * @return {Object} data An object containing arbitrary data
23172      */
23173     getDragData : function(e){
23174         return this.dragData;
23175     },
23176
23177     // private
23178     onDragEnter : function(e, id){
23179         var target = Roo.dd.DragDropMgr.getDDById(id);
23180         this.cachedTarget = target;
23181         if(this.beforeDragEnter(target, e, id) !== false){
23182             if(target.isNotifyTarget){
23183                 var status = target.notifyEnter(this, e, this.dragData);
23184                 this.proxy.setStatus(status);
23185             }else{
23186                 this.proxy.setStatus(this.dropAllowed);
23187             }
23188             
23189             if(this.afterDragEnter){
23190                 /**
23191                  * An empty function by default, but provided so that you can perform a custom action
23192                  * when the dragged item enters the drop target by providing an implementation.
23193                  * @param {Roo.dd.DragDrop} target The drop target
23194                  * @param {Event} e The event object
23195                  * @param {String} id The id of the dragged element
23196                  * @method afterDragEnter
23197                  */
23198                 this.afterDragEnter(target, e, id);
23199             }
23200         }
23201     },
23202
23203     /**
23204      * An empty function by default, but provided so that you can perform a custom action
23205      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23206      * @param {Roo.dd.DragDrop} target The drop target
23207      * @param {Event} e The event object
23208      * @param {String} id The id of the dragged element
23209      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23210      */
23211     beforeDragEnter : function(target, e, id){
23212         return true;
23213     },
23214
23215     // private
23216     alignElWithMouse: function() {
23217         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23218         this.proxy.sync();
23219     },
23220
23221     // private
23222     onDragOver : function(e, id){
23223         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23224         if(this.beforeDragOver(target, e, id) !== false){
23225             if(target.isNotifyTarget){
23226                 var status = target.notifyOver(this, e, this.dragData);
23227                 this.proxy.setStatus(status);
23228             }
23229
23230             if(this.afterDragOver){
23231                 /**
23232                  * An empty function by default, but provided so that you can perform a custom action
23233                  * while the dragged item is over the drop target by providing an implementation.
23234                  * @param {Roo.dd.DragDrop} target The drop target
23235                  * @param {Event} e The event object
23236                  * @param {String} id The id of the dragged element
23237                  * @method afterDragOver
23238                  */
23239                 this.afterDragOver(target, e, id);
23240             }
23241         }
23242     },
23243
23244     /**
23245      * An empty function by default, but provided so that you can perform a custom action
23246      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23247      * @param {Roo.dd.DragDrop} target The drop target
23248      * @param {Event} e The event object
23249      * @param {String} id The id of the dragged element
23250      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23251      */
23252     beforeDragOver : function(target, e, id){
23253         return true;
23254     },
23255
23256     // private
23257     onDragOut : function(e, id){
23258         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23259         if(this.beforeDragOut(target, e, id) !== false){
23260             if(target.isNotifyTarget){
23261                 target.notifyOut(this, e, this.dragData);
23262             }
23263             this.proxy.reset();
23264             if(this.afterDragOut){
23265                 /**
23266                  * An empty function by default, but provided so that you can perform a custom action
23267                  * after the dragged item is dragged out of the target without dropping.
23268                  * @param {Roo.dd.DragDrop} target The drop target
23269                  * @param {Event} e The event object
23270                  * @param {String} id The id of the dragged element
23271                  * @method afterDragOut
23272                  */
23273                 this.afterDragOut(target, e, id);
23274             }
23275         }
23276         this.cachedTarget = null;
23277     },
23278
23279     /**
23280      * An empty function by default, but provided so that you can perform a custom action before the dragged
23281      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23282      * @param {Roo.dd.DragDrop} target The drop target
23283      * @param {Event} e The event object
23284      * @param {String} id The id of the dragged element
23285      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23286      */
23287     beforeDragOut : function(target, e, id){
23288         return true;
23289     },
23290     
23291     // private
23292     onDragDrop : function(e, id){
23293         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23294         if(this.beforeDragDrop(target, e, id) !== false){
23295             if(target.isNotifyTarget){
23296                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23297                     this.onValidDrop(target, e, id);
23298                 }else{
23299                     this.onInvalidDrop(target, e, id);
23300                 }
23301             }else{
23302                 this.onValidDrop(target, e, id);
23303             }
23304             
23305             if(this.afterDragDrop){
23306                 /**
23307                  * An empty function by default, but provided so that you can perform a custom action
23308                  * after a valid drag drop has occurred by providing an implementation.
23309                  * @param {Roo.dd.DragDrop} target The drop target
23310                  * @param {Event} e The event object
23311                  * @param {String} id The id of the dropped element
23312                  * @method afterDragDrop
23313                  */
23314                 this.afterDragDrop(target, e, id);
23315             }
23316         }
23317         delete this.cachedTarget;
23318     },
23319
23320     /**
23321      * An empty function by default, but provided so that you can perform a custom action before the dragged
23322      * item is dropped onto the target and optionally cancel the onDragDrop.
23323      * @param {Roo.dd.DragDrop} target The drop target
23324      * @param {Event} e The event object
23325      * @param {String} id The id of the dragged element
23326      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23327      */
23328     beforeDragDrop : function(target, e, id){
23329         return true;
23330     },
23331
23332     // private
23333     onValidDrop : function(target, e, id){
23334         this.hideProxy();
23335         if(this.afterValidDrop){
23336             /**
23337              * An empty function by default, but provided so that you can perform a custom action
23338              * after a valid drop has occurred by providing an implementation.
23339              * @param {Object} target The target DD 
23340              * @param {Event} e The event object
23341              * @param {String} id The id of the dropped element
23342              * @method afterInvalidDrop
23343              */
23344             this.afterValidDrop(target, e, id);
23345         }
23346     },
23347
23348     // private
23349     getRepairXY : function(e, data){
23350         return this.el.getXY();  
23351     },
23352
23353     // private
23354     onInvalidDrop : function(target, e, id){
23355         this.beforeInvalidDrop(target, e, id);
23356         if(this.cachedTarget){
23357             if(this.cachedTarget.isNotifyTarget){
23358                 this.cachedTarget.notifyOut(this, e, this.dragData);
23359             }
23360             this.cacheTarget = null;
23361         }
23362         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23363
23364         if(this.afterInvalidDrop){
23365             /**
23366              * An empty function by default, but provided so that you can perform a custom action
23367              * after an invalid drop has occurred by providing an implementation.
23368              * @param {Event} e The event object
23369              * @param {String} id The id of the dropped element
23370              * @method afterInvalidDrop
23371              */
23372             this.afterInvalidDrop(e, id);
23373         }
23374     },
23375
23376     // private
23377     afterRepair : function(){
23378         if(Roo.enableFx){
23379             this.el.highlight(this.hlColor || "c3daf9");
23380         }
23381         this.dragging = false;
23382     },
23383
23384     /**
23385      * An empty function by default, but provided so that you can perform a custom action after an invalid
23386      * drop has occurred.
23387      * @param {Roo.dd.DragDrop} target The drop target
23388      * @param {Event} e The event object
23389      * @param {String} id The id of the dragged element
23390      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23391      */
23392     beforeInvalidDrop : function(target, e, id){
23393         return true;
23394     },
23395
23396     // private
23397     handleMouseDown : function(e){
23398         if(this.dragging) {
23399             return;
23400         }
23401         var data = this.getDragData(e);
23402         if(data && this.onBeforeDrag(data, e) !== false){
23403             this.dragData = data;
23404             this.proxy.stop();
23405             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23406         } 
23407     },
23408
23409     /**
23410      * An empty function by default, but provided so that you can perform a custom action before the initial
23411      * drag event begins and optionally cancel it.
23412      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23413      * @param {Event} e The event object
23414      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23415      */
23416     onBeforeDrag : function(data, e){
23417         return true;
23418     },
23419
23420     /**
23421      * An empty function by default, but provided so that you can perform a custom action once the initial
23422      * drag event has begun.  The drag cannot be canceled from this function.
23423      * @param {Number} x The x position of the click on the dragged object
23424      * @param {Number} y The y position of the click on the dragged object
23425      */
23426     onStartDrag : Roo.emptyFn,
23427
23428     // private - YUI override
23429     startDrag : function(x, y){
23430         this.proxy.reset();
23431         this.dragging = true;
23432         this.proxy.update("");
23433         this.onInitDrag(x, y);
23434         this.proxy.show();
23435     },
23436
23437     // private
23438     onInitDrag : function(x, y){
23439         var clone = this.el.dom.cloneNode(true);
23440         clone.id = Roo.id(); // prevent duplicate ids
23441         this.proxy.update(clone);
23442         this.onStartDrag(x, y);
23443         return true;
23444     },
23445
23446     /**
23447      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23448      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23449      */
23450     getProxy : function(){
23451         return this.proxy;  
23452     },
23453
23454     /**
23455      * Hides the drag source's {@link Roo.dd.StatusProxy}
23456      */
23457     hideProxy : function(){
23458         this.proxy.hide();  
23459         this.proxy.reset(true);
23460         this.dragging = false;
23461     },
23462
23463     // private
23464     triggerCacheRefresh : function(){
23465         Roo.dd.DDM.refreshCache(this.groups);
23466     },
23467
23468     // private - override to prevent hiding
23469     b4EndDrag: function(e) {
23470     },
23471
23472     // private - override to prevent moving
23473     endDrag : function(e){
23474         this.onEndDrag(this.dragData, e);
23475     },
23476
23477     // private
23478     onEndDrag : function(data, e){
23479     },
23480     
23481     // private - pin to cursor
23482     autoOffset : function(x, y) {
23483         this.setDelta(-12, -20);
23484     }    
23485 });/*
23486  * Based on:
23487  * Ext JS Library 1.1.1
23488  * Copyright(c) 2006-2007, Ext JS, LLC.
23489  *
23490  * Originally Released Under LGPL - original licence link has changed is not relivant.
23491  *
23492  * Fork - LGPL
23493  * <script type="text/javascript">
23494  */
23495
23496
23497 /**
23498  * @class Roo.dd.DropTarget
23499  * @extends Roo.dd.DDTarget
23500  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23501  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23502  * @constructor
23503  * @param {String/HTMLElement/Element} el The container element
23504  * @param {Object} config
23505  */
23506 Roo.dd.DropTarget = function(el, config){
23507     this.el = Roo.get(el);
23508     
23509     var listeners = false; ;
23510     if (config && config.listeners) {
23511         listeners= config.listeners;
23512         delete config.listeners;
23513     }
23514     Roo.apply(this, config);
23515     
23516     if(this.containerScroll){
23517         Roo.dd.ScrollManager.register(this.el);
23518     }
23519     this.addEvents( {
23520          /**
23521          * @scope Roo.dd.DropTarget
23522          */
23523          
23524          /**
23525          * @event enter
23526          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23527          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23528          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23529          * 
23530          * IMPORTANT : it should set  this.valid to true|false
23531          * 
23532          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23533          * @param {Event} e The event
23534          * @param {Object} data An object containing arbitrary data supplied by the drag source
23535          */
23536         "enter" : true,
23537         
23538          /**
23539          * @event over
23540          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23541          * This method will be called on every mouse movement while the drag source is over the drop target.
23542          * This default implementation simply returns the dropAllowed config value.
23543          * 
23544          * IMPORTANT : it should set  this.valid to true|false
23545          * 
23546          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23547          * @param {Event} e The event
23548          * @param {Object} data An object containing arbitrary data supplied by the drag source
23549          
23550          */
23551         "over" : true,
23552         /**
23553          * @event out
23554          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23555          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23556          * overClass (if any) from the drop element.
23557          * 
23558          * 
23559          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23560          * @param {Event} e The event
23561          * @param {Object} data An object containing arbitrary data supplied by the drag source
23562          */
23563          "out" : true,
23564          
23565         /**
23566          * @event drop
23567          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23568          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23569          * implementation that does something to process the drop event and returns true so that the drag source's
23570          * repair action does not run.
23571          * 
23572          * IMPORTANT : it should set this.success
23573          * 
23574          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23575          * @param {Event} e The event
23576          * @param {Object} data An object containing arbitrary data supplied by the drag source
23577         */
23578          "drop" : true
23579     });
23580             
23581      
23582     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23583         this.el.dom, 
23584         this.ddGroup || this.group,
23585         {
23586             isTarget: true,
23587             listeners : listeners || {} 
23588            
23589         
23590         }
23591     );
23592
23593 };
23594
23595 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23596     /**
23597      * @cfg {String} overClass
23598      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23599      */
23600      /**
23601      * @cfg {String} ddGroup
23602      * The drag drop group to handle drop events for
23603      */
23604      
23605     /**
23606      * @cfg {String} dropAllowed
23607      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23608      */
23609     dropAllowed : "x-dd-drop-ok",
23610     /**
23611      * @cfg {String} dropNotAllowed
23612      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23613      */
23614     dropNotAllowed : "x-dd-drop-nodrop",
23615     /**
23616      * @cfg {boolean} success
23617      * set this after drop listener.. 
23618      */
23619     success : false,
23620     /**
23621      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23622      * if the drop point is valid for over/enter..
23623      */
23624     valid : false,
23625     // private
23626     isTarget : true,
23627
23628     // private
23629     isNotifyTarget : true,
23630     
23631     /**
23632      * @hide
23633      */
23634     notifyEnter : function(dd, e, data)
23635     {
23636         this.valid = true;
23637         this.fireEvent('enter', dd, e, data);
23638         if(this.overClass){
23639             this.el.addClass(this.overClass);
23640         }
23641         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23642             this.valid ? this.dropAllowed : this.dropNotAllowed
23643         );
23644     },
23645
23646     /**
23647      * @hide
23648      */
23649     notifyOver : function(dd, e, data)
23650     {
23651         this.valid = true;
23652         this.fireEvent('over', dd, e, data);
23653         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23654             this.valid ? this.dropAllowed : this.dropNotAllowed
23655         );
23656     },
23657
23658     /**
23659      * @hide
23660      */
23661     notifyOut : function(dd, e, data)
23662     {
23663         this.fireEvent('out', dd, e, data);
23664         if(this.overClass){
23665             this.el.removeClass(this.overClass);
23666         }
23667     },
23668
23669     /**
23670      * @hide
23671      */
23672     notifyDrop : function(dd, e, data)
23673     {
23674         this.success = false;
23675         this.fireEvent('drop', dd, e, data);
23676         return this.success;
23677     }
23678 });/*
23679  * Based on:
23680  * Ext JS Library 1.1.1
23681  * Copyright(c) 2006-2007, Ext JS, LLC.
23682  *
23683  * Originally Released Under LGPL - original licence link has changed is not relivant.
23684  *
23685  * Fork - LGPL
23686  * <script type="text/javascript">
23687  */
23688
23689
23690 /**
23691  * @class Roo.dd.DragZone
23692  * @extends Roo.dd.DragSource
23693  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23694  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23695  * @constructor
23696  * @param {String/HTMLElement/Element} el The container element
23697  * @param {Object} config
23698  */
23699 Roo.dd.DragZone = function(el, config){
23700     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23701     if(this.containerScroll){
23702         Roo.dd.ScrollManager.register(this.el);
23703     }
23704 };
23705
23706 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23707     /**
23708      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23709      * for auto scrolling during drag operations.
23710      */
23711     /**
23712      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23713      * method after a failed drop (defaults to "c3daf9" - light blue)
23714      */
23715
23716     /**
23717      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23718      * for a valid target to drag based on the mouse down. Override this method
23719      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23720      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23721      * @param {EventObject} e The mouse down event
23722      * @return {Object} The dragData
23723      */
23724     getDragData : function(e){
23725         return Roo.dd.Registry.getHandleFromEvent(e);
23726     },
23727     
23728     /**
23729      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23730      * this.dragData.ddel
23731      * @param {Number} x The x position of the click on the dragged object
23732      * @param {Number} y The y position of the click on the dragged object
23733      * @return {Boolean} true to continue the drag, false to cancel
23734      */
23735     onInitDrag : function(x, y){
23736         this.proxy.update(this.dragData.ddel.cloneNode(true));
23737         this.onStartDrag(x, y);
23738         return true;
23739     },
23740     
23741     /**
23742      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23743      */
23744     afterRepair : function(){
23745         if(Roo.enableFx){
23746             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23747         }
23748         this.dragging = false;
23749     },
23750
23751     /**
23752      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23753      * the XY of this.dragData.ddel
23754      * @param {EventObject} e The mouse up event
23755      * @return {Array} The xy location (e.g. [100, 200])
23756      */
23757     getRepairXY : function(e){
23758         return Roo.Element.fly(this.dragData.ddel).getXY();  
23759     }
23760 });/*
23761  * Based on:
23762  * Ext JS Library 1.1.1
23763  * Copyright(c) 2006-2007, Ext JS, LLC.
23764  *
23765  * Originally Released Under LGPL - original licence link has changed is not relivant.
23766  *
23767  * Fork - LGPL
23768  * <script type="text/javascript">
23769  */
23770 /**
23771  * @class Roo.dd.DropZone
23772  * @extends Roo.dd.DropTarget
23773  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23774  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23775  * @constructor
23776  * @param {String/HTMLElement/Element} el The container element
23777  * @param {Object} config
23778  */
23779 Roo.dd.DropZone = function(el, config){
23780     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23781 };
23782
23783 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23784     /**
23785      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23786      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23787      * provide your own custom lookup.
23788      * @param {Event} e The event
23789      * @return {Object} data The custom data
23790      */
23791     getTargetFromEvent : function(e){
23792         return Roo.dd.Registry.getTargetFromEvent(e);
23793     },
23794
23795     /**
23796      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23797      * that it has registered.  This method has no default implementation and should be overridden to provide
23798      * node-specific processing if necessary.
23799      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23800      * {@link #getTargetFromEvent} for this node)
23801      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23802      * @param {Event} e The event
23803      * @param {Object} data An object containing arbitrary data supplied by the drag source
23804      */
23805     onNodeEnter : function(n, dd, e, data){
23806         
23807     },
23808
23809     /**
23810      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23811      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23812      * overridden to provide the proper feedback.
23813      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23814      * {@link #getTargetFromEvent} for this node)
23815      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23816      * @param {Event} e The event
23817      * @param {Object} data An object containing arbitrary data supplied by the drag source
23818      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23819      * underlying {@link Roo.dd.StatusProxy} can be updated
23820      */
23821     onNodeOver : function(n, dd, e, data){
23822         return this.dropAllowed;
23823     },
23824
23825     /**
23826      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23827      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23828      * node-specific processing if necessary.
23829      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23830      * {@link #getTargetFromEvent} for this node)
23831      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23832      * @param {Event} e The event
23833      * @param {Object} data An object containing arbitrary data supplied by the drag source
23834      */
23835     onNodeOut : function(n, dd, e, data){
23836         
23837     },
23838
23839     /**
23840      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23841      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23842      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23843      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23844      * {@link #getTargetFromEvent} for this node)
23845      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23846      * @param {Event} e The event
23847      * @param {Object} data An object containing arbitrary data supplied by the drag source
23848      * @return {Boolean} True if the drop was valid, else false
23849      */
23850     onNodeDrop : function(n, dd, e, data){
23851         return false;
23852     },
23853
23854     /**
23855      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23856      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23857      * it should be overridden to provide the proper feedback if necessary.
23858      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23859      * @param {Event} e The event
23860      * @param {Object} data An object containing arbitrary data supplied by the drag source
23861      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23862      * underlying {@link Roo.dd.StatusProxy} can be updated
23863      */
23864     onContainerOver : function(dd, e, data){
23865         return this.dropNotAllowed;
23866     },
23867
23868     /**
23869      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23870      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23871      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23872      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23873      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23874      * @param {Event} e The event
23875      * @param {Object} data An object containing arbitrary data supplied by the drag source
23876      * @return {Boolean} True if the drop was valid, else false
23877      */
23878     onContainerDrop : function(dd, e, data){
23879         return false;
23880     },
23881
23882     /**
23883      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23884      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23885      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23886      * you should override this method and provide a custom implementation.
23887      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23888      * @param {Event} e The event
23889      * @param {Object} data An object containing arbitrary data supplied by the drag source
23890      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23891      * underlying {@link Roo.dd.StatusProxy} can be updated
23892      */
23893     notifyEnter : function(dd, e, data){
23894         return this.dropNotAllowed;
23895     },
23896
23897     /**
23898      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23899      * This method will be called on every mouse movement while the drag source is over the drop zone.
23900      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23901      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23902      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23903      * registered node, it will call {@link #onContainerOver}.
23904      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23905      * @param {Event} e The event
23906      * @param {Object} data An object containing arbitrary data supplied by the drag source
23907      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23908      * underlying {@link Roo.dd.StatusProxy} can be updated
23909      */
23910     notifyOver : function(dd, e, data){
23911         var n = this.getTargetFromEvent(e);
23912         if(!n){ // not over valid drop target
23913             if(this.lastOverNode){
23914                 this.onNodeOut(this.lastOverNode, dd, e, data);
23915                 this.lastOverNode = null;
23916             }
23917             return this.onContainerOver(dd, e, data);
23918         }
23919         if(this.lastOverNode != n){
23920             if(this.lastOverNode){
23921                 this.onNodeOut(this.lastOverNode, dd, e, data);
23922             }
23923             this.onNodeEnter(n, dd, e, data);
23924             this.lastOverNode = n;
23925         }
23926         return this.onNodeOver(n, dd, e, data);
23927     },
23928
23929     /**
23930      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23931      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23932      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23933      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23934      * @param {Event} e The event
23935      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23936      */
23937     notifyOut : function(dd, e, data){
23938         if(this.lastOverNode){
23939             this.onNodeOut(this.lastOverNode, dd, e, data);
23940             this.lastOverNode = null;
23941         }
23942     },
23943
23944     /**
23945      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23946      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23947      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23948      * otherwise it will call {@link #onContainerDrop}.
23949      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23950      * @param {Event} e The event
23951      * @param {Object} data An object containing arbitrary data supplied by the drag source
23952      * @return {Boolean} True if the drop was valid, else false
23953      */
23954     notifyDrop : function(dd, e, data){
23955         if(this.lastOverNode){
23956             this.onNodeOut(this.lastOverNode, dd, e, data);
23957             this.lastOverNode = null;
23958         }
23959         var n = this.getTargetFromEvent(e);
23960         return n ?
23961             this.onNodeDrop(n, dd, e, data) :
23962             this.onContainerDrop(dd, e, data);
23963     },
23964
23965     // private
23966     triggerCacheRefresh : function(){
23967         Roo.dd.DDM.refreshCache(this.groups);
23968     }  
23969 });/*
23970  * Based on:
23971  * Ext JS Library 1.1.1
23972  * Copyright(c) 2006-2007, Ext JS, LLC.
23973  *
23974  * Originally Released Under LGPL - original licence link has changed is not relivant.
23975  *
23976  * Fork - LGPL
23977  * <script type="text/javascript">
23978  */
23979
23980
23981 /**
23982  * @class Roo.data.SortTypes
23983  * @static
23984  * Defines the default sorting (casting?) comparison functions used when sorting data.
23985  */
23986 Roo.data.SortTypes = {
23987     /**
23988      * Default sort that does nothing
23989      * @param {Mixed} s The value being converted
23990      * @return {Mixed} The comparison value
23991      */
23992     none : function(s){
23993         return s;
23994     },
23995     
23996     /**
23997      * The regular expression used to strip tags
23998      * @type {RegExp}
23999      * @property
24000      */
24001     stripTagsRE : /<\/?[^>]+>/gi,
24002     
24003     /**
24004      * Strips all HTML tags to sort on text only
24005      * @param {Mixed} s The value being converted
24006      * @return {String} The comparison value
24007      */
24008     asText : function(s){
24009         return String(s).replace(this.stripTagsRE, "");
24010     },
24011     
24012     /**
24013      * Strips all HTML tags to sort on text only - Case insensitive
24014      * @param {Mixed} s The value being converted
24015      * @return {String} The comparison value
24016      */
24017     asUCText : function(s){
24018         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24019     },
24020     
24021     /**
24022      * Case insensitive string
24023      * @param {Mixed} s The value being converted
24024      * @return {String} The comparison value
24025      */
24026     asUCString : function(s) {
24027         return String(s).toUpperCase();
24028     },
24029     
24030     /**
24031      * Date sorting
24032      * @param {Mixed} s The value being converted
24033      * @return {Number} The comparison value
24034      */
24035     asDate : function(s) {
24036         if(!s){
24037             return 0;
24038         }
24039         if(s instanceof Date){
24040             return s.getTime();
24041         }
24042         return Date.parse(String(s));
24043     },
24044     
24045     /**
24046      * Float sorting
24047      * @param {Mixed} s The value being converted
24048      * @return {Float} The comparison value
24049      */
24050     asFloat : function(s) {
24051         var val = parseFloat(String(s).replace(/,/g, ""));
24052         if(isNaN(val)) {
24053             val = 0;
24054         }
24055         return val;
24056     },
24057     
24058     /**
24059      * Integer sorting
24060      * @param {Mixed} s The value being converted
24061      * @return {Number} The comparison value
24062      */
24063     asInt : function(s) {
24064         var val = parseInt(String(s).replace(/,/g, ""));
24065         if(isNaN(val)) {
24066             val = 0;
24067         }
24068         return val;
24069     }
24070 };/*
24071  * Based on:
24072  * Ext JS Library 1.1.1
24073  * Copyright(c) 2006-2007, Ext JS, LLC.
24074  *
24075  * Originally Released Under LGPL - original licence link has changed is not relivant.
24076  *
24077  * Fork - LGPL
24078  * <script type="text/javascript">
24079  */
24080
24081 /**
24082 * @class Roo.data.Record
24083  * Instances of this class encapsulate both record <em>definition</em> information, and record
24084  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24085  * to access Records cached in an {@link Roo.data.Store} object.<br>
24086  * <p>
24087  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24088  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24089  * objects.<br>
24090  * <p>
24091  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24092  * @constructor
24093  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24094  * {@link #create}. The parameters are the same.
24095  * @param {Array} data An associative Array of data values keyed by the field name.
24096  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24097  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24098  * not specified an integer id is generated.
24099  */
24100 Roo.data.Record = function(data, id){
24101     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24102     this.data = data;
24103 };
24104
24105 /**
24106  * Generate a constructor for a specific record layout.
24107  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24108  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24109  * Each field definition object may contain the following properties: <ul>
24110  * <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,
24111  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24112  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24113  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24114  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24115  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24116  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24117  * this may be omitted.</p></li>
24118  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24119  * <ul><li>auto (Default, implies no conversion)</li>
24120  * <li>string</li>
24121  * <li>int</li>
24122  * <li>float</li>
24123  * <li>boolean</li>
24124  * <li>date</li></ul></p></li>
24125  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24126  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24127  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24128  * by the Reader into an object that will be stored in the Record. It is passed the
24129  * following parameters:<ul>
24130  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24131  * </ul></p></li>
24132  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24133  * </ul>
24134  * <br>usage:<br><pre><code>
24135 var TopicRecord = Roo.data.Record.create(
24136     {name: 'title', mapping: 'topic_title'},
24137     {name: 'author', mapping: 'username'},
24138     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24139     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24140     {name: 'lastPoster', mapping: 'user2'},
24141     {name: 'excerpt', mapping: 'post_text'}
24142 );
24143
24144 var myNewRecord = new TopicRecord({
24145     title: 'Do my job please',
24146     author: 'noobie',
24147     totalPosts: 1,
24148     lastPost: new Date(),
24149     lastPoster: 'Animal',
24150     excerpt: 'No way dude!'
24151 });
24152 myStore.add(myNewRecord);
24153 </code></pre>
24154  * @method create
24155  * @static
24156  */
24157 Roo.data.Record.create = function(o){
24158     var f = function(){
24159         f.superclass.constructor.apply(this, arguments);
24160     };
24161     Roo.extend(f, Roo.data.Record);
24162     var p = f.prototype;
24163     p.fields = new Roo.util.MixedCollection(false, function(field){
24164         return field.name;
24165     });
24166     for(var i = 0, len = o.length; i < len; i++){
24167         p.fields.add(new Roo.data.Field(o[i]));
24168     }
24169     f.getField = function(name){
24170         return p.fields.get(name);  
24171     };
24172     return f;
24173 };
24174
24175 Roo.data.Record.AUTO_ID = 1000;
24176 Roo.data.Record.EDIT = 'edit';
24177 Roo.data.Record.REJECT = 'reject';
24178 Roo.data.Record.COMMIT = 'commit';
24179
24180 Roo.data.Record.prototype = {
24181     /**
24182      * Readonly flag - true if this record has been modified.
24183      * @type Boolean
24184      */
24185     dirty : false,
24186     editing : false,
24187     error: null,
24188     modified: null,
24189
24190     // private
24191     join : function(store){
24192         this.store = store;
24193     },
24194
24195     /**
24196      * Set the named field to the specified value.
24197      * @param {String} name The name of the field to set.
24198      * @param {Object} value The value to set the field to.
24199      */
24200     set : function(name, value){
24201         if(this.data[name] == value){
24202             return;
24203         }
24204         this.dirty = true;
24205         if(!this.modified){
24206             this.modified = {};
24207         }
24208         if(typeof this.modified[name] == 'undefined'){
24209             this.modified[name] = this.data[name];
24210         }
24211         this.data[name] = value;
24212         if(!this.editing && this.store){
24213             this.store.afterEdit(this);
24214         }       
24215     },
24216
24217     /**
24218      * Get the value of the named field.
24219      * @param {String} name The name of the field to get the value of.
24220      * @return {Object} The value of the field.
24221      */
24222     get : function(name){
24223         return this.data[name]; 
24224     },
24225
24226     // private
24227     beginEdit : function(){
24228         this.editing = true;
24229         this.modified = {}; 
24230     },
24231
24232     // private
24233     cancelEdit : function(){
24234         this.editing = false;
24235         delete this.modified;
24236     },
24237
24238     // private
24239     endEdit : function(){
24240         this.editing = false;
24241         if(this.dirty && this.store){
24242             this.store.afterEdit(this);
24243         }
24244     },
24245
24246     /**
24247      * Usually called by the {@link Roo.data.Store} which owns the Record.
24248      * Rejects all changes made to the Record since either creation, or the last commit operation.
24249      * Modified fields are reverted to their original values.
24250      * <p>
24251      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24252      * of reject operations.
24253      */
24254     reject : function(){
24255         var m = this.modified;
24256         for(var n in m){
24257             if(typeof m[n] != "function"){
24258                 this.data[n] = m[n];
24259             }
24260         }
24261         this.dirty = false;
24262         delete this.modified;
24263         this.editing = false;
24264         if(this.store){
24265             this.store.afterReject(this);
24266         }
24267     },
24268
24269     /**
24270      * Usually called by the {@link Roo.data.Store} which owns the Record.
24271      * Commits all changes made to the Record since either creation, or the last commit operation.
24272      * <p>
24273      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24274      * of commit operations.
24275      */
24276     commit : function(){
24277         this.dirty = false;
24278         delete this.modified;
24279         this.editing = false;
24280         if(this.store){
24281             this.store.afterCommit(this);
24282         }
24283     },
24284
24285     // private
24286     hasError : function(){
24287         return this.error != null;
24288     },
24289
24290     // private
24291     clearError : function(){
24292         this.error = null;
24293     },
24294
24295     /**
24296      * Creates a copy of this record.
24297      * @param {String} id (optional) A new record id if you don't want to use this record's id
24298      * @return {Record}
24299      */
24300     copy : function(newId) {
24301         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24302     }
24303 };/*
24304  * Based on:
24305  * Ext JS Library 1.1.1
24306  * Copyright(c) 2006-2007, Ext JS, LLC.
24307  *
24308  * Originally Released Under LGPL - original licence link has changed is not relivant.
24309  *
24310  * Fork - LGPL
24311  * <script type="text/javascript">
24312  */
24313
24314
24315
24316 /**
24317  * @class Roo.data.Store
24318  * @extends Roo.util.Observable
24319  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24320  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24321  * <p>
24322  * 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
24323  * has no knowledge of the format of the data returned by the Proxy.<br>
24324  * <p>
24325  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24326  * instances from the data object. These records are cached and made available through accessor functions.
24327  * @constructor
24328  * Creates a new Store.
24329  * @param {Object} config A config object containing the objects needed for the Store to access data,
24330  * and read the data into Records.
24331  */
24332 Roo.data.Store = function(config){
24333     this.data = new Roo.util.MixedCollection(false);
24334     this.data.getKey = function(o){
24335         return o.id;
24336     };
24337     this.baseParams = {};
24338     // private
24339     this.paramNames = {
24340         "start" : "start",
24341         "limit" : "limit",
24342         "sort" : "sort",
24343         "dir" : "dir",
24344         "multisort" : "_multisort"
24345     };
24346
24347     if(config && config.data){
24348         this.inlineData = config.data;
24349         delete config.data;
24350     }
24351
24352     Roo.apply(this, config);
24353     
24354     if(this.reader){ // reader passed
24355         this.reader = Roo.factory(this.reader, Roo.data);
24356         this.reader.xmodule = this.xmodule || false;
24357         if(!this.recordType){
24358             this.recordType = this.reader.recordType;
24359         }
24360         if(this.reader.onMetaChange){
24361             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24362         }
24363     }
24364
24365     if(this.recordType){
24366         this.fields = this.recordType.prototype.fields;
24367     }
24368     this.modified = [];
24369
24370     this.addEvents({
24371         /**
24372          * @event datachanged
24373          * Fires when the data cache has changed, and a widget which is using this Store
24374          * as a Record cache should refresh its view.
24375          * @param {Store} this
24376          */
24377         datachanged : true,
24378         /**
24379          * @event metachange
24380          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24381          * @param {Store} this
24382          * @param {Object} meta The JSON metadata
24383          */
24384         metachange : true,
24385         /**
24386          * @event add
24387          * Fires when Records have been added to the Store
24388          * @param {Store} this
24389          * @param {Roo.data.Record[]} records The array of Records added
24390          * @param {Number} index The index at which the record(s) were added
24391          */
24392         add : true,
24393         /**
24394          * @event remove
24395          * Fires when a Record has been removed from the Store
24396          * @param {Store} this
24397          * @param {Roo.data.Record} record The Record that was removed
24398          * @param {Number} index The index at which the record was removed
24399          */
24400         remove : true,
24401         /**
24402          * @event update
24403          * Fires when a Record has been updated
24404          * @param {Store} this
24405          * @param {Roo.data.Record} record The Record that was updated
24406          * @param {String} operation The update operation being performed.  Value may be one of:
24407          * <pre><code>
24408  Roo.data.Record.EDIT
24409  Roo.data.Record.REJECT
24410  Roo.data.Record.COMMIT
24411          * </code></pre>
24412          */
24413         update : true,
24414         /**
24415          * @event clear
24416          * Fires when the data cache has been cleared.
24417          * @param {Store} this
24418          */
24419         clear : true,
24420         /**
24421          * @event beforeload
24422          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24423          * the load action will be canceled.
24424          * @param {Store} this
24425          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24426          */
24427         beforeload : true,
24428         /**
24429          * @event beforeloadadd
24430          * Fires after a new set of Records has been loaded.
24431          * @param {Store} this
24432          * @param {Roo.data.Record[]} records The Records that were loaded
24433          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24434          */
24435         beforeloadadd : true,
24436         /**
24437          * @event load
24438          * Fires after a new set of Records has been loaded, before they are added to the store.
24439          * @param {Store} this
24440          * @param {Roo.data.Record[]} records The Records that were loaded
24441          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24442          * @params {Object} return from reader
24443          */
24444         load : true,
24445         /**
24446          * @event loadexception
24447          * Fires if an exception occurs in the Proxy during loading.
24448          * Called with the signature of the Proxy's "loadexception" event.
24449          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24450          * 
24451          * @param {Proxy} 
24452          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24453          * @param {Object} load options 
24454          * @param {Object} jsonData from your request (normally this contains the Exception)
24455          */
24456         loadexception : true
24457     });
24458     
24459     if(this.proxy){
24460         this.proxy = Roo.factory(this.proxy, Roo.data);
24461         this.proxy.xmodule = this.xmodule || false;
24462         this.relayEvents(this.proxy,  ["loadexception"]);
24463     }
24464     this.sortToggle = {};
24465     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24466
24467     Roo.data.Store.superclass.constructor.call(this);
24468
24469     if(this.inlineData){
24470         this.loadData(this.inlineData);
24471         delete this.inlineData;
24472     }
24473 };
24474
24475 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24476      /**
24477     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24478     * without a remote query - used by combo/forms at present.
24479     */
24480     
24481     /**
24482     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24483     */
24484     /**
24485     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24486     */
24487     /**
24488     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24489     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24490     */
24491     /**
24492     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24493     * on any HTTP request
24494     */
24495     /**
24496     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24497     */
24498     /**
24499     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24500     */
24501     multiSort: false,
24502     /**
24503     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24504     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24505     */
24506     remoteSort : false,
24507
24508     /**
24509     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24510      * loaded or when a record is removed. (defaults to false).
24511     */
24512     pruneModifiedRecords : false,
24513
24514     // private
24515     lastOptions : null,
24516
24517     /**
24518      * Add Records to the Store and fires the add event.
24519      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24520      */
24521     add : function(records){
24522         records = [].concat(records);
24523         for(var i = 0, len = records.length; i < len; i++){
24524             records[i].join(this);
24525         }
24526         var index = this.data.length;
24527         this.data.addAll(records);
24528         this.fireEvent("add", this, records, index);
24529     },
24530
24531     /**
24532      * Remove a Record from the Store and fires the remove event.
24533      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24534      */
24535     remove : function(record){
24536         var index = this.data.indexOf(record);
24537         this.data.removeAt(index);
24538  
24539         if(this.pruneModifiedRecords){
24540             this.modified.remove(record);
24541         }
24542         this.fireEvent("remove", this, record, index);
24543     },
24544
24545     /**
24546      * Remove all Records from the Store and fires the clear event.
24547      */
24548     removeAll : function(){
24549         this.data.clear();
24550         if(this.pruneModifiedRecords){
24551             this.modified = [];
24552         }
24553         this.fireEvent("clear", this);
24554     },
24555
24556     /**
24557      * Inserts Records to the Store at the given index and fires the add event.
24558      * @param {Number} index The start index at which to insert the passed Records.
24559      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24560      */
24561     insert : function(index, records){
24562         records = [].concat(records);
24563         for(var i = 0, len = records.length; i < len; i++){
24564             this.data.insert(index, records[i]);
24565             records[i].join(this);
24566         }
24567         this.fireEvent("add", this, records, index);
24568     },
24569
24570     /**
24571      * Get the index within the cache of the passed Record.
24572      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24573      * @return {Number} The index of the passed Record. Returns -1 if not found.
24574      */
24575     indexOf : function(record){
24576         return this.data.indexOf(record);
24577     },
24578
24579     /**
24580      * Get the index within the cache of the Record with the passed id.
24581      * @param {String} id The id of the Record to find.
24582      * @return {Number} The index of the Record. Returns -1 if not found.
24583      */
24584     indexOfId : function(id){
24585         return this.data.indexOfKey(id);
24586     },
24587
24588     /**
24589      * Get the Record with the specified id.
24590      * @param {String} id The id of the Record to find.
24591      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24592      */
24593     getById : function(id){
24594         return this.data.key(id);
24595     },
24596
24597     /**
24598      * Get the Record at the specified index.
24599      * @param {Number} index The index of the Record to find.
24600      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24601      */
24602     getAt : function(index){
24603         return this.data.itemAt(index);
24604     },
24605
24606     /**
24607      * Returns a range of Records between specified indices.
24608      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24609      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24610      * @return {Roo.data.Record[]} An array of Records
24611      */
24612     getRange : function(start, end){
24613         return this.data.getRange(start, end);
24614     },
24615
24616     // private
24617     storeOptions : function(o){
24618         o = Roo.apply({}, o);
24619         delete o.callback;
24620         delete o.scope;
24621         this.lastOptions = o;
24622     },
24623
24624     /**
24625      * Loads the Record cache from the configured Proxy using the configured Reader.
24626      * <p>
24627      * If using remote paging, then the first load call must specify the <em>start</em>
24628      * and <em>limit</em> properties in the options.params property to establish the initial
24629      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24630      * <p>
24631      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24632      * and this call will return before the new data has been loaded. Perform any post-processing
24633      * in a callback function, or in a "load" event handler.</strong>
24634      * <p>
24635      * @param {Object} options An object containing properties which control loading options:<ul>
24636      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24637      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24638      * passed the following arguments:<ul>
24639      * <li>r : Roo.data.Record[]</li>
24640      * <li>options: Options object from the load call</li>
24641      * <li>success: Boolean success indicator</li></ul></li>
24642      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24643      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24644      * </ul>
24645      */
24646     load : function(options){
24647         options = options || {};
24648         if(this.fireEvent("beforeload", this, options) !== false){
24649             this.storeOptions(options);
24650             var p = Roo.apply(options.params || {}, this.baseParams);
24651             // if meta was not loaded from remote source.. try requesting it.
24652             if (!this.reader.metaFromRemote) {
24653                 p._requestMeta = 1;
24654             }
24655             if(this.sortInfo && this.remoteSort){
24656                 var pn = this.paramNames;
24657                 p[pn["sort"]] = this.sortInfo.field;
24658                 p[pn["dir"]] = this.sortInfo.direction;
24659             }
24660             if (this.multiSort) {
24661                 var pn = this.paramNames;
24662                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24663             }
24664             
24665             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24666         }
24667     },
24668
24669     /**
24670      * Reloads the Record cache from the configured Proxy using the configured Reader and
24671      * the options from the last load operation performed.
24672      * @param {Object} options (optional) An object containing properties which may override the options
24673      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24674      * the most recently used options are reused).
24675      */
24676     reload : function(options){
24677         this.load(Roo.applyIf(options||{}, this.lastOptions));
24678     },
24679
24680     // private
24681     // Called as a callback by the Reader during a load operation.
24682     loadRecords : function(o, options, success){
24683          
24684         if(!o){
24685             if(success !== false){
24686                 this.fireEvent("load", this, [], options, o);
24687             }
24688             if(options.callback){
24689                 options.callback.call(options.scope || this, [], options, false);
24690             }
24691             return;
24692         }
24693         // if data returned failure - throw an exception.
24694         if (o.success === false) {
24695             // show a message if no listener is registered.
24696             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24697                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24698             }
24699             // loadmask wil be hooked into this..
24700             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24701             return;
24702         }
24703         var r = o.records, t = o.totalRecords || r.length;
24704         
24705         this.fireEvent("beforeloadadd", this, r, options, o);
24706         
24707         if(!options || options.add !== true){
24708             if(this.pruneModifiedRecords){
24709                 this.modified = [];
24710             }
24711             for(var i = 0, len = r.length; i < len; i++){
24712                 r[i].join(this);
24713             }
24714             if(this.snapshot){
24715                 this.data = this.snapshot;
24716                 delete this.snapshot;
24717             }
24718             this.data.clear();
24719             this.data.addAll(r);
24720             this.totalLength = t;
24721             this.applySort();
24722             this.fireEvent("datachanged", this);
24723         }else{
24724             this.totalLength = Math.max(t, this.data.length+r.length);
24725             this.add(r);
24726         }
24727         
24728         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24729                 
24730             var e = new Roo.data.Record({});
24731
24732             e.set(this.parent.displayField, this.parent.emptyTitle);
24733             e.set(this.parent.valueField, '');
24734
24735             this.insert(0, e);
24736         }
24737             
24738         this.fireEvent("load", this, r, options, o);
24739         if(options.callback){
24740             options.callback.call(options.scope || this, r, options, true);
24741         }
24742     },
24743
24744
24745     /**
24746      * Loads data from a passed data block. A Reader which understands the format of the data
24747      * must have been configured in the constructor.
24748      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24749      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24750      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24751      */
24752     loadData : function(o, append){
24753         var r = this.reader.readRecords(o);
24754         this.loadRecords(r, {add: append}, true);
24755     },
24756     
24757      /**
24758      * using 'cn' the nested child reader read the child array into it's child stores.
24759      * @param {Object} rec The record with a 'children array
24760      */
24761     loadDataFromChildren : function(rec)
24762     {
24763         this.loadData(this.reader.toLoadData(rec));
24764     },
24765     
24766
24767     /**
24768      * Gets the number of cached records.
24769      * <p>
24770      * <em>If using paging, this may not be the total size of the dataset. If the data object
24771      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24772      * the data set size</em>
24773      */
24774     getCount : function(){
24775         return this.data.length || 0;
24776     },
24777
24778     /**
24779      * Gets the total number of records in the dataset as returned by the server.
24780      * <p>
24781      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24782      * the dataset size</em>
24783      */
24784     getTotalCount : function(){
24785         return this.totalLength || 0;
24786     },
24787
24788     /**
24789      * Returns the sort state of the Store as an object with two properties:
24790      * <pre><code>
24791  field {String} The name of the field by which the Records are sorted
24792  direction {String} The sort order, "ASC" or "DESC"
24793      * </code></pre>
24794      */
24795     getSortState : function(){
24796         return this.sortInfo;
24797     },
24798
24799     // private
24800     applySort : function(){
24801         if(this.sortInfo && !this.remoteSort){
24802             var s = this.sortInfo, f = s.field;
24803             var st = this.fields.get(f).sortType;
24804             var fn = function(r1, r2){
24805                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24806                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24807             };
24808             this.data.sort(s.direction, fn);
24809             if(this.snapshot && this.snapshot != this.data){
24810                 this.snapshot.sort(s.direction, fn);
24811             }
24812         }
24813     },
24814
24815     /**
24816      * Sets the default sort column and order to be used by the next load operation.
24817      * @param {String} fieldName The name of the field to sort by.
24818      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24819      */
24820     setDefaultSort : function(field, dir){
24821         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24822     },
24823
24824     /**
24825      * Sort the Records.
24826      * If remote sorting is used, the sort is performed on the server, and the cache is
24827      * reloaded. If local sorting is used, the cache is sorted internally.
24828      * @param {String} fieldName The name of the field to sort by.
24829      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24830      */
24831     sort : function(fieldName, dir){
24832         var f = this.fields.get(fieldName);
24833         if(!dir){
24834             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24835             
24836             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24837                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24838             }else{
24839                 dir = f.sortDir;
24840             }
24841         }
24842         this.sortToggle[f.name] = dir;
24843         this.sortInfo = {field: f.name, direction: dir};
24844         if(!this.remoteSort){
24845             this.applySort();
24846             this.fireEvent("datachanged", this);
24847         }else{
24848             this.load(this.lastOptions);
24849         }
24850     },
24851
24852     /**
24853      * Calls the specified function for each of the Records in the cache.
24854      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24855      * Returning <em>false</em> aborts and exits the iteration.
24856      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24857      */
24858     each : function(fn, scope){
24859         this.data.each(fn, scope);
24860     },
24861
24862     /**
24863      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24864      * (e.g., during paging).
24865      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24866      */
24867     getModifiedRecords : function(){
24868         return this.modified;
24869     },
24870
24871     // private
24872     createFilterFn : function(property, value, anyMatch){
24873         if(!value.exec){ // not a regex
24874             value = String(value);
24875             if(value.length == 0){
24876                 return false;
24877             }
24878             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24879         }
24880         return function(r){
24881             return value.test(r.data[property]);
24882         };
24883     },
24884
24885     /**
24886      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24887      * @param {String} property A field on your records
24888      * @param {Number} start The record index to start at (defaults to 0)
24889      * @param {Number} end The last record index to include (defaults to length - 1)
24890      * @return {Number} The sum
24891      */
24892     sum : function(property, start, end){
24893         var rs = this.data.items, v = 0;
24894         start = start || 0;
24895         end = (end || end === 0) ? end : rs.length-1;
24896
24897         for(var i = start; i <= end; i++){
24898             v += (rs[i].data[property] || 0);
24899         }
24900         return v;
24901     },
24902
24903     /**
24904      * Filter the records by a specified property.
24905      * @param {String} field A field on your records
24906      * @param {String/RegExp} value Either a string that the field
24907      * should start with or a RegExp to test against the field
24908      * @param {Boolean} anyMatch True to match any part not just the beginning
24909      */
24910     filter : function(property, value, anyMatch){
24911         var fn = this.createFilterFn(property, value, anyMatch);
24912         return fn ? this.filterBy(fn) : this.clearFilter();
24913     },
24914
24915     /**
24916      * Filter by a function. The specified function will be called with each
24917      * record in this data source. If the function returns true the record is included,
24918      * otherwise it is filtered.
24919      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24920      * @param {Object} scope (optional) The scope of the function (defaults to this)
24921      */
24922     filterBy : function(fn, scope){
24923         this.snapshot = this.snapshot || this.data;
24924         this.data = this.queryBy(fn, scope||this);
24925         this.fireEvent("datachanged", this);
24926     },
24927
24928     /**
24929      * Query the records by a specified property.
24930      * @param {String} field A field on your records
24931      * @param {String/RegExp} value Either a string that the field
24932      * should start with or a RegExp to test against the field
24933      * @param {Boolean} anyMatch True to match any part not just the beginning
24934      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24935      */
24936     query : function(property, value, anyMatch){
24937         var fn = this.createFilterFn(property, value, anyMatch);
24938         return fn ? this.queryBy(fn) : this.data.clone();
24939     },
24940
24941     /**
24942      * Query by a function. The specified function will be called with each
24943      * record in this data source. If the function returns true the record is included
24944      * in the results.
24945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24946      * @param {Object} scope (optional) The scope of the function (defaults to this)
24947       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24948      **/
24949     queryBy : function(fn, scope){
24950         var data = this.snapshot || this.data;
24951         return data.filterBy(fn, scope||this);
24952     },
24953
24954     /**
24955      * Collects unique values for a particular dataIndex from this store.
24956      * @param {String} dataIndex The property to collect
24957      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24958      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24959      * @return {Array} An array of the unique values
24960      **/
24961     collect : function(dataIndex, allowNull, bypassFilter){
24962         var d = (bypassFilter === true && this.snapshot) ?
24963                 this.snapshot.items : this.data.items;
24964         var v, sv, r = [], l = {};
24965         for(var i = 0, len = d.length; i < len; i++){
24966             v = d[i].data[dataIndex];
24967             sv = String(v);
24968             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24969                 l[sv] = true;
24970                 r[r.length] = v;
24971             }
24972         }
24973         return r;
24974     },
24975
24976     /**
24977      * Revert to a view of the Record cache with no filtering applied.
24978      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24979      */
24980     clearFilter : function(suppressEvent){
24981         if(this.snapshot && this.snapshot != this.data){
24982             this.data = this.snapshot;
24983             delete this.snapshot;
24984             if(suppressEvent !== true){
24985                 this.fireEvent("datachanged", this);
24986             }
24987         }
24988     },
24989
24990     // private
24991     afterEdit : function(record){
24992         if(this.modified.indexOf(record) == -1){
24993             this.modified.push(record);
24994         }
24995         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24996     },
24997     
24998     // private
24999     afterReject : function(record){
25000         this.modified.remove(record);
25001         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25002     },
25003
25004     // private
25005     afterCommit : function(record){
25006         this.modified.remove(record);
25007         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25008     },
25009
25010     /**
25011      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25012      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25013      */
25014     commitChanges : function(){
25015         var m = this.modified.slice(0);
25016         this.modified = [];
25017         for(var i = 0, len = m.length; i < len; i++){
25018             m[i].commit();
25019         }
25020     },
25021
25022     /**
25023      * Cancel outstanding changes on all changed records.
25024      */
25025     rejectChanges : function(){
25026         var m = this.modified.slice(0);
25027         this.modified = [];
25028         for(var i = 0, len = m.length; i < len; i++){
25029             m[i].reject();
25030         }
25031     },
25032
25033     onMetaChange : function(meta, rtype, o){
25034         this.recordType = rtype;
25035         this.fields = rtype.prototype.fields;
25036         delete this.snapshot;
25037         this.sortInfo = meta.sortInfo || this.sortInfo;
25038         this.modified = [];
25039         this.fireEvent('metachange', this, this.reader.meta);
25040     },
25041     
25042     moveIndex : function(data, type)
25043     {
25044         var index = this.indexOf(data);
25045         
25046         var newIndex = index + type;
25047         
25048         this.remove(data);
25049         
25050         this.insert(newIndex, data);
25051         
25052     }
25053 });/*
25054  * Based on:
25055  * Ext JS Library 1.1.1
25056  * Copyright(c) 2006-2007, Ext JS, LLC.
25057  *
25058  * Originally Released Under LGPL - original licence link has changed is not relivant.
25059  *
25060  * Fork - LGPL
25061  * <script type="text/javascript">
25062  */
25063
25064 /**
25065  * @class Roo.data.SimpleStore
25066  * @extends Roo.data.Store
25067  * Small helper class to make creating Stores from Array data easier.
25068  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25069  * @cfg {Array} fields An array of field definition objects, or field name strings.
25070  * @cfg {Object} an existing reader (eg. copied from another store)
25071  * @cfg {Array} data The multi-dimensional array of data
25072  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25073  * @cfg {Roo.data.Reader} reader  [not-required] 
25074  * @constructor
25075  * @param {Object} config
25076  */
25077 Roo.data.SimpleStore = function(config)
25078 {
25079     Roo.data.SimpleStore.superclass.constructor.call(this, {
25080         isLocal : true,
25081         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25082                 id: config.id
25083             },
25084             Roo.data.Record.create(config.fields)
25085         ),
25086         proxy : new Roo.data.MemoryProxy(config.data)
25087     });
25088     this.load();
25089 };
25090 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25091  * Based on:
25092  * Ext JS Library 1.1.1
25093  * Copyright(c) 2006-2007, Ext JS, LLC.
25094  *
25095  * Originally Released Under LGPL - original licence link has changed is not relivant.
25096  *
25097  * Fork - LGPL
25098  * <script type="text/javascript">
25099  */
25100
25101 /**
25102 /**
25103  * @extends Roo.data.Store
25104  * @class Roo.data.JsonStore
25105  * Small helper class to make creating Stores for JSON data easier. <br/>
25106 <pre><code>
25107 var store = new Roo.data.JsonStore({
25108     url: 'get-images.php',
25109     root: 'images',
25110     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25111 });
25112 </code></pre>
25113  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25114  * JsonReader and HttpProxy (unless inline data is provided).</b>
25115  * @cfg {Array} fields An array of field definition objects, or field name strings.
25116  * @constructor
25117  * @param {Object} config
25118  */
25119 Roo.data.JsonStore = function(c){
25120     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25121         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25122         reader: new Roo.data.JsonReader(c, c.fields)
25123     }));
25124 };
25125 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25126  * Based on:
25127  * Ext JS Library 1.1.1
25128  * Copyright(c) 2006-2007, Ext JS, LLC.
25129  *
25130  * Originally Released Under LGPL - original licence link has changed is not relivant.
25131  *
25132  * Fork - LGPL
25133  * <script type="text/javascript">
25134  */
25135
25136  
25137 Roo.data.Field = function(config){
25138     if(typeof config == "string"){
25139         config = {name: config};
25140     }
25141     Roo.apply(this, config);
25142     
25143     if(!this.type){
25144         this.type = "auto";
25145     }
25146     
25147     var st = Roo.data.SortTypes;
25148     // named sortTypes are supported, here we look them up
25149     if(typeof this.sortType == "string"){
25150         this.sortType = st[this.sortType];
25151     }
25152     
25153     // set default sortType for strings and dates
25154     if(!this.sortType){
25155         switch(this.type){
25156             case "string":
25157                 this.sortType = st.asUCString;
25158                 break;
25159             case "date":
25160                 this.sortType = st.asDate;
25161                 break;
25162             default:
25163                 this.sortType = st.none;
25164         }
25165     }
25166
25167     // define once
25168     var stripRe = /[\$,%]/g;
25169
25170     // prebuilt conversion function for this field, instead of
25171     // switching every time we're reading a value
25172     if(!this.convert){
25173         var cv, dateFormat = this.dateFormat;
25174         switch(this.type){
25175             case "":
25176             case "auto":
25177             case undefined:
25178                 cv = function(v){ return v; };
25179                 break;
25180             case "string":
25181                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25182                 break;
25183             case "int":
25184                 cv = function(v){
25185                     return v !== undefined && v !== null && v !== '' ?
25186                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25187                     };
25188                 break;
25189             case "float":
25190                 cv = function(v){
25191                     return v !== undefined && v !== null && v !== '' ?
25192                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25193                     };
25194                 break;
25195             case "bool":
25196             case "boolean":
25197                 cv = function(v){ return v === true || v === "true" || v == 1; };
25198                 break;
25199             case "date":
25200                 cv = function(v){
25201                     if(!v){
25202                         return '';
25203                     }
25204                     if(v instanceof Date){
25205                         return v;
25206                     }
25207                     if(dateFormat){
25208                         if(dateFormat == "timestamp"){
25209                             return new Date(v*1000);
25210                         }
25211                         return Date.parseDate(v, dateFormat);
25212                     }
25213                     var parsed = Date.parse(v);
25214                     return parsed ? new Date(parsed) : null;
25215                 };
25216              break;
25217             
25218         }
25219         this.convert = cv;
25220     }
25221 };
25222
25223 Roo.data.Field.prototype = {
25224     dateFormat: null,
25225     defaultValue: "",
25226     mapping: null,
25227     sortType : null,
25228     sortDir : "ASC"
25229 };/*
25230  * Based on:
25231  * Ext JS Library 1.1.1
25232  * Copyright(c) 2006-2007, Ext JS, LLC.
25233  *
25234  * Originally Released Under LGPL - original licence link has changed is not relivant.
25235  *
25236  * Fork - LGPL
25237  * <script type="text/javascript">
25238  */
25239  
25240 // Base class for reading structured data from a data source.  This class is intended to be
25241 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25242
25243 /**
25244  * @class Roo.data.DataReader
25245  * @abstract
25246  * Base class for reading structured data from a data source.  This class is intended to be
25247  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25248  */
25249
25250 Roo.data.DataReader = function(meta, recordType){
25251     
25252     this.meta = meta;
25253     
25254     this.recordType = recordType instanceof Array ? 
25255         Roo.data.Record.create(recordType) : recordType;
25256 };
25257
25258 Roo.data.DataReader.prototype = {
25259     
25260     
25261     readerType : 'Data',
25262      /**
25263      * Create an empty record
25264      * @param {Object} data (optional) - overlay some values
25265      * @return {Roo.data.Record} record created.
25266      */
25267     newRow :  function(d) {
25268         var da =  {};
25269         this.recordType.prototype.fields.each(function(c) {
25270             switch( c.type) {
25271                 case 'int' : da[c.name] = 0; break;
25272                 case 'date' : da[c.name] = new Date(); break;
25273                 case 'float' : da[c.name] = 0.0; break;
25274                 case 'boolean' : da[c.name] = false; break;
25275                 default : da[c.name] = ""; break;
25276             }
25277             
25278         });
25279         return new this.recordType(Roo.apply(da, d));
25280     }
25281     
25282     
25283 };/*
25284  * Based on:
25285  * Ext JS Library 1.1.1
25286  * Copyright(c) 2006-2007, Ext JS, LLC.
25287  *
25288  * Originally Released Under LGPL - original licence link has changed is not relivant.
25289  *
25290  * Fork - LGPL
25291  * <script type="text/javascript">
25292  */
25293
25294 /**
25295  * @class Roo.data.DataProxy
25296  * @extends Roo.util.Observable
25297  * @abstract
25298  * This class is an abstract base class for implementations which provide retrieval of
25299  * unformatted data objects.<br>
25300  * <p>
25301  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25302  * (of the appropriate type which knows how to parse the data object) to provide a block of
25303  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25304  * <p>
25305  * Custom implementations must implement the load method as described in
25306  * {@link Roo.data.HttpProxy#load}.
25307  */
25308 Roo.data.DataProxy = function(){
25309     this.addEvents({
25310         /**
25311          * @event beforeload
25312          * Fires before a network request is made to retrieve a data object.
25313          * @param {Object} This DataProxy object.
25314          * @param {Object} params The params parameter to the load function.
25315          */
25316         beforeload : true,
25317         /**
25318          * @event load
25319          * Fires before the load method's callback is called.
25320          * @param {Object} This DataProxy object.
25321          * @param {Object} o The data object.
25322          * @param {Object} arg The callback argument object passed to the load function.
25323          */
25324         load : true,
25325         /**
25326          * @event loadexception
25327          * Fires if an Exception occurs during data retrieval.
25328          * @param {Object} This DataProxy object.
25329          * @param {Object} o The data object.
25330          * @param {Object} arg The callback argument object passed to the load function.
25331          * @param {Object} e The Exception.
25332          */
25333         loadexception : true
25334     });
25335     Roo.data.DataProxy.superclass.constructor.call(this);
25336 };
25337
25338 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25339
25340     /**
25341      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25342      */
25343 /*
25344  * Based on:
25345  * Ext JS Library 1.1.1
25346  * Copyright(c) 2006-2007, Ext JS, LLC.
25347  *
25348  * Originally Released Under LGPL - original licence link has changed is not relivant.
25349  *
25350  * Fork - LGPL
25351  * <script type="text/javascript">
25352  */
25353 /**
25354  * @class Roo.data.MemoryProxy
25355  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25356  * to the Reader when its load method is called.
25357  * @constructor
25358  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25359  */
25360 Roo.data.MemoryProxy = function(data){
25361     if (data.data) {
25362         data = data.data;
25363     }
25364     Roo.data.MemoryProxy.superclass.constructor.call(this);
25365     this.data = data;
25366 };
25367
25368 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25369     
25370     /**
25371      * Load data from the requested source (in this case an in-memory
25372      * data object passed to the constructor), read the data object into
25373      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25374      * process that block using the passed callback.
25375      * @param {Object} params This parameter is not used by the MemoryProxy class.
25376      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25377      * object into a block of Roo.data.Records.
25378      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25379      * The function must be passed <ul>
25380      * <li>The Record block object</li>
25381      * <li>The "arg" argument from the load function</li>
25382      * <li>A boolean success indicator</li>
25383      * </ul>
25384      * @param {Object} scope The scope in which to call the callback
25385      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25386      */
25387     load : function(params, reader, callback, scope, arg){
25388         params = params || {};
25389         var result;
25390         try {
25391             result = reader.readRecords(params.data ? params.data :this.data);
25392         }catch(e){
25393             this.fireEvent("loadexception", this, arg, null, e);
25394             callback.call(scope, null, arg, false);
25395             return;
25396         }
25397         callback.call(scope, result, arg, true);
25398     },
25399     
25400     // private
25401     update : function(params, records){
25402         
25403     }
25404 });/*
25405  * Based on:
25406  * Ext JS Library 1.1.1
25407  * Copyright(c) 2006-2007, Ext JS, LLC.
25408  *
25409  * Originally Released Under LGPL - original licence link has changed is not relivant.
25410  *
25411  * Fork - LGPL
25412  * <script type="text/javascript">
25413  */
25414 /**
25415  * @class Roo.data.HttpProxy
25416  * @extends Roo.data.DataProxy
25417  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25418  * configured to reference a certain URL.<br><br>
25419  * <p>
25420  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25421  * from which the running page was served.<br><br>
25422  * <p>
25423  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25424  * <p>
25425  * Be aware that to enable the browser to parse an XML document, the server must set
25426  * the Content-Type header in the HTTP response to "text/xml".
25427  * @constructor
25428  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25429  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25430  * will be used to make the request.
25431  */
25432 Roo.data.HttpProxy = function(conn){
25433     Roo.data.HttpProxy.superclass.constructor.call(this);
25434     // is conn a conn config or a real conn?
25435     this.conn = conn;
25436     this.useAjax = !conn || !conn.events;
25437   
25438 };
25439
25440 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25441     // thse are take from connection...
25442     
25443     /**
25444      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25445      */
25446     /**
25447      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25448      * extra parameters to each request made by this object. (defaults to undefined)
25449      */
25450     /**
25451      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25452      *  to each request made by this object. (defaults to undefined)
25453      */
25454     /**
25455      * @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)
25456      */
25457     /**
25458      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25459      */
25460      /**
25461      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25462      * @type Boolean
25463      */
25464   
25465
25466     /**
25467      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25468      * @type Boolean
25469      */
25470     /**
25471      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25472      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25473      * a finer-grained basis than the DataProxy events.
25474      */
25475     getConnection : function(){
25476         return this.useAjax ? Roo.Ajax : this.conn;
25477     },
25478
25479     /**
25480      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25481      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25482      * process that block using the passed callback.
25483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25484      * for the request to the remote server.
25485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25486      * object into a block of Roo.data.Records.
25487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25488      * The function must be passed <ul>
25489      * <li>The Record block object</li>
25490      * <li>The "arg" argument from the load function</li>
25491      * <li>A boolean success indicator</li>
25492      * </ul>
25493      * @param {Object} scope The scope in which to call the callback
25494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25495      */
25496     load : function(params, reader, callback, scope, arg){
25497         if(this.fireEvent("beforeload", this, params) !== false){
25498             var  o = {
25499                 params : params || {},
25500                 request: {
25501                     callback : callback,
25502                     scope : scope,
25503                     arg : arg
25504                 },
25505                 reader: reader,
25506                 callback : this.loadResponse,
25507                 scope: this
25508             };
25509             if(this.useAjax){
25510                 Roo.applyIf(o, this.conn);
25511                 if(this.activeRequest){
25512                     Roo.Ajax.abort(this.activeRequest);
25513                 }
25514                 this.activeRequest = Roo.Ajax.request(o);
25515             }else{
25516                 this.conn.request(o);
25517             }
25518         }else{
25519             callback.call(scope||this, null, arg, false);
25520         }
25521     },
25522
25523     // private
25524     loadResponse : function(o, success, response){
25525         delete this.activeRequest;
25526         if(!success){
25527             this.fireEvent("loadexception", this, o, response);
25528             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25529             return;
25530         }
25531         var result;
25532         try {
25533             result = o.reader.read(response);
25534         }catch(e){
25535             o.success = false;
25536             o.raw = { errorMsg : response.responseText };
25537             this.fireEvent("loadexception", this, o, response, e);
25538             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25539             return;
25540         }
25541         
25542         this.fireEvent("load", this, o, o.request.arg);
25543         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25544     },
25545
25546     // private
25547     update : function(dataSet){
25548
25549     },
25550
25551     // private
25552     updateResponse : function(dataSet){
25553
25554     }
25555 });/*
25556  * Based on:
25557  * Ext JS Library 1.1.1
25558  * Copyright(c) 2006-2007, Ext JS, LLC.
25559  *
25560  * Originally Released Under LGPL - original licence link has changed is not relivant.
25561  *
25562  * Fork - LGPL
25563  * <script type="text/javascript">
25564  */
25565
25566 /**
25567  * @class Roo.data.ScriptTagProxy
25568  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25569  * other than the originating domain of the running page.<br><br>
25570  * <p>
25571  * <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
25572  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25573  * <p>
25574  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25575  * source code that is used as the source inside a &lt;script> tag.<br><br>
25576  * <p>
25577  * In order for the browser to process the returned data, the server must wrap the data object
25578  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25579  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25580  * depending on whether the callback name was passed:
25581  * <p>
25582  * <pre><code>
25583 boolean scriptTag = false;
25584 String cb = request.getParameter("callback");
25585 if (cb != null) {
25586     scriptTag = true;
25587     response.setContentType("text/javascript");
25588 } else {
25589     response.setContentType("application/x-json");
25590 }
25591 Writer out = response.getWriter();
25592 if (scriptTag) {
25593     out.write(cb + "(");
25594 }
25595 out.print(dataBlock.toJsonString());
25596 if (scriptTag) {
25597     out.write(");");
25598 }
25599 </pre></code>
25600  *
25601  * @constructor
25602  * @param {Object} config A configuration object.
25603  */
25604 Roo.data.ScriptTagProxy = function(config){
25605     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25606     Roo.apply(this, config);
25607     this.head = document.getElementsByTagName("head")[0];
25608 };
25609
25610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25611
25612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25613     /**
25614      * @cfg {String} url The URL from which to request the data object.
25615      */
25616     /**
25617      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25618      */
25619     timeout : 30000,
25620     /**
25621      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25622      * the server the name of the callback function set up by the load call to process the returned data object.
25623      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25624      * javascript output which calls this named function passing the data object as its only parameter.
25625      */
25626     callbackParam : "callback",
25627     /**
25628      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25629      * name to the request.
25630      */
25631     nocache : true,
25632
25633     /**
25634      * Load data from the configured URL, read the data object into
25635      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25636      * process that block using the passed callback.
25637      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25638      * for the request to the remote server.
25639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25640      * object into a block of Roo.data.Records.
25641      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25642      * The function must be passed <ul>
25643      * <li>The Record block object</li>
25644      * <li>The "arg" argument from the load function</li>
25645      * <li>A boolean success indicator</li>
25646      * </ul>
25647      * @param {Object} scope The scope in which to call the callback
25648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25649      */
25650     load : function(params, reader, callback, scope, arg){
25651         if(this.fireEvent("beforeload", this, params) !== false){
25652
25653             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25654
25655             var url = this.url;
25656             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25657             if(this.nocache){
25658                 url += "&_dc=" + (new Date().getTime());
25659             }
25660             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25661             var trans = {
25662                 id : transId,
25663                 cb : "stcCallback"+transId,
25664                 scriptId : "stcScript"+transId,
25665                 params : params,
25666                 arg : arg,
25667                 url : url,
25668                 callback : callback,
25669                 scope : scope,
25670                 reader : reader
25671             };
25672             var conn = this;
25673
25674             window[trans.cb] = function(o){
25675                 conn.handleResponse(o, trans);
25676             };
25677
25678             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25679
25680             if(this.autoAbort !== false){
25681                 this.abort();
25682             }
25683
25684             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25685
25686             var script = document.createElement("script");
25687             script.setAttribute("src", url);
25688             script.setAttribute("type", "text/javascript");
25689             script.setAttribute("id", trans.scriptId);
25690             this.head.appendChild(script);
25691
25692             this.trans = trans;
25693         }else{
25694             callback.call(scope||this, null, arg, false);
25695         }
25696     },
25697
25698     // private
25699     isLoading : function(){
25700         return this.trans ? true : false;
25701     },
25702
25703     /**
25704      * Abort the current server request.
25705      */
25706     abort : function(){
25707         if(this.isLoading()){
25708             this.destroyTrans(this.trans);
25709         }
25710     },
25711
25712     // private
25713     destroyTrans : function(trans, isLoaded){
25714         this.head.removeChild(document.getElementById(trans.scriptId));
25715         clearTimeout(trans.timeoutId);
25716         if(isLoaded){
25717             window[trans.cb] = undefined;
25718             try{
25719                 delete window[trans.cb];
25720             }catch(e){}
25721         }else{
25722             // if hasn't been loaded, wait for load to remove it to prevent script error
25723             window[trans.cb] = function(){
25724                 window[trans.cb] = undefined;
25725                 try{
25726                     delete window[trans.cb];
25727                 }catch(e){}
25728             };
25729         }
25730     },
25731
25732     // private
25733     handleResponse : function(o, trans){
25734         this.trans = false;
25735         this.destroyTrans(trans, true);
25736         var result;
25737         try {
25738             result = trans.reader.readRecords(o);
25739         }catch(e){
25740             this.fireEvent("loadexception", this, o, trans.arg, e);
25741             trans.callback.call(trans.scope||window, null, trans.arg, false);
25742             return;
25743         }
25744         this.fireEvent("load", this, o, trans.arg);
25745         trans.callback.call(trans.scope||window, result, trans.arg, true);
25746     },
25747
25748     // private
25749     handleFailure : function(trans){
25750         this.trans = false;
25751         this.destroyTrans(trans, false);
25752         this.fireEvent("loadexception", this, null, trans.arg);
25753         trans.callback.call(trans.scope||window, null, trans.arg, false);
25754     }
25755 });/*
25756  * Based on:
25757  * Ext JS Library 1.1.1
25758  * Copyright(c) 2006-2007, Ext JS, LLC.
25759  *
25760  * Originally Released Under LGPL - original licence link has changed is not relivant.
25761  *
25762  * Fork - LGPL
25763  * <script type="text/javascript">
25764  */
25765
25766 /**
25767  * @class Roo.data.JsonReader
25768  * @extends Roo.data.DataReader
25769  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25770  * based on mappings in a provided Roo.data.Record constructor.
25771  * 
25772  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25773  * in the reply previously. 
25774  * 
25775  * <p>
25776  * Example code:
25777  * <pre><code>
25778 var RecordDef = Roo.data.Record.create([
25779     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25780     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25781 ]);
25782 var myReader = new Roo.data.JsonReader({
25783     totalProperty: "results",    // The property which contains the total dataset size (optional)
25784     root: "rows",                // The property which contains an Array of row objects
25785     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25786 }, RecordDef);
25787 </code></pre>
25788  * <p>
25789  * This would consume a JSON file like this:
25790  * <pre><code>
25791 { 'results': 2, 'rows': [
25792     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25793     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25794 }
25795 </code></pre>
25796  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25798  * paged from the remote server.
25799  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25800  * @cfg {String} root name of the property which contains the Array of row objects.
25801  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25802  * @cfg {Array} fields Array of field definition objects
25803  * @constructor
25804  * Create a new JsonReader
25805  * @param {Object} meta Metadata configuration options
25806  * @param {Object} recordType Either an Array of field definition objects,
25807  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25808  */
25809 Roo.data.JsonReader = function(meta, recordType){
25810     
25811     meta = meta || {};
25812     // set some defaults:
25813     Roo.applyIf(meta, {
25814         totalProperty: 'total',
25815         successProperty : 'success',
25816         root : 'data',
25817         id : 'id'
25818     });
25819     
25820     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25821 };
25822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25823     
25824     readerType : 'Json',
25825     
25826     /**
25827      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25828      * Used by Store query builder to append _requestMeta to params.
25829      * 
25830      */
25831     metaFromRemote : false,
25832     /**
25833      * This method is only used by a DataProxy which has retrieved data from a remote server.
25834      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25835      * @return {Object} data A data block which is used by an Roo.data.Store object as
25836      * a cache of Roo.data.Records.
25837      */
25838     read : function(response){
25839         var json = response.responseText;
25840        
25841         var o = /* eval:var:o */ eval("("+json+")");
25842         if(!o) {
25843             throw {message: "JsonReader.read: Json object not found"};
25844         }
25845         
25846         if(o.metaData){
25847             
25848             delete this.ef;
25849             this.metaFromRemote = true;
25850             this.meta = o.metaData;
25851             this.recordType = Roo.data.Record.create(o.metaData.fields);
25852             this.onMetaChange(this.meta, this.recordType, o);
25853         }
25854         return this.readRecords(o);
25855     },
25856
25857     // private function a store will implement
25858     onMetaChange : function(meta, recordType, o){
25859
25860     },
25861
25862     /**
25863          * @ignore
25864          */
25865     simpleAccess: function(obj, subsc) {
25866         return obj[subsc];
25867     },
25868
25869         /**
25870          * @ignore
25871          */
25872     getJsonAccessor: function(){
25873         var re = /[\[\.]/;
25874         return function(expr) {
25875             try {
25876                 return(re.test(expr))
25877                     ? new Function("obj", "return obj." + expr)
25878                     : function(obj){
25879                         return obj[expr];
25880                     };
25881             } catch(e){}
25882             return Roo.emptyFn;
25883         };
25884     }(),
25885
25886     /**
25887      * Create a data block containing Roo.data.Records from an XML document.
25888      * @param {Object} o An object which contains an Array of row objects in the property specified
25889      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25890      * which contains the total size of the dataset.
25891      * @return {Object} data A data block which is used by an Roo.data.Store object as
25892      * a cache of Roo.data.Records.
25893      */
25894     readRecords : function(o){
25895         /**
25896          * After any data loads, the raw JSON data is available for further custom processing.
25897          * @type Object
25898          */
25899         this.o = o;
25900         var s = this.meta, Record = this.recordType,
25901             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25902
25903 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25904         if (!this.ef) {
25905             if(s.totalProperty) {
25906                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25907                 }
25908                 if(s.successProperty) {
25909                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25910                 }
25911                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25912                 if (s.id) {
25913                         var g = this.getJsonAccessor(s.id);
25914                         this.getId = function(rec) {
25915                                 var r = g(rec);  
25916                                 return (r === undefined || r === "") ? null : r;
25917                         };
25918                 } else {
25919                         this.getId = function(){return null;};
25920                 }
25921             this.ef = [];
25922             for(var jj = 0; jj < fl; jj++){
25923                 f = fi[jj];
25924                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25925                 this.ef[jj] = this.getJsonAccessor(map);
25926             }
25927         }
25928
25929         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25930         if(s.totalProperty){
25931             var vt = parseInt(this.getTotal(o), 10);
25932             if(!isNaN(vt)){
25933                 totalRecords = vt;
25934             }
25935         }
25936         if(s.successProperty){
25937             var vs = this.getSuccess(o);
25938             if(vs === false || vs === 'false'){
25939                 success = false;
25940             }
25941         }
25942         var records = [];
25943         for(var i = 0; i < c; i++){
25944             var n = root[i];
25945             var values = {};
25946             var id = this.getId(n);
25947             for(var j = 0; j < fl; j++){
25948                 f = fi[j];
25949                                 var v = this.ef[j](n);
25950                                 if (!f.convert) {
25951                                         Roo.log('missing convert for ' + f.name);
25952                                         Roo.log(f);
25953                                         continue;
25954                                 }
25955                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25956             }
25957                         if (!Record) {
25958                                 return {
25959                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25960                                         success : false,
25961                                         records : [],
25962                                         totalRecords : 0
25963                                 };
25964                         }
25965             var record = new Record(values, id);
25966             record.json = n;
25967             records[i] = record;
25968         }
25969         return {
25970             raw : o,
25971             success : success,
25972             records : records,
25973             totalRecords : totalRecords
25974         };
25975     },
25976     // used when loading children.. @see loadDataFromChildren
25977     toLoadData: function(rec)
25978     {
25979         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25980         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25981         return { data : data, total : data.length };
25982         
25983     }
25984 });/*
25985  * Based on:
25986  * Ext JS Library 1.1.1
25987  * Copyright(c) 2006-2007, Ext JS, LLC.
25988  *
25989  * Originally Released Under LGPL - original licence link has changed is not relivant.
25990  *
25991  * Fork - LGPL
25992  * <script type="text/javascript">
25993  */
25994
25995 /**
25996  * @class Roo.data.XmlReader
25997  * @extends Roo.data.DataReader
25998  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25999  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26000  * <p>
26001  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26002  * header in the HTTP response must be set to "text/xml".</em>
26003  * <p>
26004  * Example code:
26005  * <pre><code>
26006 var RecordDef = Roo.data.Record.create([
26007    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26008    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26009 ]);
26010 var myReader = new Roo.data.XmlReader({
26011    totalRecords: "results", // The element which contains the total dataset size (optional)
26012    record: "row",           // The repeated element which contains row information
26013    id: "id"                 // The element within the row that provides an ID for the record (optional)
26014 }, RecordDef);
26015 </code></pre>
26016  * <p>
26017  * This would consume an XML file like this:
26018  * <pre><code>
26019 &lt;?xml?>
26020 &lt;dataset>
26021  &lt;results>2&lt;/results>
26022  &lt;row>
26023    &lt;id>1&lt;/id>
26024    &lt;name>Bill&lt;/name>
26025    &lt;occupation>Gardener&lt;/occupation>
26026  &lt;/row>
26027  &lt;row>
26028    &lt;id>2&lt;/id>
26029    &lt;name>Ben&lt;/name>
26030    &lt;occupation>Horticulturalist&lt;/occupation>
26031  &lt;/row>
26032 &lt;/dataset>
26033 </code></pre>
26034  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26035  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26036  * paged from the remote server.
26037  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26038  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26039  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26040  * a record identifier value.
26041  * @constructor
26042  * Create a new XmlReader
26043  * @param {Object} meta Metadata configuration options
26044  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26045  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26046  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26047  */
26048 Roo.data.XmlReader = function(meta, recordType){
26049     meta = meta || {};
26050     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26051 };
26052 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26053     
26054     readerType : 'Xml',
26055     
26056     /**
26057      * This method is only used by a DataProxy which has retrieved data from a remote server.
26058          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26059          * to contain a method called 'responseXML' that returns an XML document object.
26060      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26061      * a cache of Roo.data.Records.
26062      */
26063     read : function(response){
26064         var doc = response.responseXML;
26065         if(!doc) {
26066             throw {message: "XmlReader.read: XML Document not available"};
26067         }
26068         return this.readRecords(doc);
26069     },
26070
26071     /**
26072      * Create a data block containing Roo.data.Records from an XML document.
26073          * @param {Object} doc A parsed XML document.
26074      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26075      * a cache of Roo.data.Records.
26076      */
26077     readRecords : function(doc){
26078         /**
26079          * After any data loads/reads, the raw XML Document is available for further custom processing.
26080          * @type XMLDocument
26081          */
26082         this.xmlData = doc;
26083         var root = doc.documentElement || doc;
26084         var q = Roo.DomQuery;
26085         var recordType = this.recordType, fields = recordType.prototype.fields;
26086         var sid = this.meta.id;
26087         var totalRecords = 0, success = true;
26088         if(this.meta.totalRecords){
26089             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26090         }
26091         
26092         if(this.meta.success){
26093             var sv = q.selectValue(this.meta.success, root, true);
26094             success = sv !== false && sv !== 'false';
26095         }
26096         var records = [];
26097         var ns = q.select(this.meta.record, root);
26098         for(var i = 0, len = ns.length; i < len; i++) {
26099                 var n = ns[i];
26100                 var values = {};
26101                 var id = sid ? q.selectValue(sid, n) : undefined;
26102                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26103                     var f = fields.items[j];
26104                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26105                     v = f.convert(v);
26106                     values[f.name] = v;
26107                 }
26108                 var record = new recordType(values, id);
26109                 record.node = n;
26110                 records[records.length] = record;
26111             }
26112
26113             return {
26114                 success : success,
26115                 records : records,
26116                 totalRecords : totalRecords || records.length
26117             };
26118     }
26119 });/*
26120  * Based on:
26121  * Ext JS Library 1.1.1
26122  * Copyright(c) 2006-2007, Ext JS, LLC.
26123  *
26124  * Originally Released Under LGPL - original licence link has changed is not relivant.
26125  *
26126  * Fork - LGPL
26127  * <script type="text/javascript">
26128  */
26129
26130 /**
26131  * @class Roo.data.ArrayReader
26132  * @extends Roo.data.DataReader
26133  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26134  * Each element of that Array represents a row of data fields. The
26135  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26136  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26137  * <p>
26138  * Example code:.
26139  * <pre><code>
26140 var RecordDef = Roo.data.Record.create([
26141     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26142     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26143 ]);
26144 var myReader = new Roo.data.ArrayReader({
26145     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26146 }, RecordDef);
26147 </code></pre>
26148  * <p>
26149  * This would consume an Array like this:
26150  * <pre><code>
26151 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26152   </code></pre>
26153  
26154  * @constructor
26155  * Create a new JsonReader
26156  * @param {Object} meta Metadata configuration options.
26157  * @param {Object|Array} recordType Either an Array of field definition objects
26158  * 
26159  * @cfg {Array} fields Array of field definition objects
26160  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26161  * as specified to {@link Roo.data.Record#create},
26162  * or an {@link Roo.data.Record} object
26163  *
26164  * 
26165  * created using {@link Roo.data.Record#create}.
26166  */
26167 Roo.data.ArrayReader = function(meta, recordType)
26168 {    
26169     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26170 };
26171
26172 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26173     
26174       /**
26175      * Create a data block containing Roo.data.Records from an XML document.
26176      * @param {Object} o An Array of row objects which represents the dataset.
26177      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26178      * a cache of Roo.data.Records.
26179      */
26180     readRecords : function(o)
26181     {
26182         var sid = this.meta ? this.meta.id : null;
26183         var recordType = this.recordType, fields = recordType.prototype.fields;
26184         var records = [];
26185         var root = o;
26186         for(var i = 0; i < root.length; i++){
26187             var n = root[i];
26188             var values = {};
26189             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26190             for(var j = 0, jlen = fields.length; j < jlen; j++){
26191                 var f = fields.items[j];
26192                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26193                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26194                 v = f.convert(v);
26195                 values[f.name] = v;
26196             }
26197             var record = new recordType(values, id);
26198             record.json = n;
26199             records[records.length] = record;
26200         }
26201         return {
26202             records : records,
26203             totalRecords : records.length
26204         };
26205     },
26206     // used when loading children.. @see loadDataFromChildren
26207     toLoadData: function(rec)
26208     {
26209         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26210         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26211         
26212     }
26213     
26214     
26215 });/*
26216  * Based on:
26217  * Ext JS Library 1.1.1
26218  * Copyright(c) 2006-2007, Ext JS, LLC.
26219  *
26220  * Originally Released Under LGPL - original licence link has changed is not relivant.
26221  *
26222  * Fork - LGPL
26223  * <script type="text/javascript">
26224  */
26225
26226
26227 /**
26228  * @class Roo.data.Tree
26229  * @extends Roo.util.Observable
26230  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26231  * in the tree have most standard DOM functionality.
26232  * @constructor
26233  * @param {Node} root (optional) The root node
26234  */
26235 Roo.data.Tree = function(root){
26236    this.nodeHash = {};
26237    /**
26238     * The root node for this tree
26239     * @type Node
26240     */
26241    this.root = null;
26242    if(root){
26243        this.setRootNode(root);
26244    }
26245    this.addEvents({
26246        /**
26247         * @event append
26248         * Fires when a new child node is appended to a node in this tree.
26249         * @param {Tree} tree The owner tree
26250         * @param {Node} parent The parent node
26251         * @param {Node} node The newly appended node
26252         * @param {Number} index The index of the newly appended node
26253         */
26254        "append" : true,
26255        /**
26256         * @event remove
26257         * Fires when a child node is removed from a node in this tree.
26258         * @param {Tree} tree The owner tree
26259         * @param {Node} parent The parent node
26260         * @param {Node} node The child node removed
26261         */
26262        "remove" : true,
26263        /**
26264         * @event move
26265         * Fires when a node is moved to a new location in the tree
26266         * @param {Tree} tree The owner tree
26267         * @param {Node} node The node moved
26268         * @param {Node} oldParent The old parent of this node
26269         * @param {Node} newParent The new parent of this node
26270         * @param {Number} index The index it was moved to
26271         */
26272        "move" : true,
26273        /**
26274         * @event insert
26275         * Fires when a new child node is inserted in a node in this tree.
26276         * @param {Tree} tree The owner tree
26277         * @param {Node} parent The parent node
26278         * @param {Node} node The child node inserted
26279         * @param {Node} refNode The child node the node was inserted before
26280         */
26281        "insert" : true,
26282        /**
26283         * @event beforeappend
26284         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26285         * @param {Tree} tree The owner tree
26286         * @param {Node} parent The parent node
26287         * @param {Node} node The child node to be appended
26288         */
26289        "beforeappend" : true,
26290        /**
26291         * @event beforeremove
26292         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26293         * @param {Tree} tree The owner tree
26294         * @param {Node} parent The parent node
26295         * @param {Node} node The child node to be removed
26296         */
26297        "beforeremove" : true,
26298        /**
26299         * @event beforemove
26300         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26301         * @param {Tree} tree The owner tree
26302         * @param {Node} node The node being moved
26303         * @param {Node} oldParent The parent of the node
26304         * @param {Node} newParent The new parent the node is moving to
26305         * @param {Number} index The index it is being moved to
26306         */
26307        "beforemove" : true,
26308        /**
26309         * @event beforeinsert
26310         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26311         * @param {Tree} tree The owner tree
26312         * @param {Node} parent The parent node
26313         * @param {Node} node The child node to be inserted
26314         * @param {Node} refNode The child node the node is being inserted before
26315         */
26316        "beforeinsert" : true
26317    });
26318
26319     Roo.data.Tree.superclass.constructor.call(this);
26320 };
26321
26322 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26323     pathSeparator: "/",
26324
26325     proxyNodeEvent : function(){
26326         return this.fireEvent.apply(this, arguments);
26327     },
26328
26329     /**
26330      * Returns the root node for this tree.
26331      * @return {Node}
26332      */
26333     getRootNode : function(){
26334         return this.root;
26335     },
26336
26337     /**
26338      * Sets the root node for this tree.
26339      * @param {Node} node
26340      * @return {Node}
26341      */
26342     setRootNode : function(node){
26343         this.root = node;
26344         node.ownerTree = this;
26345         node.isRoot = true;
26346         this.registerNode(node);
26347         return node;
26348     },
26349
26350     /**
26351      * Gets a node in this tree by its id.
26352      * @param {String} id
26353      * @return {Node}
26354      */
26355     getNodeById : function(id){
26356         return this.nodeHash[id];
26357     },
26358
26359     registerNode : function(node){
26360         this.nodeHash[node.id] = node;
26361     },
26362
26363     unregisterNode : function(node){
26364         delete this.nodeHash[node.id];
26365     },
26366
26367     toString : function(){
26368         return "[Tree"+(this.id?" "+this.id:"")+"]";
26369     }
26370 });
26371
26372 /**
26373  * @class Roo.data.Node
26374  * @extends Roo.util.Observable
26375  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26376  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26377  * @constructor
26378  * @param {Object} attributes The attributes/config for the node
26379  */
26380 Roo.data.Node = function(attributes){
26381     /**
26382      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26383      * @type {Object}
26384      */
26385     this.attributes = attributes || {};
26386     this.leaf = this.attributes.leaf;
26387     /**
26388      * The node id. @type String
26389      */
26390     this.id = this.attributes.id;
26391     if(!this.id){
26392         this.id = Roo.id(null, "ynode-");
26393         this.attributes.id = this.id;
26394     }
26395      
26396     
26397     /**
26398      * All child nodes of this node. @type Array
26399      */
26400     this.childNodes = [];
26401     if(!this.childNodes.indexOf){ // indexOf is a must
26402         this.childNodes.indexOf = function(o){
26403             for(var i = 0, len = this.length; i < len; i++){
26404                 if(this[i] == o) {
26405                     return i;
26406                 }
26407             }
26408             return -1;
26409         };
26410     }
26411     /**
26412      * The parent node for this node. @type Node
26413      */
26414     this.parentNode = null;
26415     /**
26416      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26417      */
26418     this.firstChild = null;
26419     /**
26420      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26421      */
26422     this.lastChild = null;
26423     /**
26424      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26425      */
26426     this.previousSibling = null;
26427     /**
26428      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26429      */
26430     this.nextSibling = null;
26431
26432     this.addEvents({
26433        /**
26434         * @event append
26435         * Fires when a new child node is appended
26436         * @param {Tree} tree The owner tree
26437         * @param {Node} this This node
26438         * @param {Node} node The newly appended node
26439         * @param {Number} index The index of the newly appended node
26440         */
26441        "append" : true,
26442        /**
26443         * @event remove
26444         * Fires when a child node is removed
26445         * @param {Tree} tree The owner tree
26446         * @param {Node} this This node
26447         * @param {Node} node The removed node
26448         */
26449        "remove" : true,
26450        /**
26451         * @event move
26452         * Fires when this node is moved to a new location in the tree
26453         * @param {Tree} tree The owner tree
26454         * @param {Node} this This node
26455         * @param {Node} oldParent The old parent of this node
26456         * @param {Node} newParent The new parent of this node
26457         * @param {Number} index The index it was moved to
26458         */
26459        "move" : true,
26460        /**
26461         * @event insert
26462         * Fires when a new child node is inserted.
26463         * @param {Tree} tree The owner tree
26464         * @param {Node} this This node
26465         * @param {Node} node The child node inserted
26466         * @param {Node} refNode The child node the node was inserted before
26467         */
26468        "insert" : true,
26469        /**
26470         * @event beforeappend
26471         * Fires before a new child is appended, return false to cancel the append.
26472         * @param {Tree} tree The owner tree
26473         * @param {Node} this This node
26474         * @param {Node} node The child node to be appended
26475         */
26476        "beforeappend" : true,
26477        /**
26478         * @event beforeremove
26479         * Fires before a child is removed, return false to cancel the remove.
26480         * @param {Tree} tree The owner tree
26481         * @param {Node} this This node
26482         * @param {Node} node The child node to be removed
26483         */
26484        "beforeremove" : true,
26485        /**
26486         * @event beforemove
26487         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26488         * @param {Tree} tree The owner tree
26489         * @param {Node} this This node
26490         * @param {Node} oldParent The parent of this node
26491         * @param {Node} newParent The new parent this node is moving to
26492         * @param {Number} index The index it is being moved to
26493         */
26494        "beforemove" : true,
26495        /**
26496         * @event beforeinsert
26497         * Fires before a new child is inserted, return false to cancel the insert.
26498         * @param {Tree} tree The owner tree
26499         * @param {Node} this This node
26500         * @param {Node} node The child node to be inserted
26501         * @param {Node} refNode The child node the node is being inserted before
26502         */
26503        "beforeinsert" : true
26504    });
26505     this.listeners = this.attributes.listeners;
26506     Roo.data.Node.superclass.constructor.call(this);
26507 };
26508
26509 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26510     fireEvent : function(evtName){
26511         // first do standard event for this node
26512         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26513             return false;
26514         }
26515         // then bubble it up to the tree if the event wasn't cancelled
26516         var ot = this.getOwnerTree();
26517         if(ot){
26518             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26519                 return false;
26520             }
26521         }
26522         return true;
26523     },
26524
26525     /**
26526      * Returns true if this node is a leaf
26527      * @return {Boolean}
26528      */
26529     isLeaf : function(){
26530         return this.leaf === true;
26531     },
26532
26533     // private
26534     setFirstChild : function(node){
26535         this.firstChild = node;
26536     },
26537
26538     //private
26539     setLastChild : function(node){
26540         this.lastChild = node;
26541     },
26542
26543
26544     /**
26545      * Returns true if this node is the last child of its parent
26546      * @return {Boolean}
26547      */
26548     isLast : function(){
26549        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26550     },
26551
26552     /**
26553      * Returns true if this node is the first child of its parent
26554      * @return {Boolean}
26555      */
26556     isFirst : function(){
26557        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26558     },
26559
26560     hasChildNodes : function(){
26561         return !this.isLeaf() && this.childNodes.length > 0;
26562     },
26563
26564     /**
26565      * Insert node(s) as the last child node of this node.
26566      * @param {Node/Array} node The node or Array of nodes to append
26567      * @return {Node} The appended node if single append, or null if an array was passed
26568      */
26569     appendChild : function(node){
26570         var multi = false;
26571         if(node instanceof Array){
26572             multi = node;
26573         }else if(arguments.length > 1){
26574             multi = arguments;
26575         }
26576         
26577         // if passed an array or multiple args do them one by one
26578         if(multi){
26579             for(var i = 0, len = multi.length; i < len; i++) {
26580                 this.appendChild(multi[i]);
26581             }
26582         }else{
26583             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26584                 return false;
26585             }
26586             var index = this.childNodes.length;
26587             var oldParent = node.parentNode;
26588             // it's a move, make sure we move it cleanly
26589             if(oldParent){
26590                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26591                     return false;
26592                 }
26593                 oldParent.removeChild(node);
26594             }
26595             
26596             index = this.childNodes.length;
26597             if(index == 0){
26598                 this.setFirstChild(node);
26599             }
26600             this.childNodes.push(node);
26601             node.parentNode = this;
26602             var ps = this.childNodes[index-1];
26603             if(ps){
26604                 node.previousSibling = ps;
26605                 ps.nextSibling = node;
26606             }else{
26607                 node.previousSibling = null;
26608             }
26609             node.nextSibling = null;
26610             this.setLastChild(node);
26611             node.setOwnerTree(this.getOwnerTree());
26612             this.fireEvent("append", this.ownerTree, this, node, index);
26613             if(this.ownerTree) {
26614                 this.ownerTree.fireEvent("appendnode", this, node, index);
26615             }
26616             if(oldParent){
26617                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26618             }
26619             return node;
26620         }
26621     },
26622
26623     /**
26624      * Removes a child node from this node.
26625      * @param {Node} node The node to remove
26626      * @return {Node} The removed node
26627      */
26628     removeChild : function(node){
26629         var index = this.childNodes.indexOf(node);
26630         if(index == -1){
26631             return false;
26632         }
26633         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26634             return false;
26635         }
26636
26637         // remove it from childNodes collection
26638         this.childNodes.splice(index, 1);
26639
26640         // update siblings
26641         if(node.previousSibling){
26642             node.previousSibling.nextSibling = node.nextSibling;
26643         }
26644         if(node.nextSibling){
26645             node.nextSibling.previousSibling = node.previousSibling;
26646         }
26647
26648         // update child refs
26649         if(this.firstChild == node){
26650             this.setFirstChild(node.nextSibling);
26651         }
26652         if(this.lastChild == node){
26653             this.setLastChild(node.previousSibling);
26654         }
26655
26656         node.setOwnerTree(null);
26657         // clear any references from the node
26658         node.parentNode = null;
26659         node.previousSibling = null;
26660         node.nextSibling = null;
26661         this.fireEvent("remove", this.ownerTree, this, node);
26662         return node;
26663     },
26664
26665     /**
26666      * Inserts the first node before the second node in this nodes childNodes collection.
26667      * @param {Node} node The node to insert
26668      * @param {Node} refNode The node to insert before (if null the node is appended)
26669      * @return {Node} The inserted node
26670      */
26671     insertBefore : function(node, refNode){
26672         if(!refNode){ // like standard Dom, refNode can be null for append
26673             return this.appendChild(node);
26674         }
26675         // nothing to do
26676         if(node == refNode){
26677             return false;
26678         }
26679
26680         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26681             return false;
26682         }
26683         var index = this.childNodes.indexOf(refNode);
26684         var oldParent = node.parentNode;
26685         var refIndex = index;
26686
26687         // when moving internally, indexes will change after remove
26688         if(oldParent == this && this.childNodes.indexOf(node) < index){
26689             refIndex--;
26690         }
26691
26692         // it's a move, make sure we move it cleanly
26693         if(oldParent){
26694             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26695                 return false;
26696             }
26697             oldParent.removeChild(node);
26698         }
26699         if(refIndex == 0){
26700             this.setFirstChild(node);
26701         }
26702         this.childNodes.splice(refIndex, 0, node);
26703         node.parentNode = this;
26704         var ps = this.childNodes[refIndex-1];
26705         if(ps){
26706             node.previousSibling = ps;
26707             ps.nextSibling = node;
26708         }else{
26709             node.previousSibling = null;
26710         }
26711         node.nextSibling = refNode;
26712         refNode.previousSibling = node;
26713         node.setOwnerTree(this.getOwnerTree());
26714         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26715         if(oldParent){
26716             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26717         }
26718         return node;
26719     },
26720
26721     /**
26722      * Returns the child node at the specified index.
26723      * @param {Number} index
26724      * @return {Node}
26725      */
26726     item : function(index){
26727         return this.childNodes[index];
26728     },
26729
26730     /**
26731      * Replaces one child node in this node with another.
26732      * @param {Node} newChild The replacement node
26733      * @param {Node} oldChild The node to replace
26734      * @return {Node} The replaced node
26735      */
26736     replaceChild : function(newChild, oldChild){
26737         this.insertBefore(newChild, oldChild);
26738         this.removeChild(oldChild);
26739         return oldChild;
26740     },
26741
26742     /**
26743      * Returns the index of a child node
26744      * @param {Node} node
26745      * @return {Number} The index of the node or -1 if it was not found
26746      */
26747     indexOf : function(child){
26748         return this.childNodes.indexOf(child);
26749     },
26750
26751     /**
26752      * Returns the tree this node is in.
26753      * @return {Tree}
26754      */
26755     getOwnerTree : function(){
26756         // if it doesn't have one, look for one
26757         if(!this.ownerTree){
26758             var p = this;
26759             while(p){
26760                 if(p.ownerTree){
26761                     this.ownerTree = p.ownerTree;
26762                     break;
26763                 }
26764                 p = p.parentNode;
26765             }
26766         }
26767         return this.ownerTree;
26768     },
26769
26770     /**
26771      * Returns depth of this node (the root node has a depth of 0)
26772      * @return {Number}
26773      */
26774     getDepth : function(){
26775         var depth = 0;
26776         var p = this;
26777         while(p.parentNode){
26778             ++depth;
26779             p = p.parentNode;
26780         }
26781         return depth;
26782     },
26783
26784     // private
26785     setOwnerTree : function(tree){
26786         // if it's move, we need to update everyone
26787         if(tree != this.ownerTree){
26788             if(this.ownerTree){
26789                 this.ownerTree.unregisterNode(this);
26790             }
26791             this.ownerTree = tree;
26792             var cs = this.childNodes;
26793             for(var i = 0, len = cs.length; i < len; i++) {
26794                 cs[i].setOwnerTree(tree);
26795             }
26796             if(tree){
26797                 tree.registerNode(this);
26798             }
26799         }
26800     },
26801
26802     /**
26803      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26804      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26805      * @return {String} The path
26806      */
26807     getPath : function(attr){
26808         attr = attr || "id";
26809         var p = this.parentNode;
26810         var b = [this.attributes[attr]];
26811         while(p){
26812             b.unshift(p.attributes[attr]);
26813             p = p.parentNode;
26814         }
26815         var sep = this.getOwnerTree().pathSeparator;
26816         return sep + b.join(sep);
26817     },
26818
26819     /**
26820      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26821      * function call will be the scope provided or the current node. The arguments to the function
26822      * will be the args provided or the current node. If the function returns false at any point,
26823      * the bubble is stopped.
26824      * @param {Function} fn The function to call
26825      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26826      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26827      */
26828     bubble : function(fn, scope, args){
26829         var p = this;
26830         while(p){
26831             if(fn.call(scope || p, args || p) === false){
26832                 break;
26833             }
26834             p = p.parentNode;
26835         }
26836     },
26837
26838     /**
26839      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26840      * function call will be the scope provided or the current node. The arguments to the function
26841      * will be the args provided or the current node. If the function returns false at any point,
26842      * the cascade is stopped on that branch.
26843      * @param {Function} fn The function to call
26844      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26845      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26846      */
26847     cascade : function(fn, scope, args){
26848         if(fn.call(scope || this, args || this) !== false){
26849             var cs = this.childNodes;
26850             for(var i = 0, len = cs.length; i < len; i++) {
26851                 cs[i].cascade(fn, scope, args);
26852             }
26853         }
26854     },
26855
26856     /**
26857      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26858      * function call will be the scope provided or the current node. The arguments to the function
26859      * will be the args provided or the current node. If the function returns false at any point,
26860      * the iteration stops.
26861      * @param {Function} fn The function to call
26862      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26863      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26864      */
26865     eachChild : function(fn, scope, args){
26866         var cs = this.childNodes;
26867         for(var i = 0, len = cs.length; i < len; i++) {
26868                 if(fn.call(scope || this, args || cs[i]) === false){
26869                     break;
26870                 }
26871         }
26872     },
26873
26874     /**
26875      * Finds the first child that has the attribute with the specified value.
26876      * @param {String} attribute The attribute name
26877      * @param {Mixed} value The value to search for
26878      * @return {Node} The found child or null if none was found
26879      */
26880     findChild : function(attribute, value){
26881         var cs = this.childNodes;
26882         for(var i = 0, len = cs.length; i < len; i++) {
26883                 if(cs[i].attributes[attribute] == value){
26884                     return cs[i];
26885                 }
26886         }
26887         return null;
26888     },
26889
26890     /**
26891      * Finds the first child by a custom function. The child matches if the function passed
26892      * returns true.
26893      * @param {Function} fn
26894      * @param {Object} scope (optional)
26895      * @return {Node} The found child or null if none was found
26896      */
26897     findChildBy : function(fn, scope){
26898         var cs = this.childNodes;
26899         for(var i = 0, len = cs.length; i < len; i++) {
26900                 if(fn.call(scope||cs[i], cs[i]) === true){
26901                     return cs[i];
26902                 }
26903         }
26904         return null;
26905     },
26906
26907     /**
26908      * Sorts this nodes children using the supplied sort function
26909      * @param {Function} fn
26910      * @param {Object} scope (optional)
26911      */
26912     sort : function(fn, scope){
26913         var cs = this.childNodes;
26914         var len = cs.length;
26915         if(len > 0){
26916             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26917             cs.sort(sortFn);
26918             for(var i = 0; i < len; i++){
26919                 var n = cs[i];
26920                 n.previousSibling = cs[i-1];
26921                 n.nextSibling = cs[i+1];
26922                 if(i == 0){
26923                     this.setFirstChild(n);
26924                 }
26925                 if(i == len-1){
26926                     this.setLastChild(n);
26927                 }
26928             }
26929         }
26930     },
26931
26932     /**
26933      * Returns true if this node is an ancestor (at any point) of the passed node.
26934      * @param {Node} node
26935      * @return {Boolean}
26936      */
26937     contains : function(node){
26938         return node.isAncestor(this);
26939     },
26940
26941     /**
26942      * Returns true if the passed node is an ancestor (at any point) of this node.
26943      * @param {Node} node
26944      * @return {Boolean}
26945      */
26946     isAncestor : function(node){
26947         var p = this.parentNode;
26948         while(p){
26949             if(p == node){
26950                 return true;
26951             }
26952             p = p.parentNode;
26953         }
26954         return false;
26955     },
26956
26957     toString : function(){
26958         return "[Node"+(this.id?" "+this.id:"")+"]";
26959     }
26960 });/*
26961  * Based on:
26962  * Ext JS Library 1.1.1
26963  * Copyright(c) 2006-2007, Ext JS, LLC.
26964  *
26965  * Originally Released Under LGPL - original licence link has changed is not relivant.
26966  *
26967  * Fork - LGPL
26968  * <script type="text/javascript">
26969  */
26970
26971
26972 /**
26973  * @class Roo.Shadow
26974  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26975  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26976  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26977  * @constructor
26978  * Create a new Shadow
26979  * @param {Object} config The config object
26980  */
26981 Roo.Shadow = function(config){
26982     Roo.apply(this, config);
26983     if(typeof this.mode != "string"){
26984         this.mode = this.defaultMode;
26985     }
26986     var o = this.offset, a = {h: 0};
26987     var rad = Math.floor(this.offset/2);
26988     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26989         case "drop":
26990             a.w = 0;
26991             a.l = a.t = o;
26992             a.t -= 1;
26993             if(Roo.isIE){
26994                 a.l -= this.offset + rad;
26995                 a.t -= this.offset + rad;
26996                 a.w -= rad;
26997                 a.h -= rad;
26998                 a.t += 1;
26999             }
27000         break;
27001         case "sides":
27002             a.w = (o*2);
27003             a.l = -o;
27004             a.t = o-1;
27005             if(Roo.isIE){
27006                 a.l -= (this.offset - rad);
27007                 a.t -= this.offset + rad;
27008                 a.l += 1;
27009                 a.w -= (this.offset - rad)*2;
27010                 a.w -= rad + 1;
27011                 a.h -= 1;
27012             }
27013         break;
27014         case "frame":
27015             a.w = a.h = (o*2);
27016             a.l = a.t = -o;
27017             a.t += 1;
27018             a.h -= 2;
27019             if(Roo.isIE){
27020                 a.l -= (this.offset - rad);
27021                 a.t -= (this.offset - rad);
27022                 a.l += 1;
27023                 a.w -= (this.offset + rad + 1);
27024                 a.h -= (this.offset + rad);
27025                 a.h += 1;
27026             }
27027         break;
27028     };
27029
27030     this.adjusts = a;
27031 };
27032
27033 Roo.Shadow.prototype = {
27034     /**
27035      * @cfg {String} mode
27036      * The shadow display mode.  Supports the following options:<br />
27037      * sides: Shadow displays on both sides and bottom only<br />
27038      * frame: Shadow displays equally on all four sides<br />
27039      * drop: Traditional bottom-right drop shadow (default)
27040      */
27041     mode: false,
27042     /**
27043      * @cfg {String} offset
27044      * The number of pixels to offset the shadow from the element (defaults to 4)
27045      */
27046     offset: 4,
27047
27048     // private
27049     defaultMode: "drop",
27050
27051     /**
27052      * Displays the shadow under the target element
27053      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27054      */
27055     show : function(target){
27056         target = Roo.get(target);
27057         if(!this.el){
27058             this.el = Roo.Shadow.Pool.pull();
27059             if(this.el.dom.nextSibling != target.dom){
27060                 this.el.insertBefore(target);
27061             }
27062         }
27063         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27064         if(Roo.isIE){
27065             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27066         }
27067         this.realign(
27068             target.getLeft(true),
27069             target.getTop(true),
27070             target.getWidth(),
27071             target.getHeight()
27072         );
27073         this.el.dom.style.display = "block";
27074     },
27075
27076     /**
27077      * Returns true if the shadow is visible, else false
27078      */
27079     isVisible : function(){
27080         return this.el ? true : false;  
27081     },
27082
27083     /**
27084      * Direct alignment when values are already available. Show must be called at least once before
27085      * calling this method to ensure it is initialized.
27086      * @param {Number} left The target element left position
27087      * @param {Number} top The target element top position
27088      * @param {Number} width The target element width
27089      * @param {Number} height The target element height
27090      */
27091     realign : function(l, t, w, h){
27092         if(!this.el){
27093             return;
27094         }
27095         var a = this.adjusts, d = this.el.dom, s = d.style;
27096         var iea = 0;
27097         s.left = (l+a.l)+"px";
27098         s.top = (t+a.t)+"px";
27099         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27100  
27101         if(s.width != sws || s.height != shs){
27102             s.width = sws;
27103             s.height = shs;
27104             if(!Roo.isIE){
27105                 var cn = d.childNodes;
27106                 var sww = Math.max(0, (sw-12))+"px";
27107                 cn[0].childNodes[1].style.width = sww;
27108                 cn[1].childNodes[1].style.width = sww;
27109                 cn[2].childNodes[1].style.width = sww;
27110                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Hides this shadow
27117      */
27118     hide : function(){
27119         if(this.el){
27120             this.el.dom.style.display = "none";
27121             Roo.Shadow.Pool.push(this.el);
27122             delete this.el;
27123         }
27124     },
27125
27126     /**
27127      * Adjust the z-index of this shadow
27128      * @param {Number} zindex The new z-index
27129      */
27130     setZIndex : function(z){
27131         this.zIndex = z;
27132         if(this.el){
27133             this.el.setStyle("z-index", z);
27134         }
27135     }
27136 };
27137
27138 // Private utility class that manages the internal Shadow cache
27139 Roo.Shadow.Pool = function(){
27140     var p = [];
27141     var markup = Roo.isIE ?
27142                  '<div class="x-ie-shadow"></div>' :
27143                  '<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>';
27144     return {
27145         pull : function(){
27146             var sh = p.shift();
27147             if(!sh){
27148                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27149                 sh.autoBoxAdjust = false;
27150             }
27151             return sh;
27152         },
27153
27154         push : function(sh){
27155             p.push(sh);
27156         }
27157     };
27158 }();/*
27159  * Based on:
27160  * Ext JS Library 1.1.1
27161  * Copyright(c) 2006-2007, Ext JS, LLC.
27162  *
27163  * Originally Released Under LGPL - original licence link has changed is not relivant.
27164  *
27165  * Fork - LGPL
27166  * <script type="text/javascript">
27167  */
27168
27169
27170 /**
27171  * @class Roo.SplitBar
27172  * @extends Roo.util.Observable
27173  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27174  * <br><br>
27175  * Usage:
27176  * <pre><code>
27177 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27178                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27179 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27180 split.minSize = 100;
27181 split.maxSize = 600;
27182 split.animate = true;
27183 split.on('moved', splitterMoved);
27184 </code></pre>
27185  * @constructor
27186  * Create a new SplitBar
27187  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27188  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27189  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27190  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27191                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27192                         position of the SplitBar).
27193  */
27194 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27195     
27196     /** @private */
27197     this.el = Roo.get(dragElement, true);
27198     this.el.dom.unselectable = "on";
27199     /** @private */
27200     this.resizingEl = Roo.get(resizingElement, true);
27201
27202     /**
27203      * @private
27204      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27205      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27206      * @type Number
27207      */
27208     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27209     
27210     /**
27211      * The minimum size of the resizing element. (Defaults to 0)
27212      * @type Number
27213      */
27214     this.minSize = 0;
27215     
27216     /**
27217      * The maximum size of the resizing element. (Defaults to 2000)
27218      * @type Number
27219      */
27220     this.maxSize = 2000;
27221     
27222     /**
27223      * Whether to animate the transition to the new size
27224      * @type Boolean
27225      */
27226     this.animate = false;
27227     
27228     /**
27229      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27230      * @type Boolean
27231      */
27232     this.useShim = false;
27233     
27234     /** @private */
27235     this.shim = null;
27236     
27237     if(!existingProxy){
27238         /** @private */
27239         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27240     }else{
27241         this.proxy = Roo.get(existingProxy).dom;
27242     }
27243     /** @private */
27244     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27245     
27246     /** @private */
27247     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27248     
27249     /** @private */
27250     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27251     
27252     /** @private */
27253     this.dragSpecs = {};
27254     
27255     /**
27256      * @private The adapter to use to positon and resize elements
27257      */
27258     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27259     this.adapter.init(this);
27260     
27261     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27262         /** @private */
27263         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27264         this.el.addClass("x-splitbar-h");
27265     }else{
27266         /** @private */
27267         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27268         this.el.addClass("x-splitbar-v");
27269     }
27270     
27271     this.addEvents({
27272         /**
27273          * @event resize
27274          * Fires when the splitter is moved (alias for {@link #event-moved})
27275          * @param {Roo.SplitBar} this
27276          * @param {Number} newSize the new width or height
27277          */
27278         "resize" : true,
27279         /**
27280          * @event moved
27281          * Fires when the splitter is moved
27282          * @param {Roo.SplitBar} this
27283          * @param {Number} newSize the new width or height
27284          */
27285         "moved" : true,
27286         /**
27287          * @event beforeresize
27288          * Fires before the splitter is dragged
27289          * @param {Roo.SplitBar} this
27290          */
27291         "beforeresize" : true,
27292
27293         "beforeapply" : true
27294     });
27295
27296     Roo.util.Observable.call(this);
27297 };
27298
27299 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27300     onStartProxyDrag : function(x, y){
27301         this.fireEvent("beforeresize", this);
27302         if(!this.overlay){
27303             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27304             o.unselectable();
27305             o.enableDisplayMode("block");
27306             // all splitbars share the same overlay
27307             Roo.SplitBar.prototype.overlay = o;
27308         }
27309         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27310         this.overlay.show();
27311         Roo.get(this.proxy).setDisplayed("block");
27312         var size = this.adapter.getElementSize(this);
27313         this.activeMinSize = this.getMinimumSize();;
27314         this.activeMaxSize = this.getMaximumSize();;
27315         var c1 = size - this.activeMinSize;
27316         var c2 = Math.max(this.activeMaxSize - size, 0);
27317         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27318             this.dd.resetConstraints();
27319             this.dd.setXConstraint(
27320                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27321                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27322             );
27323             this.dd.setYConstraint(0, 0);
27324         }else{
27325             this.dd.resetConstraints();
27326             this.dd.setXConstraint(0, 0);
27327             this.dd.setYConstraint(
27328                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27329                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27330             );
27331          }
27332         this.dragSpecs.startSize = size;
27333         this.dragSpecs.startPoint = [x, y];
27334         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27335     },
27336     
27337     /** 
27338      * @private Called after the drag operation by the DDProxy
27339      */
27340     onEndProxyDrag : function(e){
27341         Roo.get(this.proxy).setDisplayed(false);
27342         var endPoint = Roo.lib.Event.getXY(e);
27343         if(this.overlay){
27344             this.overlay.hide();
27345         }
27346         var newSize;
27347         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27348             newSize = this.dragSpecs.startSize + 
27349                 (this.placement == Roo.SplitBar.LEFT ?
27350                     endPoint[0] - this.dragSpecs.startPoint[0] :
27351                     this.dragSpecs.startPoint[0] - endPoint[0]
27352                 );
27353         }else{
27354             newSize = this.dragSpecs.startSize + 
27355                 (this.placement == Roo.SplitBar.TOP ?
27356                     endPoint[1] - this.dragSpecs.startPoint[1] :
27357                     this.dragSpecs.startPoint[1] - endPoint[1]
27358                 );
27359         }
27360         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27361         if(newSize != this.dragSpecs.startSize){
27362             if(this.fireEvent('beforeapply', this, newSize) !== false){
27363                 this.adapter.setElementSize(this, newSize);
27364                 this.fireEvent("moved", this, newSize);
27365                 this.fireEvent("resize", this, newSize);
27366             }
27367         }
27368     },
27369     
27370     /**
27371      * Get the adapter this SplitBar uses
27372      * @return The adapter object
27373      */
27374     getAdapter : function(){
27375         return this.adapter;
27376     },
27377     
27378     /**
27379      * Set the adapter this SplitBar uses
27380      * @param {Object} adapter A SplitBar adapter object
27381      */
27382     setAdapter : function(adapter){
27383         this.adapter = adapter;
27384         this.adapter.init(this);
27385     },
27386     
27387     /**
27388      * Gets the minimum size for the resizing element
27389      * @return {Number} The minimum size
27390      */
27391     getMinimumSize : function(){
27392         return this.minSize;
27393     },
27394     
27395     /**
27396      * Sets the minimum size for the resizing element
27397      * @param {Number} minSize The minimum size
27398      */
27399     setMinimumSize : function(minSize){
27400         this.minSize = minSize;
27401     },
27402     
27403     /**
27404      * Gets the maximum size for the resizing element
27405      * @return {Number} The maximum size
27406      */
27407     getMaximumSize : function(){
27408         return this.maxSize;
27409     },
27410     
27411     /**
27412      * Sets the maximum size for the resizing element
27413      * @param {Number} maxSize The maximum size
27414      */
27415     setMaximumSize : function(maxSize){
27416         this.maxSize = maxSize;
27417     },
27418     
27419     /**
27420      * Sets the initialize size for the resizing element
27421      * @param {Number} size The initial size
27422      */
27423     setCurrentSize : function(size){
27424         var oldAnimate = this.animate;
27425         this.animate = false;
27426         this.adapter.setElementSize(this, size);
27427         this.animate = oldAnimate;
27428     },
27429     
27430     /**
27431      * Destroy this splitbar. 
27432      * @param {Boolean} removeEl True to remove the element
27433      */
27434     destroy : function(removeEl){
27435         if(this.shim){
27436             this.shim.remove();
27437         }
27438         this.dd.unreg();
27439         this.proxy.parentNode.removeChild(this.proxy);
27440         if(removeEl){
27441             this.el.remove();
27442         }
27443     }
27444 });
27445
27446 /**
27447  * @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.
27448  */
27449 Roo.SplitBar.createProxy = function(dir){
27450     var proxy = new Roo.Element(document.createElement("div"));
27451     proxy.unselectable();
27452     var cls = 'x-splitbar-proxy';
27453     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27454     document.body.appendChild(proxy.dom);
27455     return proxy.dom;
27456 };
27457
27458 /** 
27459  * @class Roo.SplitBar.BasicLayoutAdapter
27460  * Default Adapter. It assumes the splitter and resizing element are not positioned
27461  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27462  */
27463 Roo.SplitBar.BasicLayoutAdapter = function(){
27464 };
27465
27466 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27467     // do nothing for now
27468     init : function(s){
27469     
27470     },
27471     /**
27472      * Called before drag operations to get the current size of the resizing element. 
27473      * @param {Roo.SplitBar} s The SplitBar using this adapter
27474      */
27475      getElementSize : function(s){
27476         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27477             return s.resizingEl.getWidth();
27478         }else{
27479             return s.resizingEl.getHeight();
27480         }
27481     },
27482     
27483     /**
27484      * Called after drag operations to set the size of the resizing element.
27485      * @param {Roo.SplitBar} s The SplitBar using this adapter
27486      * @param {Number} newSize The new size to set
27487      * @param {Function} onComplete A function to be invoked when resizing is complete
27488      */
27489     setElementSize : function(s, newSize, onComplete){
27490         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27491             if(!s.animate){
27492                 s.resizingEl.setWidth(newSize);
27493                 if(onComplete){
27494                     onComplete(s, newSize);
27495                 }
27496             }else{
27497                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27498             }
27499         }else{
27500             
27501             if(!s.animate){
27502                 s.resizingEl.setHeight(newSize);
27503                 if(onComplete){
27504                     onComplete(s, newSize);
27505                 }
27506             }else{
27507                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27508             }
27509         }
27510     }
27511 };
27512
27513 /** 
27514  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27515  * @extends Roo.SplitBar.BasicLayoutAdapter
27516  * Adapter that  moves the splitter element to align with the resized sizing element. 
27517  * Used with an absolute positioned SplitBar.
27518  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27519  * document.body, make sure you assign an id to the body element.
27520  */
27521 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27522     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27523     this.container = Roo.get(container);
27524 };
27525
27526 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27527     init : function(s){
27528         this.basic.init(s);
27529     },
27530     
27531     getElementSize : function(s){
27532         return this.basic.getElementSize(s);
27533     },
27534     
27535     setElementSize : function(s, newSize, onComplete){
27536         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27537     },
27538     
27539     moveSplitter : function(s){
27540         var yes = Roo.SplitBar;
27541         switch(s.placement){
27542             case yes.LEFT:
27543                 s.el.setX(s.resizingEl.getRight());
27544                 break;
27545             case yes.RIGHT:
27546                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27547                 break;
27548             case yes.TOP:
27549                 s.el.setY(s.resizingEl.getBottom());
27550                 break;
27551             case yes.BOTTOM:
27552                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27553                 break;
27554         }
27555     }
27556 };
27557
27558 /**
27559  * Orientation constant - Create a vertical SplitBar
27560  * @static
27561  * @type Number
27562  */
27563 Roo.SplitBar.VERTICAL = 1;
27564
27565 /**
27566  * Orientation constant - Create a horizontal SplitBar
27567  * @static
27568  * @type Number
27569  */
27570 Roo.SplitBar.HORIZONTAL = 2;
27571
27572 /**
27573  * Placement constant - The resizing element is to the left of the splitter element
27574  * @static
27575  * @type Number
27576  */
27577 Roo.SplitBar.LEFT = 1;
27578
27579 /**
27580  * Placement constant - The resizing element is to the right of the splitter element
27581  * @static
27582  * @type Number
27583  */
27584 Roo.SplitBar.RIGHT = 2;
27585
27586 /**
27587  * Placement constant - The resizing element is positioned above the splitter element
27588  * @static
27589  * @type Number
27590  */
27591 Roo.SplitBar.TOP = 3;
27592
27593 /**
27594  * Placement constant - The resizing element is positioned under splitter element
27595  * @static
27596  * @type Number
27597  */
27598 Roo.SplitBar.BOTTOM = 4;
27599 /*
27600  * Based on:
27601  * Ext JS Library 1.1.1
27602  * Copyright(c) 2006-2007, Ext JS, LLC.
27603  *
27604  * Originally Released Under LGPL - original licence link has changed is not relivant.
27605  *
27606  * Fork - LGPL
27607  * <script type="text/javascript">
27608  */
27609
27610 /**
27611  * @class Roo.View
27612  * @extends Roo.util.Observable
27613  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27614  * This class also supports single and multi selection modes. <br>
27615  * Create a data model bound view:
27616  <pre><code>
27617  var store = new Roo.data.Store(...);
27618
27619  var view = new Roo.View({
27620     el : "my-element",
27621     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27622  
27623     singleSelect: true,
27624     selectedClass: "ydataview-selected",
27625     store: store
27626  });
27627
27628  // listen for node click?
27629  view.on("click", function(vw, index, node, e){
27630  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27631  });
27632
27633  // load XML data
27634  dataModel.load("foobar.xml");
27635  </code></pre>
27636  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27637  * <br><br>
27638  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27639  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27640  * 
27641  * Note: old style constructor is still suported (container, template, config)
27642  * 
27643  * @constructor
27644  * Create a new View
27645  * @param {Object} config The config object
27646  * 
27647  */
27648 Roo.View = function(config, depreciated_tpl, depreciated_config){
27649     
27650     this.parent = false;
27651     
27652     if (typeof(depreciated_tpl) == 'undefined') {
27653         // new way.. - universal constructor.
27654         Roo.apply(this, config);
27655         this.el  = Roo.get(this.el);
27656     } else {
27657         // old format..
27658         this.el  = Roo.get(config);
27659         this.tpl = depreciated_tpl;
27660         Roo.apply(this, depreciated_config);
27661     }
27662     this.wrapEl  = this.el.wrap().wrap();
27663     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27664     
27665     
27666     if(typeof(this.tpl) == "string"){
27667         this.tpl = new Roo.Template(this.tpl);
27668     } else {
27669         // support xtype ctors..
27670         this.tpl = new Roo.factory(this.tpl, Roo);
27671     }
27672     
27673     
27674     this.tpl.compile();
27675     
27676     /** @private */
27677     this.addEvents({
27678         /**
27679          * @event beforeclick
27680          * Fires before a click is processed. Returns false to cancel the default action.
27681          * @param {Roo.View} this
27682          * @param {Number} index The index of the target node
27683          * @param {HTMLElement} node The target node
27684          * @param {Roo.EventObject} e The raw event object
27685          */
27686             "beforeclick" : true,
27687         /**
27688          * @event click
27689          * Fires when a template node is clicked.
27690          * @param {Roo.View} this
27691          * @param {Number} index The index of the target node
27692          * @param {HTMLElement} node The target node
27693          * @param {Roo.EventObject} e The raw event object
27694          */
27695             "click" : true,
27696         /**
27697          * @event dblclick
27698          * Fires when a template node is double clicked.
27699          * @param {Roo.View} this
27700          * @param {Number} index The index of the target node
27701          * @param {HTMLElement} node The target node
27702          * @param {Roo.EventObject} e The raw event object
27703          */
27704             "dblclick" : true,
27705         /**
27706          * @event contextmenu
27707          * Fires when a template node is right clicked.
27708          * @param {Roo.View} this
27709          * @param {Number} index The index of the target node
27710          * @param {HTMLElement} node The target node
27711          * @param {Roo.EventObject} e The raw event object
27712          */
27713             "contextmenu" : true,
27714         /**
27715          * @event selectionchange
27716          * Fires when the selected nodes change.
27717          * @param {Roo.View} this
27718          * @param {Array} selections Array of the selected nodes
27719          */
27720             "selectionchange" : true,
27721     
27722         /**
27723          * @event beforeselect
27724          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27725          * @param {Roo.View} this
27726          * @param {HTMLElement} node The node to be selected
27727          * @param {Array} selections Array of currently selected nodes
27728          */
27729             "beforeselect" : true,
27730         /**
27731          * @event preparedata
27732          * Fires on every row to render, to allow you to change the data.
27733          * @param {Roo.View} this
27734          * @param {Object} data to be rendered (change this)
27735          */
27736           "preparedata" : true
27737           
27738           
27739         });
27740
27741
27742
27743     this.el.on({
27744         "click": this.onClick,
27745         "dblclick": this.onDblClick,
27746         "contextmenu": this.onContextMenu,
27747         scope:this
27748     });
27749
27750     this.selections = [];
27751     this.nodes = [];
27752     this.cmp = new Roo.CompositeElementLite([]);
27753     if(this.store){
27754         this.store = Roo.factory(this.store, Roo.data);
27755         this.setStore(this.store, true);
27756     }
27757     
27758     if ( this.footer && this.footer.xtype) {
27759            
27760          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27761         
27762         this.footer.dataSource = this.store;
27763         this.footer.container = fctr;
27764         this.footer = Roo.factory(this.footer, Roo);
27765         fctr.insertFirst(this.el);
27766         
27767         // this is a bit insane - as the paging toolbar seems to detach the el..
27768 //        dom.parentNode.parentNode.parentNode
27769          // they get detached?
27770     }
27771     
27772     
27773     Roo.View.superclass.constructor.call(this);
27774     
27775     
27776 };
27777
27778 Roo.extend(Roo.View, Roo.util.Observable, {
27779     
27780      /**
27781      * @cfg {Roo.data.Store} store Data store to load data from.
27782      */
27783     store : false,
27784     
27785     /**
27786      * @cfg {String|Roo.Element} el The container element.
27787      */
27788     el : '',
27789     
27790     /**
27791      * @cfg {String|Roo.Template} tpl The template used by this View 
27792      */
27793     tpl : false,
27794     /**
27795      * @cfg {String} dataName the named area of the template to use as the data area
27796      *                          Works with domtemplates roo-name="name"
27797      */
27798     dataName: false,
27799     /**
27800      * @cfg {String} selectedClass The css class to add to selected nodes
27801      */
27802     selectedClass : "x-view-selected",
27803      /**
27804      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27805      */
27806     emptyText : "",
27807     
27808     /**
27809      * @cfg {String} text to display on mask (default Loading)
27810      */
27811     mask : false,
27812     /**
27813      * @cfg {Boolean} multiSelect Allow multiple selection
27814      */
27815     multiSelect : false,
27816     /**
27817      * @cfg {Boolean} singleSelect Allow single selection
27818      */
27819     singleSelect:  false,
27820     
27821     /**
27822      * @cfg {Boolean} toggleSelect - selecting 
27823      */
27824     toggleSelect : false,
27825     
27826     /**
27827      * @cfg {Boolean} tickable - selecting 
27828      */
27829     tickable : false,
27830     
27831     /**
27832      * Returns the element this view is bound to.
27833      * @return {Roo.Element}
27834      */
27835     getEl : function(){
27836         return this.wrapEl;
27837     },
27838     
27839     
27840
27841     /**
27842      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27843      */
27844     refresh : function(){
27845         //Roo.log('refresh');
27846         var t = this.tpl;
27847         
27848         // if we are using something like 'domtemplate', then
27849         // the what gets used is:
27850         // t.applySubtemplate(NAME, data, wrapping data..)
27851         // the outer template then get' applied with
27852         //     the store 'extra data'
27853         // and the body get's added to the
27854         //      roo-name="data" node?
27855         //      <span class='roo-tpl-{name}'></span> ?????
27856         
27857         
27858         
27859         this.clearSelections();
27860         this.el.update("");
27861         var html = [];
27862         var records = this.store.getRange();
27863         if(records.length < 1) {
27864             
27865             // is this valid??  = should it render a template??
27866             
27867             this.el.update(this.emptyText);
27868             return;
27869         }
27870         var el = this.el;
27871         if (this.dataName) {
27872             this.el.update(t.apply(this.store.meta)); //????
27873             el = this.el.child('.roo-tpl-' + this.dataName);
27874         }
27875         
27876         for(var i = 0, len = records.length; i < len; i++){
27877             var data = this.prepareData(records[i].data, i, records[i]);
27878             this.fireEvent("preparedata", this, data, i, records[i]);
27879             
27880             var d = Roo.apply({}, data);
27881             
27882             if(this.tickable){
27883                 Roo.apply(d, {'roo-id' : Roo.id()});
27884                 
27885                 var _this = this;
27886             
27887                 Roo.each(this.parent.item, function(item){
27888                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27889                         return;
27890                     }
27891                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27892                 });
27893             }
27894             
27895             html[html.length] = Roo.util.Format.trim(
27896                 this.dataName ?
27897                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27898                     t.apply(d)
27899             );
27900         }
27901         
27902         
27903         
27904         el.update(html.join(""));
27905         this.nodes = el.dom.childNodes;
27906         this.updateIndexes(0);
27907     },
27908     
27909
27910     /**
27911      * Function to override to reformat the data that is sent to
27912      * the template for each node.
27913      * DEPRICATED - use the preparedata event handler.
27914      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27915      * a JSON object for an UpdateManager bound view).
27916      */
27917     prepareData : function(data, index, record)
27918     {
27919         this.fireEvent("preparedata", this, data, index, record);
27920         return data;
27921     },
27922
27923     onUpdate : function(ds, record){
27924         // Roo.log('on update');   
27925         this.clearSelections();
27926         var index = this.store.indexOf(record);
27927         var n = this.nodes[index];
27928         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27929         n.parentNode.removeChild(n);
27930         this.updateIndexes(index, index);
27931     },
27932
27933     
27934     
27935 // --------- FIXME     
27936     onAdd : function(ds, records, index)
27937     {
27938         //Roo.log(['on Add', ds, records, index] );        
27939         this.clearSelections();
27940         if(this.nodes.length == 0){
27941             this.refresh();
27942             return;
27943         }
27944         var n = this.nodes[index];
27945         for(var i = 0, len = records.length; i < len; i++){
27946             var d = this.prepareData(records[i].data, i, records[i]);
27947             if(n){
27948                 this.tpl.insertBefore(n, d);
27949             }else{
27950                 
27951                 this.tpl.append(this.el, d);
27952             }
27953         }
27954         this.updateIndexes(index);
27955     },
27956
27957     onRemove : function(ds, record, index){
27958        // Roo.log('onRemove');
27959         this.clearSelections();
27960         var el = this.dataName  ?
27961             this.el.child('.roo-tpl-' + this.dataName) :
27962             this.el; 
27963         
27964         el.dom.removeChild(this.nodes[index]);
27965         this.updateIndexes(index);
27966     },
27967
27968     /**
27969      * Refresh an individual node.
27970      * @param {Number} index
27971      */
27972     refreshNode : function(index){
27973         this.onUpdate(this.store, this.store.getAt(index));
27974     },
27975
27976     updateIndexes : function(startIndex, endIndex){
27977         var ns = this.nodes;
27978         startIndex = startIndex || 0;
27979         endIndex = endIndex || ns.length - 1;
27980         for(var i = startIndex; i <= endIndex; i++){
27981             ns[i].nodeIndex = i;
27982         }
27983     },
27984
27985     /**
27986      * Changes the data store this view uses and refresh the view.
27987      * @param {Store} store
27988      */
27989     setStore : function(store, initial){
27990         if(!initial && this.store){
27991             this.store.un("datachanged", this.refresh);
27992             this.store.un("add", this.onAdd);
27993             this.store.un("remove", this.onRemove);
27994             this.store.un("update", this.onUpdate);
27995             this.store.un("clear", this.refresh);
27996             this.store.un("beforeload", this.onBeforeLoad);
27997             this.store.un("load", this.onLoad);
27998             this.store.un("loadexception", this.onLoad);
27999         }
28000         if(store){
28001           
28002             store.on("datachanged", this.refresh, this);
28003             store.on("add", this.onAdd, this);
28004             store.on("remove", this.onRemove, this);
28005             store.on("update", this.onUpdate, this);
28006             store.on("clear", this.refresh, this);
28007             store.on("beforeload", this.onBeforeLoad, this);
28008             store.on("load", this.onLoad, this);
28009             store.on("loadexception", this.onLoad, this);
28010         }
28011         
28012         if(store){
28013             this.refresh();
28014         }
28015     },
28016     /**
28017      * onbeforeLoad - masks the loading area.
28018      *
28019      */
28020     onBeforeLoad : function(store,opts)
28021     {
28022          //Roo.log('onBeforeLoad');   
28023         if (!opts.add) {
28024             this.el.update("");
28025         }
28026         this.el.mask(this.mask ? this.mask : "Loading" ); 
28027     },
28028     onLoad : function ()
28029     {
28030         this.el.unmask();
28031     },
28032     
28033
28034     /**
28035      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28036      * @param {HTMLElement} node
28037      * @return {HTMLElement} The template node
28038      */
28039     findItemFromChild : function(node){
28040         var el = this.dataName  ?
28041             this.el.child('.roo-tpl-' + this.dataName,true) :
28042             this.el.dom; 
28043         
28044         if(!node || node.parentNode == el){
28045                     return node;
28046             }
28047             var p = node.parentNode;
28048             while(p && p != el){
28049             if(p.parentNode == el){
28050                 return p;
28051             }
28052             p = p.parentNode;
28053         }
28054             return null;
28055     },
28056
28057     /** @ignore */
28058     onClick : function(e){
28059         var item = this.findItemFromChild(e.getTarget());
28060         if(item){
28061             var index = this.indexOf(item);
28062             if(this.onItemClick(item, index, e) !== false){
28063                 this.fireEvent("click", this, index, item, e);
28064             }
28065         }else{
28066             this.clearSelections();
28067         }
28068     },
28069
28070     /** @ignore */
28071     onContextMenu : function(e){
28072         var item = this.findItemFromChild(e.getTarget());
28073         if(item){
28074             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28075         }
28076     },
28077
28078     /** @ignore */
28079     onDblClick : function(e){
28080         var item = this.findItemFromChild(e.getTarget());
28081         if(item){
28082             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28083         }
28084     },
28085
28086     onItemClick : function(item, index, e)
28087     {
28088         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28089             return false;
28090         }
28091         if (this.toggleSelect) {
28092             var m = this.isSelected(item) ? 'unselect' : 'select';
28093             //Roo.log(m);
28094             var _t = this;
28095             _t[m](item, true, false);
28096             return true;
28097         }
28098         if(this.multiSelect || this.singleSelect){
28099             if(this.multiSelect && e.shiftKey && this.lastSelection){
28100                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28101             }else{
28102                 this.select(item, this.multiSelect && e.ctrlKey);
28103                 this.lastSelection = item;
28104             }
28105             
28106             if(!this.tickable){
28107                 e.preventDefault();
28108             }
28109             
28110         }
28111         return true;
28112     },
28113
28114     /**
28115      * Get the number of selected nodes.
28116      * @return {Number}
28117      */
28118     getSelectionCount : function(){
28119         return this.selections.length;
28120     },
28121
28122     /**
28123      * Get the currently selected nodes.
28124      * @return {Array} An array of HTMLElements
28125      */
28126     getSelectedNodes : function(){
28127         return this.selections;
28128     },
28129
28130     /**
28131      * Get the indexes of the selected nodes.
28132      * @return {Array}
28133      */
28134     getSelectedIndexes : function(){
28135         var indexes = [], s = this.selections;
28136         for(var i = 0, len = s.length; i < len; i++){
28137             indexes.push(s[i].nodeIndex);
28138         }
28139         return indexes;
28140     },
28141
28142     /**
28143      * Clear all selections
28144      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28145      */
28146     clearSelections : function(suppressEvent){
28147         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28148             this.cmp.elements = this.selections;
28149             this.cmp.removeClass(this.selectedClass);
28150             this.selections = [];
28151             if(!suppressEvent){
28152                 this.fireEvent("selectionchange", this, this.selections);
28153             }
28154         }
28155     },
28156
28157     /**
28158      * Returns true if the passed node is selected
28159      * @param {HTMLElement/Number} node The node or node index
28160      * @return {Boolean}
28161      */
28162     isSelected : function(node){
28163         var s = this.selections;
28164         if(s.length < 1){
28165             return false;
28166         }
28167         node = this.getNode(node);
28168         return s.indexOf(node) !== -1;
28169     },
28170
28171     /**
28172      * Selects nodes.
28173      * @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
28174      * @param {Boolean} keepExisting (optional) true to keep existing selections
28175      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28176      */
28177     select : function(nodeInfo, keepExisting, suppressEvent){
28178         if(nodeInfo instanceof Array){
28179             if(!keepExisting){
28180                 this.clearSelections(true);
28181             }
28182             for(var i = 0, len = nodeInfo.length; i < len; i++){
28183                 this.select(nodeInfo[i], true, true);
28184             }
28185             return;
28186         } 
28187         var node = this.getNode(nodeInfo);
28188         if(!node || this.isSelected(node)){
28189             return; // already selected.
28190         }
28191         if(!keepExisting){
28192             this.clearSelections(true);
28193         }
28194         
28195         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28196             Roo.fly(node).addClass(this.selectedClass);
28197             this.selections.push(node);
28198             if(!suppressEvent){
28199                 this.fireEvent("selectionchange", this, this.selections);
28200             }
28201         }
28202         
28203         
28204     },
28205       /**
28206      * Unselects nodes.
28207      * @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
28208      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28209      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28210      */
28211     unselect : function(nodeInfo, keepExisting, suppressEvent)
28212     {
28213         if(nodeInfo instanceof Array){
28214             Roo.each(this.selections, function(s) {
28215                 this.unselect(s, nodeInfo);
28216             }, this);
28217             return;
28218         }
28219         var node = this.getNode(nodeInfo);
28220         if(!node || !this.isSelected(node)){
28221             //Roo.log("not selected");
28222             return; // not selected.
28223         }
28224         // fireevent???
28225         var ns = [];
28226         Roo.each(this.selections, function(s) {
28227             if (s == node ) {
28228                 Roo.fly(node).removeClass(this.selectedClass);
28229
28230                 return;
28231             }
28232             ns.push(s);
28233         },this);
28234         
28235         this.selections= ns;
28236         this.fireEvent("selectionchange", this, this.selections);
28237     },
28238
28239     /**
28240      * Gets a template node.
28241      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28242      * @return {HTMLElement} The node or null if it wasn't found
28243      */
28244     getNode : function(nodeInfo){
28245         if(typeof nodeInfo == "string"){
28246             return document.getElementById(nodeInfo);
28247         }else if(typeof nodeInfo == "number"){
28248             return this.nodes[nodeInfo];
28249         }
28250         return nodeInfo;
28251     },
28252
28253     /**
28254      * Gets a range template nodes.
28255      * @param {Number} startIndex
28256      * @param {Number} endIndex
28257      * @return {Array} An array of nodes
28258      */
28259     getNodes : function(start, end){
28260         var ns = this.nodes;
28261         start = start || 0;
28262         end = typeof end == "undefined" ? ns.length - 1 : end;
28263         var nodes = [];
28264         if(start <= end){
28265             for(var i = start; i <= end; i++){
28266                 nodes.push(ns[i]);
28267             }
28268         } else{
28269             for(var i = start; i >= end; i--){
28270                 nodes.push(ns[i]);
28271             }
28272         }
28273         return nodes;
28274     },
28275
28276     /**
28277      * Finds the index of the passed node
28278      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28279      * @return {Number} The index of the node or -1
28280      */
28281     indexOf : function(node){
28282         node = this.getNode(node);
28283         if(typeof node.nodeIndex == "number"){
28284             return node.nodeIndex;
28285         }
28286         var ns = this.nodes;
28287         for(var i = 0, len = ns.length; i < len; i++){
28288             if(ns[i] == node){
28289                 return i;
28290             }
28291         }
28292         return -1;
28293     }
28294 });
28295 /*
28296  * Based on:
28297  * Ext JS Library 1.1.1
28298  * Copyright(c) 2006-2007, Ext JS, LLC.
28299  *
28300  * Originally Released Under LGPL - original licence link has changed is not relivant.
28301  *
28302  * Fork - LGPL
28303  * <script type="text/javascript">
28304  */
28305
28306 /**
28307  * @class Roo.JsonView
28308  * @extends Roo.View
28309  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28310 <pre><code>
28311 var view = new Roo.JsonView({
28312     container: "my-element",
28313     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28314     multiSelect: true, 
28315     jsonRoot: "data" 
28316 });
28317
28318 // listen for node click?
28319 view.on("click", function(vw, index, node, e){
28320     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28321 });
28322
28323 // direct load of JSON data
28324 view.load("foobar.php");
28325
28326 // Example from my blog list
28327 var tpl = new Roo.Template(
28328     '&lt;div class="entry"&gt;' +
28329     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28330     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28331     "&lt;/div&gt;&lt;hr /&gt;"
28332 );
28333
28334 var moreView = new Roo.JsonView({
28335     container :  "entry-list", 
28336     template : tpl,
28337     jsonRoot: "posts"
28338 });
28339 moreView.on("beforerender", this.sortEntries, this);
28340 moreView.load({
28341     url: "/blog/get-posts.php",
28342     params: "allposts=true",
28343     text: "Loading Blog Entries..."
28344 });
28345 </code></pre>
28346
28347 * Note: old code is supported with arguments : (container, template, config)
28348
28349
28350  * @constructor
28351  * Create a new JsonView
28352  * 
28353  * @param {Object} config The config object
28354  * 
28355  */
28356 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28357     
28358     
28359     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28360
28361     var um = this.el.getUpdateManager();
28362     um.setRenderer(this);
28363     um.on("update", this.onLoad, this);
28364     um.on("failure", this.onLoadException, this);
28365
28366     /**
28367      * @event beforerender
28368      * Fires before rendering of the downloaded JSON data.
28369      * @param {Roo.JsonView} this
28370      * @param {Object} data The JSON data loaded
28371      */
28372     /**
28373      * @event load
28374      * Fires when data is loaded.
28375      * @param {Roo.JsonView} this
28376      * @param {Object} data The JSON data loaded
28377      * @param {Object} response The raw Connect response object
28378      */
28379     /**
28380      * @event loadexception
28381      * Fires when loading fails.
28382      * @param {Roo.JsonView} this
28383      * @param {Object} response The raw Connect response object
28384      */
28385     this.addEvents({
28386         'beforerender' : true,
28387         'load' : true,
28388         'loadexception' : true
28389     });
28390 };
28391 Roo.extend(Roo.JsonView, Roo.View, {
28392     /**
28393      * @type {String} The root property in the loaded JSON object that contains the data
28394      */
28395     jsonRoot : "",
28396
28397     /**
28398      * Refreshes the view.
28399      */
28400     refresh : function(){
28401         this.clearSelections();
28402         this.el.update("");
28403         var html = [];
28404         var o = this.jsonData;
28405         if(o && o.length > 0){
28406             for(var i = 0, len = o.length; i < len; i++){
28407                 var data = this.prepareData(o[i], i, o);
28408                 html[html.length] = this.tpl.apply(data);
28409             }
28410         }else{
28411             html.push(this.emptyText);
28412         }
28413         this.el.update(html.join(""));
28414         this.nodes = this.el.dom.childNodes;
28415         this.updateIndexes(0);
28416     },
28417
28418     /**
28419      * 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.
28420      * @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:
28421      <pre><code>
28422      view.load({
28423          url: "your-url.php",
28424          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28425          callback: yourFunction,
28426          scope: yourObject, //(optional scope)
28427          discardUrl: false,
28428          nocache: false,
28429          text: "Loading...",
28430          timeout: 30,
28431          scripts: false
28432      });
28433      </code></pre>
28434      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28435      * 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.
28436      * @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}
28437      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28438      * @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.
28439      */
28440     load : function(){
28441         var um = this.el.getUpdateManager();
28442         um.update.apply(um, arguments);
28443     },
28444
28445     // note - render is a standard framework call...
28446     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28447     render : function(el, response){
28448         
28449         this.clearSelections();
28450         this.el.update("");
28451         var o;
28452         try{
28453             if (response != '') {
28454                 o = Roo.util.JSON.decode(response.responseText);
28455                 if(this.jsonRoot){
28456                     
28457                     o = o[this.jsonRoot];
28458                 }
28459             }
28460         } catch(e){
28461         }
28462         /**
28463          * The current JSON data or null
28464          */
28465         this.jsonData = o;
28466         this.beforeRender();
28467         this.refresh();
28468     },
28469
28470 /**
28471  * Get the number of records in the current JSON dataset
28472  * @return {Number}
28473  */
28474     getCount : function(){
28475         return this.jsonData ? this.jsonData.length : 0;
28476     },
28477
28478 /**
28479  * Returns the JSON object for the specified node(s)
28480  * @param {HTMLElement/Array} node The node or an array of nodes
28481  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28482  * you get the JSON object for the node
28483  */
28484     getNodeData : function(node){
28485         if(node instanceof Array){
28486             var data = [];
28487             for(var i = 0, len = node.length; i < len; i++){
28488                 data.push(this.getNodeData(node[i]));
28489             }
28490             return data;
28491         }
28492         return this.jsonData[this.indexOf(node)] || null;
28493     },
28494
28495     beforeRender : function(){
28496         this.snapshot = this.jsonData;
28497         if(this.sortInfo){
28498             this.sort.apply(this, this.sortInfo);
28499         }
28500         this.fireEvent("beforerender", this, this.jsonData);
28501     },
28502
28503     onLoad : function(el, o){
28504         this.fireEvent("load", this, this.jsonData, o);
28505     },
28506
28507     onLoadException : function(el, o){
28508         this.fireEvent("loadexception", this, o);
28509     },
28510
28511 /**
28512  * Filter the data by a specific property.
28513  * @param {String} property A property on your JSON objects
28514  * @param {String/RegExp} value Either string that the property values
28515  * should start with, or a RegExp to test against the property
28516  */
28517     filter : function(property, value){
28518         if(this.jsonData){
28519             var data = [];
28520             var ss = this.snapshot;
28521             if(typeof value == "string"){
28522                 var vlen = value.length;
28523                 if(vlen == 0){
28524                     this.clearFilter();
28525                     return;
28526                 }
28527                 value = value.toLowerCase();
28528                 for(var i = 0, len = ss.length; i < len; i++){
28529                     var o = ss[i];
28530                     if(o[property].substr(0, vlen).toLowerCase() == value){
28531                         data.push(o);
28532                     }
28533                 }
28534             } else if(value.exec){ // regex?
28535                 for(var i = 0, len = ss.length; i < len; i++){
28536                     var o = ss[i];
28537                     if(value.test(o[property])){
28538                         data.push(o);
28539                     }
28540                 }
28541             } else{
28542                 return;
28543             }
28544             this.jsonData = data;
28545             this.refresh();
28546         }
28547     },
28548
28549 /**
28550  * Filter by a function. The passed function will be called with each
28551  * object in the current dataset. If the function returns true the value is kept,
28552  * otherwise it is filtered.
28553  * @param {Function} fn
28554  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28555  */
28556     filterBy : function(fn, scope){
28557         if(this.jsonData){
28558             var data = [];
28559             var ss = this.snapshot;
28560             for(var i = 0, len = ss.length; i < len; i++){
28561                 var o = ss[i];
28562                 if(fn.call(scope || this, o)){
28563                     data.push(o);
28564                 }
28565             }
28566             this.jsonData = data;
28567             this.refresh();
28568         }
28569     },
28570
28571 /**
28572  * Clears the current filter.
28573  */
28574     clearFilter : function(){
28575         if(this.snapshot && this.jsonData != this.snapshot){
28576             this.jsonData = this.snapshot;
28577             this.refresh();
28578         }
28579     },
28580
28581
28582 /**
28583  * Sorts the data for this view and refreshes it.
28584  * @param {String} property A property on your JSON objects to sort on
28585  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28586  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28587  */
28588     sort : function(property, dir, sortType){
28589         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28590         if(this.jsonData){
28591             var p = property;
28592             var dsc = dir && dir.toLowerCase() == "desc";
28593             var f = function(o1, o2){
28594                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28595                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28596                 ;
28597                 if(v1 < v2){
28598                     return dsc ? +1 : -1;
28599                 } else if(v1 > v2){
28600                     return dsc ? -1 : +1;
28601                 } else{
28602                     return 0;
28603                 }
28604             };
28605             this.jsonData.sort(f);
28606             this.refresh();
28607             if(this.jsonData != this.snapshot){
28608                 this.snapshot.sort(f);
28609             }
28610         }
28611     }
28612 });/*
28613  * Based on:
28614  * Ext JS Library 1.1.1
28615  * Copyright(c) 2006-2007, Ext JS, LLC.
28616  *
28617  * Originally Released Under LGPL - original licence link has changed is not relivant.
28618  *
28619  * Fork - LGPL
28620  * <script type="text/javascript">
28621  */
28622  
28623
28624 /**
28625  * @class Roo.ColorPalette
28626  * @extends Roo.Component
28627  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28628  * Here's an example of typical usage:
28629  * <pre><code>
28630 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28631 cp.render('my-div');
28632
28633 cp.on('select', function(palette, selColor){
28634     // do something with selColor
28635 });
28636 </code></pre>
28637  * @constructor
28638  * Create a new ColorPalette
28639  * @param {Object} config The config object
28640  */
28641 Roo.ColorPalette = function(config){
28642     Roo.ColorPalette.superclass.constructor.call(this, config);
28643     this.addEvents({
28644         /**
28645              * @event select
28646              * Fires when a color is selected
28647              * @param {ColorPalette} this
28648              * @param {String} color The 6-digit color hex code (without the # symbol)
28649              */
28650         select: true
28651     });
28652
28653     if(this.handler){
28654         this.on("select", this.handler, this.scope, true);
28655     }
28656 };
28657 Roo.extend(Roo.ColorPalette, Roo.Component, {
28658     /**
28659      * @cfg {String} itemCls
28660      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28661      */
28662     itemCls : "x-color-palette",
28663     /**
28664      * @cfg {String} value
28665      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28666      * the hex codes are case-sensitive.
28667      */
28668     value : null,
28669     clickEvent:'click',
28670     // private
28671     ctype: "Roo.ColorPalette",
28672
28673     /**
28674      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28675      */
28676     allowReselect : false,
28677
28678     /**
28679      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28680      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28681      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28682      * of colors with the width setting until the box is symmetrical.</p>
28683      * <p>You can override individual colors if needed:</p>
28684      * <pre><code>
28685 var cp = new Roo.ColorPalette();
28686 cp.colors[0] = "FF0000";  // change the first box to red
28687 </code></pre>
28688
28689 Or you can provide a custom array of your own for complete control:
28690 <pre><code>
28691 var cp = new Roo.ColorPalette();
28692 cp.colors = ["000000", "993300", "333300"];
28693 </code></pre>
28694      * @type Array
28695      */
28696     colors : [
28697         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28698         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28699         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28700         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28701         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28702     ],
28703
28704     // private
28705     onRender : function(container, position){
28706         var t = new Roo.MasterTemplate(
28707             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28708         );
28709         var c = this.colors;
28710         for(var i = 0, len = c.length; i < len; i++){
28711             t.add([c[i]]);
28712         }
28713         var el = document.createElement("div");
28714         el.className = this.itemCls;
28715         t.overwrite(el);
28716         container.dom.insertBefore(el, position);
28717         this.el = Roo.get(el);
28718         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28719         if(this.clickEvent != 'click'){
28720             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28721         }
28722     },
28723
28724     // private
28725     afterRender : function(){
28726         Roo.ColorPalette.superclass.afterRender.call(this);
28727         if(this.value){
28728             var s = this.value;
28729             this.value = null;
28730             this.select(s);
28731         }
28732     },
28733
28734     // private
28735     handleClick : function(e, t){
28736         e.preventDefault();
28737         if(!this.disabled){
28738             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28739             this.select(c.toUpperCase());
28740         }
28741     },
28742
28743     /**
28744      * Selects the specified color in the palette (fires the select event)
28745      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28746      */
28747     select : function(color){
28748         color = color.replace("#", "");
28749         if(color != this.value || this.allowReselect){
28750             var el = this.el;
28751             if(this.value){
28752                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28753             }
28754             el.child("a.color-"+color).addClass("x-color-palette-sel");
28755             this.value = color;
28756             this.fireEvent("select", this, color);
28757         }
28758     }
28759 });/*
28760  * Based on:
28761  * Ext JS Library 1.1.1
28762  * Copyright(c) 2006-2007, Ext JS, LLC.
28763  *
28764  * Originally Released Under LGPL - original licence link has changed is not relivant.
28765  *
28766  * Fork - LGPL
28767  * <script type="text/javascript">
28768  */
28769  
28770 /**
28771  * @class Roo.DatePicker
28772  * @extends Roo.Component
28773  * Simple date picker class.
28774  * @constructor
28775  * Create a new DatePicker
28776  * @param {Object} config The config object
28777  */
28778 Roo.DatePicker = function(config){
28779     Roo.DatePicker.superclass.constructor.call(this, config);
28780
28781     this.value = config && config.value ?
28782                  config.value.clearTime() : new Date().clearTime();
28783
28784     this.addEvents({
28785         /**
28786              * @event select
28787              * Fires when a date is selected
28788              * @param {DatePicker} this
28789              * @param {Date} date The selected date
28790              */
28791         'select': true,
28792         /**
28793              * @event monthchange
28794              * Fires when the displayed month changes 
28795              * @param {DatePicker} this
28796              * @param {Date} date The selected month
28797              */
28798         'monthchange': true
28799     });
28800
28801     if(this.handler){
28802         this.on("select", this.handler,  this.scope || this);
28803     }
28804     // build the disabledDatesRE
28805     if(!this.disabledDatesRE && this.disabledDates){
28806         var dd = this.disabledDates;
28807         var re = "(?:";
28808         for(var i = 0; i < dd.length; i++){
28809             re += dd[i];
28810             if(i != dd.length-1) {
28811                 re += "|";
28812             }
28813         }
28814         this.disabledDatesRE = new RegExp(re + ")");
28815     }
28816 };
28817
28818 Roo.extend(Roo.DatePicker, Roo.Component, {
28819     /**
28820      * @cfg {String} todayText
28821      * The text to display on the button that selects the current date (defaults to "Today")
28822      */
28823     todayText : "Today",
28824     /**
28825      * @cfg {String} okText
28826      * The text to display on the ok button
28827      */
28828     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28829     /**
28830      * @cfg {String} cancelText
28831      * The text to display on the cancel button
28832      */
28833     cancelText : "Cancel",
28834     /**
28835      * @cfg {String} todayTip
28836      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28837      */
28838     todayTip : "{0} (Spacebar)",
28839     /**
28840      * @cfg {Date} minDate
28841      * Minimum allowable date (JavaScript date object, defaults to null)
28842      */
28843     minDate : null,
28844     /**
28845      * @cfg {Date} maxDate
28846      * Maximum allowable date (JavaScript date object, defaults to null)
28847      */
28848     maxDate : null,
28849     /**
28850      * @cfg {String} minText
28851      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28852      */
28853     minText : "This date is before the minimum date",
28854     /**
28855      * @cfg {String} maxText
28856      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28857      */
28858     maxText : "This date is after the maximum date",
28859     /**
28860      * @cfg {String} format
28861      * The default date format string which can be overriden for localization support.  The format must be
28862      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28863      */
28864     format : "m/d/y",
28865     /**
28866      * @cfg {Array} disabledDays
28867      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28868      */
28869     disabledDays : null,
28870     /**
28871      * @cfg {String} disabledDaysText
28872      * The tooltip to display when the date falls on a disabled day (defaults to "")
28873      */
28874     disabledDaysText : "",
28875     /**
28876      * @cfg {RegExp} disabledDatesRE
28877      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28878      */
28879     disabledDatesRE : null,
28880     /**
28881      * @cfg {String} disabledDatesText
28882      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28883      */
28884     disabledDatesText : "",
28885     /**
28886      * @cfg {Boolean} constrainToViewport
28887      * True to constrain the date picker to the viewport (defaults to true)
28888      */
28889     constrainToViewport : true,
28890     /**
28891      * @cfg {Array} monthNames
28892      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28893      */
28894     monthNames : Date.monthNames,
28895     /**
28896      * @cfg {Array} dayNames
28897      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28898      */
28899     dayNames : Date.dayNames,
28900     /**
28901      * @cfg {String} nextText
28902      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28903      */
28904     nextText: 'Next Month (Control+Right)',
28905     /**
28906      * @cfg {String} prevText
28907      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28908      */
28909     prevText: 'Previous Month (Control+Left)',
28910     /**
28911      * @cfg {String} monthYearText
28912      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28913      */
28914     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28915     /**
28916      * @cfg {Number} startDay
28917      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28918      */
28919     startDay : 0,
28920     /**
28921      * @cfg {Bool} showClear
28922      * Show a clear button (usefull for date form elements that can be blank.)
28923      */
28924     
28925     showClear: false,
28926     
28927     /**
28928      * Sets the value of the date field
28929      * @param {Date} value The date to set
28930      */
28931     setValue : function(value){
28932         var old = this.value;
28933         
28934         if (typeof(value) == 'string') {
28935          
28936             value = Date.parseDate(value, this.format);
28937         }
28938         if (!value) {
28939             value = new Date();
28940         }
28941         
28942         this.value = value.clearTime(true);
28943         if(this.el){
28944             this.update(this.value);
28945         }
28946     },
28947
28948     /**
28949      * Gets the current selected value of the date field
28950      * @return {Date} The selected date
28951      */
28952     getValue : function(){
28953         return this.value;
28954     },
28955
28956     // private
28957     focus : function(){
28958         if(this.el){
28959             this.update(this.activeDate);
28960         }
28961     },
28962
28963     // privateval
28964     onRender : function(container, position){
28965         
28966         var m = [
28967              '<table cellspacing="0">',
28968                 '<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>',
28969                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28970         var dn = this.dayNames;
28971         for(var i = 0; i < 7; i++){
28972             var d = this.startDay+i;
28973             if(d > 6){
28974                 d = d-7;
28975             }
28976             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28977         }
28978         m[m.length] = "</tr></thead><tbody><tr>";
28979         for(var i = 0; i < 42; i++) {
28980             if(i % 7 == 0 && i != 0){
28981                 m[m.length] = "</tr><tr>";
28982             }
28983             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28984         }
28985         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28986             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28987
28988         var el = document.createElement("div");
28989         el.className = "x-date-picker";
28990         el.innerHTML = m.join("");
28991
28992         container.dom.insertBefore(el, position);
28993
28994         this.el = Roo.get(el);
28995         this.eventEl = Roo.get(el.firstChild);
28996
28997         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28998             handler: this.showPrevMonth,
28999             scope: this,
29000             preventDefault:true,
29001             stopDefault:true
29002         });
29003
29004         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29005             handler: this.showNextMonth,
29006             scope: this,
29007             preventDefault:true,
29008             stopDefault:true
29009         });
29010
29011         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29012
29013         this.monthPicker = this.el.down('div.x-date-mp');
29014         this.monthPicker.enableDisplayMode('block');
29015         
29016         var kn = new Roo.KeyNav(this.eventEl, {
29017             "left" : function(e){
29018                 e.ctrlKey ?
29019                     this.showPrevMonth() :
29020                     this.update(this.activeDate.add("d", -1));
29021             },
29022
29023             "right" : function(e){
29024                 e.ctrlKey ?
29025                     this.showNextMonth() :
29026                     this.update(this.activeDate.add("d", 1));
29027             },
29028
29029             "up" : function(e){
29030                 e.ctrlKey ?
29031                     this.showNextYear() :
29032                     this.update(this.activeDate.add("d", -7));
29033             },
29034
29035             "down" : function(e){
29036                 e.ctrlKey ?
29037                     this.showPrevYear() :
29038                     this.update(this.activeDate.add("d", 7));
29039             },
29040
29041             "pageUp" : function(e){
29042                 this.showNextMonth();
29043             },
29044
29045             "pageDown" : function(e){
29046                 this.showPrevMonth();
29047             },
29048
29049             "enter" : function(e){
29050                 e.stopPropagation();
29051                 return true;
29052             },
29053
29054             scope : this
29055         });
29056
29057         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29058
29059         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29060
29061         this.el.unselectable();
29062         
29063         this.cells = this.el.select("table.x-date-inner tbody td");
29064         this.textNodes = this.el.query("table.x-date-inner tbody span");
29065
29066         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29067             text: "&#160;",
29068             tooltip: this.monthYearText
29069         });
29070
29071         this.mbtn.on('click', this.showMonthPicker, this);
29072         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29073
29074
29075         var today = (new Date()).dateFormat(this.format);
29076         
29077         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29078         if (this.showClear) {
29079             baseTb.add( new Roo.Toolbar.Fill());
29080         }
29081         baseTb.add({
29082             text: String.format(this.todayText, today),
29083             tooltip: String.format(this.todayTip, today),
29084             handler: this.selectToday,
29085             scope: this
29086         });
29087         
29088         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29089             
29090         //});
29091         if (this.showClear) {
29092             
29093             baseTb.add( new Roo.Toolbar.Fill());
29094             baseTb.add({
29095                 text: '&#160;',
29096                 cls: 'x-btn-icon x-btn-clear',
29097                 handler: function() {
29098                     //this.value = '';
29099                     this.fireEvent("select", this, '');
29100                 },
29101                 scope: this
29102             });
29103         }
29104         
29105         
29106         if(Roo.isIE){
29107             this.el.repaint();
29108         }
29109         this.update(this.value);
29110     },
29111
29112     createMonthPicker : function(){
29113         if(!this.monthPicker.dom.firstChild){
29114             var buf = ['<table border="0" cellspacing="0">'];
29115             for(var i = 0; i < 6; i++){
29116                 buf.push(
29117                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29118                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29119                     i == 0 ?
29120                     '<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>' :
29121                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29122                 );
29123             }
29124             buf.push(
29125                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29126                     this.okText,
29127                     '</button><button type="button" class="x-date-mp-cancel">',
29128                     this.cancelText,
29129                     '</button></td></tr>',
29130                 '</table>'
29131             );
29132             this.monthPicker.update(buf.join(''));
29133             this.monthPicker.on('click', this.onMonthClick, this);
29134             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29135
29136             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29137             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29138
29139             this.mpMonths.each(function(m, a, i){
29140                 i += 1;
29141                 if((i%2) == 0){
29142                     m.dom.xmonth = 5 + Math.round(i * .5);
29143                 }else{
29144                     m.dom.xmonth = Math.round((i-1) * .5);
29145                 }
29146             });
29147         }
29148     },
29149
29150     showMonthPicker : function(){
29151         this.createMonthPicker();
29152         var size = this.el.getSize();
29153         this.monthPicker.setSize(size);
29154         this.monthPicker.child('table').setSize(size);
29155
29156         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29157         this.updateMPMonth(this.mpSelMonth);
29158         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29159         this.updateMPYear(this.mpSelYear);
29160
29161         this.monthPicker.slideIn('t', {duration:.2});
29162     },
29163
29164     updateMPYear : function(y){
29165         this.mpyear = y;
29166         var ys = this.mpYears.elements;
29167         for(var i = 1; i <= 10; i++){
29168             var td = ys[i-1], y2;
29169             if((i%2) == 0){
29170                 y2 = y + Math.round(i * .5);
29171                 td.firstChild.innerHTML = y2;
29172                 td.xyear = y2;
29173             }else{
29174                 y2 = y - (5-Math.round(i * .5));
29175                 td.firstChild.innerHTML = y2;
29176                 td.xyear = y2;
29177             }
29178             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29179         }
29180     },
29181
29182     updateMPMonth : function(sm){
29183         this.mpMonths.each(function(m, a, i){
29184             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29185         });
29186     },
29187
29188     selectMPMonth: function(m){
29189         
29190     },
29191
29192     onMonthClick : function(e, t){
29193         e.stopEvent();
29194         var el = new Roo.Element(t), pn;
29195         if(el.is('button.x-date-mp-cancel')){
29196             this.hideMonthPicker();
29197         }
29198         else if(el.is('button.x-date-mp-ok')){
29199             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29200             this.hideMonthPicker();
29201         }
29202         else if(pn = el.up('td.x-date-mp-month', 2)){
29203             this.mpMonths.removeClass('x-date-mp-sel');
29204             pn.addClass('x-date-mp-sel');
29205             this.mpSelMonth = pn.dom.xmonth;
29206         }
29207         else if(pn = el.up('td.x-date-mp-year', 2)){
29208             this.mpYears.removeClass('x-date-mp-sel');
29209             pn.addClass('x-date-mp-sel');
29210             this.mpSelYear = pn.dom.xyear;
29211         }
29212         else if(el.is('a.x-date-mp-prev')){
29213             this.updateMPYear(this.mpyear-10);
29214         }
29215         else if(el.is('a.x-date-mp-next')){
29216             this.updateMPYear(this.mpyear+10);
29217         }
29218     },
29219
29220     onMonthDblClick : function(e, t){
29221         e.stopEvent();
29222         var el = new Roo.Element(t), pn;
29223         if(pn = el.up('td.x-date-mp-month', 2)){
29224             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29225             this.hideMonthPicker();
29226         }
29227         else if(pn = el.up('td.x-date-mp-year', 2)){
29228             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29229             this.hideMonthPicker();
29230         }
29231     },
29232
29233     hideMonthPicker : function(disableAnim){
29234         if(this.monthPicker){
29235             if(disableAnim === true){
29236                 this.monthPicker.hide();
29237             }else{
29238                 this.monthPicker.slideOut('t', {duration:.2});
29239             }
29240         }
29241     },
29242
29243     // private
29244     showPrevMonth : function(e){
29245         this.update(this.activeDate.add("mo", -1));
29246     },
29247
29248     // private
29249     showNextMonth : function(e){
29250         this.update(this.activeDate.add("mo", 1));
29251     },
29252
29253     // private
29254     showPrevYear : function(){
29255         this.update(this.activeDate.add("y", -1));
29256     },
29257
29258     // private
29259     showNextYear : function(){
29260         this.update(this.activeDate.add("y", 1));
29261     },
29262
29263     // private
29264     handleMouseWheel : function(e){
29265         var delta = e.getWheelDelta();
29266         if(delta > 0){
29267             this.showPrevMonth();
29268             e.stopEvent();
29269         } else if(delta < 0){
29270             this.showNextMonth();
29271             e.stopEvent();
29272         }
29273     },
29274
29275     // private
29276     handleDateClick : function(e, t){
29277         e.stopEvent();
29278         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29279             this.setValue(new Date(t.dateValue));
29280             this.fireEvent("select", this, this.value);
29281         }
29282     },
29283
29284     // private
29285     selectToday : function(){
29286         this.setValue(new Date().clearTime());
29287         this.fireEvent("select", this, this.value);
29288     },
29289
29290     // private
29291     update : function(date)
29292     {
29293         var vd = this.activeDate;
29294         this.activeDate = date;
29295         if(vd && this.el){
29296             var t = date.getTime();
29297             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29298                 this.cells.removeClass("x-date-selected");
29299                 this.cells.each(function(c){
29300                    if(c.dom.firstChild.dateValue == t){
29301                        c.addClass("x-date-selected");
29302                        setTimeout(function(){
29303                             try{c.dom.firstChild.focus();}catch(e){}
29304                        }, 50);
29305                        return false;
29306                    }
29307                 });
29308                 return;
29309             }
29310         }
29311         
29312         var days = date.getDaysInMonth();
29313         var firstOfMonth = date.getFirstDateOfMonth();
29314         var startingPos = firstOfMonth.getDay()-this.startDay;
29315
29316         if(startingPos <= this.startDay){
29317             startingPos += 7;
29318         }
29319
29320         var pm = date.add("mo", -1);
29321         var prevStart = pm.getDaysInMonth()-startingPos;
29322
29323         var cells = this.cells.elements;
29324         var textEls = this.textNodes;
29325         days += startingPos;
29326
29327         // convert everything to numbers so it's fast
29328         var day = 86400000;
29329         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29330         var today = new Date().clearTime().getTime();
29331         var sel = date.clearTime().getTime();
29332         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29333         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29334         var ddMatch = this.disabledDatesRE;
29335         var ddText = this.disabledDatesText;
29336         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29337         var ddaysText = this.disabledDaysText;
29338         var format = this.format;
29339
29340         var setCellClass = function(cal, cell){
29341             cell.title = "";
29342             var t = d.getTime();
29343             cell.firstChild.dateValue = t;
29344             if(t == today){
29345                 cell.className += " x-date-today";
29346                 cell.title = cal.todayText;
29347             }
29348             if(t == sel){
29349                 cell.className += " x-date-selected";
29350                 setTimeout(function(){
29351                     try{cell.firstChild.focus();}catch(e){}
29352                 }, 50);
29353             }
29354             // disabling
29355             if(t < min) {
29356                 cell.className = " x-date-disabled";
29357                 cell.title = cal.minText;
29358                 return;
29359             }
29360             if(t > max) {
29361                 cell.className = " x-date-disabled";
29362                 cell.title = cal.maxText;
29363                 return;
29364             }
29365             if(ddays){
29366                 if(ddays.indexOf(d.getDay()) != -1){
29367                     cell.title = ddaysText;
29368                     cell.className = " x-date-disabled";
29369                 }
29370             }
29371             if(ddMatch && format){
29372                 var fvalue = d.dateFormat(format);
29373                 if(ddMatch.test(fvalue)){
29374                     cell.title = ddText.replace("%0", fvalue);
29375                     cell.className = " x-date-disabled";
29376                 }
29377             }
29378         };
29379
29380         var i = 0;
29381         for(; i < startingPos; i++) {
29382             textEls[i].innerHTML = (++prevStart);
29383             d.setDate(d.getDate()+1);
29384             cells[i].className = "x-date-prevday";
29385             setCellClass(this, cells[i]);
29386         }
29387         for(; i < days; i++){
29388             intDay = i - startingPos + 1;
29389             textEls[i].innerHTML = (intDay);
29390             d.setDate(d.getDate()+1);
29391             cells[i].className = "x-date-active";
29392             setCellClass(this, cells[i]);
29393         }
29394         var extraDays = 0;
29395         for(; i < 42; i++) {
29396              textEls[i].innerHTML = (++extraDays);
29397              d.setDate(d.getDate()+1);
29398              cells[i].className = "x-date-nextday";
29399              setCellClass(this, cells[i]);
29400         }
29401
29402         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29403         this.fireEvent('monthchange', this, date);
29404         
29405         if(!this.internalRender){
29406             var main = this.el.dom.firstChild;
29407             var w = main.offsetWidth;
29408             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29409             Roo.fly(main).setWidth(w);
29410             this.internalRender = true;
29411             // opera does not respect the auto grow header center column
29412             // then, after it gets a width opera refuses to recalculate
29413             // without a second pass
29414             if(Roo.isOpera && !this.secondPass){
29415                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29416                 this.secondPass = true;
29417                 this.update.defer(10, this, [date]);
29418             }
29419         }
29420         
29421         
29422     }
29423 });        /*
29424  * Based on:
29425  * Ext JS Library 1.1.1
29426  * Copyright(c) 2006-2007, Ext JS, LLC.
29427  *
29428  * Originally Released Under LGPL - original licence link has changed is not relivant.
29429  *
29430  * Fork - LGPL
29431  * <script type="text/javascript">
29432  */
29433 /**
29434  * @class Roo.TabPanel
29435  * @extends Roo.util.Observable
29436  * A lightweight tab container.
29437  * <br><br>
29438  * Usage:
29439  * <pre><code>
29440 // basic tabs 1, built from existing content
29441 var tabs = new Roo.TabPanel("tabs1");
29442 tabs.addTab("script", "View Script");
29443 tabs.addTab("markup", "View Markup");
29444 tabs.activate("script");
29445
29446 // more advanced tabs, built from javascript
29447 var jtabs = new Roo.TabPanel("jtabs");
29448 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29449
29450 // set up the UpdateManager
29451 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29452 var updater = tab2.getUpdateManager();
29453 updater.setDefaultUrl("ajax1.htm");
29454 tab2.on('activate', updater.refresh, updater, true);
29455
29456 // Use setUrl for Ajax loading
29457 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29458 tab3.setUrl("ajax2.htm", null, true);
29459
29460 // Disabled tab
29461 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29462 tab4.disable();
29463
29464 jtabs.activate("jtabs-1");
29465  * </code></pre>
29466  * @constructor
29467  * Create a new TabPanel.
29468  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29469  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29470  */
29471 Roo.TabPanel = function(container, config){
29472     /**
29473     * The container element for this TabPanel.
29474     * @type Roo.Element
29475     */
29476     this.el = Roo.get(container, true);
29477     if(config){
29478         if(typeof config == "boolean"){
29479             this.tabPosition = config ? "bottom" : "top";
29480         }else{
29481             Roo.apply(this, config);
29482         }
29483     }
29484     if(this.tabPosition == "bottom"){
29485         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29486         this.el.addClass("x-tabs-bottom");
29487     }
29488     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29489     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29490     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29491     if(Roo.isIE){
29492         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29493     }
29494     if(this.tabPosition != "bottom"){
29495         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29496          * @type Roo.Element
29497          */
29498         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29499         this.el.addClass("x-tabs-top");
29500     }
29501     this.items = [];
29502
29503     this.bodyEl.setStyle("position", "relative");
29504
29505     this.active = null;
29506     this.activateDelegate = this.activate.createDelegate(this);
29507
29508     this.addEvents({
29509         /**
29510          * @event tabchange
29511          * Fires when the active tab changes
29512          * @param {Roo.TabPanel} this
29513          * @param {Roo.TabPanelItem} activePanel The new active tab
29514          */
29515         "tabchange": true,
29516         /**
29517          * @event beforetabchange
29518          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29519          * @param {Roo.TabPanel} this
29520          * @param {Object} e Set cancel to true on this object to cancel the tab change
29521          * @param {Roo.TabPanelItem} tab The tab being changed to
29522          */
29523         "beforetabchange" : true
29524     });
29525
29526     Roo.EventManager.onWindowResize(this.onResize, this);
29527     this.cpad = this.el.getPadding("lr");
29528     this.hiddenCount = 0;
29529
29530
29531     // toolbar on the tabbar support...
29532     if (this.toolbar) {
29533         var tcfg = this.toolbar;
29534         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29535         this.toolbar = new Roo.Toolbar(tcfg);
29536         if (Roo.isSafari) {
29537             var tbl = tcfg.container.child('table', true);
29538             tbl.setAttribute('width', '100%');
29539         }
29540         
29541     }
29542    
29543
29544
29545     Roo.TabPanel.superclass.constructor.call(this);
29546 };
29547
29548 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29549     /*
29550      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29551      */
29552     tabPosition : "top",
29553     /*
29554      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29555      */
29556     currentTabWidth : 0,
29557     /*
29558      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29559      */
29560     minTabWidth : 40,
29561     /*
29562      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29563      */
29564     maxTabWidth : 250,
29565     /*
29566      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29567      */
29568     preferredTabWidth : 175,
29569     /*
29570      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29571      */
29572     resizeTabs : false,
29573     /*
29574      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29575      */
29576     monitorResize : true,
29577     /*
29578      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29579      */
29580     toolbar : false,
29581
29582     /**
29583      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29584      * @param {String} id The id of the div to use <b>or create</b>
29585      * @param {String} text The text for the tab
29586      * @param {String} content (optional) Content to put in the TabPanelItem body
29587      * @param {Boolean} closable (optional) True to create a close icon on the tab
29588      * @return {Roo.TabPanelItem} The created TabPanelItem
29589      */
29590     addTab : function(id, text, content, closable){
29591         var item = new Roo.TabPanelItem(this, id, text, closable);
29592         this.addTabItem(item);
29593         if(content){
29594             item.setContent(content);
29595         }
29596         return item;
29597     },
29598
29599     /**
29600      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29601      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29602      * @return {Roo.TabPanelItem}
29603      */
29604     getTab : function(id){
29605         return this.items[id];
29606     },
29607
29608     /**
29609      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29610      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29611      */
29612     hideTab : function(id){
29613         var t = this.items[id];
29614         if(!t.isHidden()){
29615            t.setHidden(true);
29616            this.hiddenCount++;
29617            this.autoSizeTabs();
29618         }
29619     },
29620
29621     /**
29622      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29623      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29624      */
29625     unhideTab : function(id){
29626         var t = this.items[id];
29627         if(t.isHidden()){
29628            t.setHidden(false);
29629            this.hiddenCount--;
29630            this.autoSizeTabs();
29631         }
29632     },
29633
29634     /**
29635      * Adds an existing {@link Roo.TabPanelItem}.
29636      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29637      */
29638     addTabItem : function(item){
29639         this.items[item.id] = item;
29640         this.items.push(item);
29641         if(this.resizeTabs){
29642            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29643            this.autoSizeTabs();
29644         }else{
29645             item.autoSize();
29646         }
29647     },
29648
29649     /**
29650      * Removes a {@link Roo.TabPanelItem}.
29651      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29652      */
29653     removeTab : function(id){
29654         var items = this.items;
29655         var tab = items[id];
29656         if(!tab) { return; }
29657         var index = items.indexOf(tab);
29658         if(this.active == tab && items.length > 1){
29659             var newTab = this.getNextAvailable(index);
29660             if(newTab) {
29661                 newTab.activate();
29662             }
29663         }
29664         this.stripEl.dom.removeChild(tab.pnode.dom);
29665         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29666             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29667         }
29668         items.splice(index, 1);
29669         delete this.items[tab.id];
29670         tab.fireEvent("close", tab);
29671         tab.purgeListeners();
29672         this.autoSizeTabs();
29673     },
29674
29675     getNextAvailable : function(start){
29676         var items = this.items;
29677         var index = start;
29678         // look for a next tab that will slide over to
29679         // replace the one being removed
29680         while(index < items.length){
29681             var item = items[++index];
29682             if(item && !item.isHidden()){
29683                 return item;
29684             }
29685         }
29686         // if one isn't found select the previous tab (on the left)
29687         index = start;
29688         while(index >= 0){
29689             var item = items[--index];
29690             if(item && !item.isHidden()){
29691                 return item;
29692             }
29693         }
29694         return null;
29695     },
29696
29697     /**
29698      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29699      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29700      */
29701     disableTab : function(id){
29702         var tab = this.items[id];
29703         if(tab && this.active != tab){
29704             tab.disable();
29705         }
29706     },
29707
29708     /**
29709      * Enables a {@link Roo.TabPanelItem} that is disabled.
29710      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29711      */
29712     enableTab : function(id){
29713         var tab = this.items[id];
29714         tab.enable();
29715     },
29716
29717     /**
29718      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29719      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29720      * @return {Roo.TabPanelItem} The TabPanelItem.
29721      */
29722     activate : function(id){
29723         var tab = this.items[id];
29724         if(!tab){
29725             return null;
29726         }
29727         if(tab == this.active || tab.disabled){
29728             return tab;
29729         }
29730         var e = {};
29731         this.fireEvent("beforetabchange", this, e, tab);
29732         if(e.cancel !== true && !tab.disabled){
29733             if(this.active){
29734                 this.active.hide();
29735             }
29736             this.active = this.items[id];
29737             this.active.show();
29738             this.fireEvent("tabchange", this, this.active);
29739         }
29740         return tab;
29741     },
29742
29743     /**
29744      * Gets the active {@link Roo.TabPanelItem}.
29745      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29746      */
29747     getActiveTab : function(){
29748         return this.active;
29749     },
29750
29751     /**
29752      * Updates the tab body element to fit the height of the container element
29753      * for overflow scrolling
29754      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29755      */
29756     syncHeight : function(targetHeight){
29757         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29758         var bm = this.bodyEl.getMargins();
29759         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29760         this.bodyEl.setHeight(newHeight);
29761         return newHeight;
29762     },
29763
29764     onResize : function(){
29765         if(this.monitorResize){
29766             this.autoSizeTabs();
29767         }
29768     },
29769
29770     /**
29771      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29772      */
29773     beginUpdate : function(){
29774         this.updating = true;
29775     },
29776
29777     /**
29778      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29779      */
29780     endUpdate : function(){
29781         this.updating = false;
29782         this.autoSizeTabs();
29783     },
29784
29785     /**
29786      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29787      */
29788     autoSizeTabs : function(){
29789         var count = this.items.length;
29790         var vcount = count - this.hiddenCount;
29791         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29792             return;
29793         }
29794         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29795         var availWidth = Math.floor(w / vcount);
29796         var b = this.stripBody;
29797         if(b.getWidth() > w){
29798             var tabs = this.items;
29799             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29800             if(availWidth < this.minTabWidth){
29801                 /*if(!this.sleft){    // incomplete scrolling code
29802                     this.createScrollButtons();
29803                 }
29804                 this.showScroll();
29805                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29806             }
29807         }else{
29808             if(this.currentTabWidth < this.preferredTabWidth){
29809                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29810             }
29811         }
29812     },
29813
29814     /**
29815      * Returns the number of tabs in this TabPanel.
29816      * @return {Number}
29817      */
29818      getCount : function(){
29819          return this.items.length;
29820      },
29821
29822     /**
29823      * Resizes all the tabs to the passed width
29824      * @param {Number} The new width
29825      */
29826     setTabWidth : function(width){
29827         this.currentTabWidth = width;
29828         for(var i = 0, len = this.items.length; i < len; i++) {
29829                 if(!this.items[i].isHidden()) {
29830                 this.items[i].setWidth(width);
29831             }
29832         }
29833     },
29834
29835     /**
29836      * Destroys this TabPanel
29837      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29838      */
29839     destroy : function(removeEl){
29840         Roo.EventManager.removeResizeListener(this.onResize, this);
29841         for(var i = 0, len = this.items.length; i < len; i++){
29842             this.items[i].purgeListeners();
29843         }
29844         if(removeEl === true){
29845             this.el.update("");
29846             this.el.remove();
29847         }
29848     }
29849 });
29850
29851 /**
29852  * @class Roo.TabPanelItem
29853  * @extends Roo.util.Observable
29854  * Represents an individual item (tab plus body) in a TabPanel.
29855  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29856  * @param {String} id The id of this TabPanelItem
29857  * @param {String} text The text for the tab of this TabPanelItem
29858  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29859  */
29860 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29861     /**
29862      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29863      * @type Roo.TabPanel
29864      */
29865     this.tabPanel = tabPanel;
29866     /**
29867      * The id for this TabPanelItem
29868      * @type String
29869      */
29870     this.id = id;
29871     /** @private */
29872     this.disabled = false;
29873     /** @private */
29874     this.text = text;
29875     /** @private */
29876     this.loaded = false;
29877     this.closable = closable;
29878
29879     /**
29880      * The body element for this TabPanelItem.
29881      * @type Roo.Element
29882      */
29883     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29884     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29885     this.bodyEl.setStyle("display", "block");
29886     this.bodyEl.setStyle("zoom", "1");
29887     this.hideAction();
29888
29889     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29890     /** @private */
29891     this.el = Roo.get(els.el, true);
29892     this.inner = Roo.get(els.inner, true);
29893     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29894     this.pnode = Roo.get(els.el.parentNode, true);
29895     this.el.on("mousedown", this.onTabMouseDown, this);
29896     this.el.on("click", this.onTabClick, this);
29897     /** @private */
29898     if(closable){
29899         var c = Roo.get(els.close, true);
29900         c.dom.title = this.closeText;
29901         c.addClassOnOver("close-over");
29902         c.on("click", this.closeClick, this);
29903      }
29904
29905     this.addEvents({
29906          /**
29907          * @event activate
29908          * Fires when this tab becomes the active tab.
29909          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29910          * @param {Roo.TabPanelItem} this
29911          */
29912         "activate": true,
29913         /**
29914          * @event beforeclose
29915          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29916          * @param {Roo.TabPanelItem} this
29917          * @param {Object} e Set cancel to true on this object to cancel the close.
29918          */
29919         "beforeclose": true,
29920         /**
29921          * @event close
29922          * Fires when this tab is closed.
29923          * @param {Roo.TabPanelItem} this
29924          */
29925          "close": true,
29926         /**
29927          * @event deactivate
29928          * Fires when this tab is no longer the active tab.
29929          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29930          * @param {Roo.TabPanelItem} this
29931          */
29932          "deactivate" : true
29933     });
29934     this.hidden = false;
29935
29936     Roo.TabPanelItem.superclass.constructor.call(this);
29937 };
29938
29939 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29940     purgeListeners : function(){
29941        Roo.util.Observable.prototype.purgeListeners.call(this);
29942        this.el.removeAllListeners();
29943     },
29944     /**
29945      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29946      */
29947     show : function(){
29948         this.pnode.addClass("on");
29949         this.showAction();
29950         if(Roo.isOpera){
29951             this.tabPanel.stripWrap.repaint();
29952         }
29953         this.fireEvent("activate", this.tabPanel, this);
29954     },
29955
29956     /**
29957      * Returns true if this tab is the active tab.
29958      * @return {Boolean}
29959      */
29960     isActive : function(){
29961         return this.tabPanel.getActiveTab() == this;
29962     },
29963
29964     /**
29965      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29966      */
29967     hide : function(){
29968         this.pnode.removeClass("on");
29969         this.hideAction();
29970         this.fireEvent("deactivate", this.tabPanel, this);
29971     },
29972
29973     hideAction : function(){
29974         this.bodyEl.hide();
29975         this.bodyEl.setStyle("position", "absolute");
29976         this.bodyEl.setLeft("-20000px");
29977         this.bodyEl.setTop("-20000px");
29978     },
29979
29980     showAction : function(){
29981         this.bodyEl.setStyle("position", "relative");
29982         this.bodyEl.setTop("");
29983         this.bodyEl.setLeft("");
29984         this.bodyEl.show();
29985     },
29986
29987     /**
29988      * Set the tooltip for the tab.
29989      * @param {String} tooltip The tab's tooltip
29990      */
29991     setTooltip : function(text){
29992         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29993             this.textEl.dom.qtip = text;
29994             this.textEl.dom.removeAttribute('title');
29995         }else{
29996             this.textEl.dom.title = text;
29997         }
29998     },
29999
30000     onTabClick : function(e){
30001         e.preventDefault();
30002         this.tabPanel.activate(this.id);
30003     },
30004
30005     onTabMouseDown : function(e){
30006         e.preventDefault();
30007         this.tabPanel.activate(this.id);
30008     },
30009
30010     getWidth : function(){
30011         return this.inner.getWidth();
30012     },
30013
30014     setWidth : function(width){
30015         var iwidth = width - this.pnode.getPadding("lr");
30016         this.inner.setWidth(iwidth);
30017         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30018         this.pnode.setWidth(width);
30019     },
30020
30021     /**
30022      * Show or hide the tab
30023      * @param {Boolean} hidden True to hide or false to show.
30024      */
30025     setHidden : function(hidden){
30026         this.hidden = hidden;
30027         this.pnode.setStyle("display", hidden ? "none" : "");
30028     },
30029
30030     /**
30031      * Returns true if this tab is "hidden"
30032      * @return {Boolean}
30033      */
30034     isHidden : function(){
30035         return this.hidden;
30036     },
30037
30038     /**
30039      * Returns the text for this tab
30040      * @return {String}
30041      */
30042     getText : function(){
30043         return this.text;
30044     },
30045
30046     autoSize : function(){
30047         //this.el.beginMeasure();
30048         this.textEl.setWidth(1);
30049         /*
30050          *  #2804 [new] Tabs in Roojs
30051          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30052          */
30053         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30054         //this.el.endMeasure();
30055     },
30056
30057     /**
30058      * Sets the text for the tab (Note: this also sets the tooltip text)
30059      * @param {String} text The tab's text and tooltip
30060      */
30061     setText : function(text){
30062         this.text = text;
30063         this.textEl.update(text);
30064         this.setTooltip(text);
30065         if(!this.tabPanel.resizeTabs){
30066             this.autoSize();
30067         }
30068     },
30069     /**
30070      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30071      */
30072     activate : function(){
30073         this.tabPanel.activate(this.id);
30074     },
30075
30076     /**
30077      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30078      */
30079     disable : function(){
30080         if(this.tabPanel.active != this){
30081             this.disabled = true;
30082             this.pnode.addClass("disabled");
30083         }
30084     },
30085
30086     /**
30087      * Enables this TabPanelItem if it was previously disabled.
30088      */
30089     enable : function(){
30090         this.disabled = false;
30091         this.pnode.removeClass("disabled");
30092     },
30093
30094     /**
30095      * Sets the content for this TabPanelItem.
30096      * @param {String} content The content
30097      * @param {Boolean} loadScripts true to look for and load scripts
30098      */
30099     setContent : function(content, loadScripts){
30100         this.bodyEl.update(content, loadScripts);
30101     },
30102
30103     /**
30104      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30105      * @return {Roo.UpdateManager} The UpdateManager
30106      */
30107     getUpdateManager : function(){
30108         return this.bodyEl.getUpdateManager();
30109     },
30110
30111     /**
30112      * Set a URL to be used to load the content for this TabPanelItem.
30113      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30114      * @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)
30115      * @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)
30116      * @return {Roo.UpdateManager} The UpdateManager
30117      */
30118     setUrl : function(url, params, loadOnce){
30119         if(this.refreshDelegate){
30120             this.un('activate', this.refreshDelegate);
30121         }
30122         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30123         this.on("activate", this.refreshDelegate);
30124         return this.bodyEl.getUpdateManager();
30125     },
30126
30127     /** @private */
30128     _handleRefresh : function(url, params, loadOnce){
30129         if(!loadOnce || !this.loaded){
30130             var updater = this.bodyEl.getUpdateManager();
30131             updater.update(url, params, this._setLoaded.createDelegate(this));
30132         }
30133     },
30134
30135     /**
30136      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30137      *   Will fail silently if the setUrl method has not been called.
30138      *   This does not activate the panel, just updates its content.
30139      */
30140     refresh : function(){
30141         if(this.refreshDelegate){
30142            this.loaded = false;
30143            this.refreshDelegate();
30144         }
30145     },
30146
30147     /** @private */
30148     _setLoaded : function(){
30149         this.loaded = true;
30150     },
30151
30152     /** @private */
30153     closeClick : function(e){
30154         var o = {};
30155         e.stopEvent();
30156         this.fireEvent("beforeclose", this, o);
30157         if(o.cancel !== true){
30158             this.tabPanel.removeTab(this.id);
30159         }
30160     },
30161     /**
30162      * The text displayed in the tooltip for the close icon.
30163      * @type String
30164      */
30165     closeText : "Close this tab"
30166 });
30167
30168 /** @private */
30169 Roo.TabPanel.prototype.createStrip = function(container){
30170     var strip = document.createElement("div");
30171     strip.className = "x-tabs-wrap";
30172     container.appendChild(strip);
30173     return strip;
30174 };
30175 /** @private */
30176 Roo.TabPanel.prototype.createStripList = function(strip){
30177     // div wrapper for retard IE
30178     // returns the "tr" element.
30179     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30180         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30181         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30182     return strip.firstChild.firstChild.firstChild.firstChild;
30183 };
30184 /** @private */
30185 Roo.TabPanel.prototype.createBody = function(container){
30186     var body = document.createElement("div");
30187     Roo.id(body, "tab-body");
30188     Roo.fly(body).addClass("x-tabs-body");
30189     container.appendChild(body);
30190     return body;
30191 };
30192 /** @private */
30193 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30194     var body = Roo.getDom(id);
30195     if(!body){
30196         body = document.createElement("div");
30197         body.id = id;
30198     }
30199     Roo.fly(body).addClass("x-tabs-item-body");
30200     bodyEl.insertBefore(body, bodyEl.firstChild);
30201     return body;
30202 };
30203 /** @private */
30204 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30205     var td = document.createElement("td");
30206     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30207     //stripEl.appendChild(td);
30208     if(closable){
30209         td.className = "x-tabs-closable";
30210         if(!this.closeTpl){
30211             this.closeTpl = new Roo.Template(
30212                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30213                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30214                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30215             );
30216         }
30217         var el = this.closeTpl.overwrite(td, {"text": text});
30218         var close = el.getElementsByTagName("div")[0];
30219         var inner = el.getElementsByTagName("em")[0];
30220         return {"el": el, "close": close, "inner": inner};
30221     } else {
30222         if(!this.tabTpl){
30223             this.tabTpl = new Roo.Template(
30224                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30225                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30226             );
30227         }
30228         var el = this.tabTpl.overwrite(td, {"text": text});
30229         var inner = el.getElementsByTagName("em")[0];
30230         return {"el": el, "inner": inner};
30231     }
30232 };/*
30233  * Based on:
30234  * Ext JS Library 1.1.1
30235  * Copyright(c) 2006-2007, Ext JS, LLC.
30236  *
30237  * Originally Released Under LGPL - original licence link has changed is not relivant.
30238  *
30239  * Fork - LGPL
30240  * <script type="text/javascript">
30241  */
30242
30243 /**
30244  * @class Roo.Button
30245  * @extends Roo.util.Observable
30246  * Simple Button class
30247  * @cfg {String} text The button text
30248  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30249  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30250  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30251  * @cfg {Object} scope The scope of the handler
30252  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30253  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30254  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30255  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30256  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30257  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30258    applies if enableToggle = true)
30259  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30260  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30261   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30262  * @constructor
30263  * Create a new button
30264  * @param {Object} config The config object
30265  */
30266 Roo.Button = function(renderTo, config)
30267 {
30268     if (!config) {
30269         config = renderTo;
30270         renderTo = config.renderTo || false;
30271     }
30272     
30273     Roo.apply(this, config);
30274     this.addEvents({
30275         /**
30276              * @event click
30277              * Fires when this button is clicked
30278              * @param {Button} this
30279              * @param {EventObject} e The click event
30280              */
30281             "click" : true,
30282         /**
30283              * @event toggle
30284              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30285              * @param {Button} this
30286              * @param {Boolean} pressed
30287              */
30288             "toggle" : true,
30289         /**
30290              * @event mouseover
30291              * Fires when the mouse hovers over the button
30292              * @param {Button} this
30293              * @param {Event} e The event object
30294              */
30295         'mouseover' : true,
30296         /**
30297              * @event mouseout
30298              * Fires when the mouse exits the button
30299              * @param {Button} this
30300              * @param {Event} e The event object
30301              */
30302         'mouseout': true,
30303          /**
30304              * @event render
30305              * Fires when the button is rendered
30306              * @param {Button} this
30307              */
30308         'render': true
30309     });
30310     if(this.menu){
30311         this.menu = Roo.menu.MenuMgr.get(this.menu);
30312     }
30313     // register listeners first!!  - so render can be captured..
30314     Roo.util.Observable.call(this);
30315     if(renderTo){
30316         this.render(renderTo);
30317     }
30318     
30319   
30320 };
30321
30322 Roo.extend(Roo.Button, Roo.util.Observable, {
30323     /**
30324      * 
30325      */
30326     
30327     /**
30328      * Read-only. True if this button is hidden
30329      * @type Boolean
30330      */
30331     hidden : false,
30332     /**
30333      * Read-only. True if this button is disabled
30334      * @type Boolean
30335      */
30336     disabled : false,
30337     /**
30338      * Read-only. True if this button is pressed (only if enableToggle = true)
30339      * @type Boolean
30340      */
30341     pressed : false,
30342
30343     /**
30344      * @cfg {Number} tabIndex 
30345      * The DOM tabIndex for this button (defaults to undefined)
30346      */
30347     tabIndex : undefined,
30348
30349     /**
30350      * @cfg {Boolean} enableToggle
30351      * True to enable pressed/not pressed toggling (defaults to false)
30352      */
30353     enableToggle: false,
30354     /**
30355      * @cfg {Roo.menu.Menu} menu
30356      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30357      */
30358     menu : undefined,
30359     /**
30360      * @cfg {String} menuAlign
30361      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30362      */
30363     menuAlign : "tl-bl?",
30364
30365     /**
30366      * @cfg {String} iconCls
30367      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30368      */
30369     iconCls : undefined,
30370     /**
30371      * @cfg {String} type
30372      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30373      */
30374     type : 'button',
30375
30376     // private
30377     menuClassTarget: 'tr',
30378
30379     /**
30380      * @cfg {String} clickEvent
30381      * The type of event to map to the button's event handler (defaults to 'click')
30382      */
30383     clickEvent : 'click',
30384
30385     /**
30386      * @cfg {Boolean} handleMouseEvents
30387      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30388      */
30389     handleMouseEvents : true,
30390
30391     /**
30392      * @cfg {String} tooltipType
30393      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30394      */
30395     tooltipType : 'qtip',
30396
30397     /**
30398      * @cfg {String} cls
30399      * A CSS class to apply to the button's main element.
30400      */
30401     
30402     /**
30403      * @cfg {Roo.Template} template (Optional)
30404      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30405      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30406      * require code modifications if required elements (e.g. a button) aren't present.
30407      */
30408
30409     // private
30410     render : function(renderTo){
30411         var btn;
30412         if(this.hideParent){
30413             this.parentEl = Roo.get(renderTo);
30414         }
30415         if(!this.dhconfig){
30416             if(!this.template){
30417                 if(!Roo.Button.buttonTemplate){
30418                     // hideous table template
30419                     Roo.Button.buttonTemplate = new Roo.Template(
30420                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30421                         '<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>',
30422                         "</tr></tbody></table>");
30423                 }
30424                 this.template = Roo.Button.buttonTemplate;
30425             }
30426             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30427             var btnEl = btn.child("button:first");
30428             btnEl.on('focus', this.onFocus, this);
30429             btnEl.on('blur', this.onBlur, this);
30430             if(this.cls){
30431                 btn.addClass(this.cls);
30432             }
30433             if(this.icon){
30434                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30435             }
30436             if(this.iconCls){
30437                 btnEl.addClass(this.iconCls);
30438                 if(!this.cls){
30439                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30440                 }
30441             }
30442             if(this.tabIndex !== undefined){
30443                 btnEl.dom.tabIndex = this.tabIndex;
30444             }
30445             if(this.tooltip){
30446                 if(typeof this.tooltip == 'object'){
30447                     Roo.QuickTips.tips(Roo.apply({
30448                           target: btnEl.id
30449                     }, this.tooltip));
30450                 } else {
30451                     btnEl.dom[this.tooltipType] = this.tooltip;
30452                 }
30453             }
30454         }else{
30455             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30456         }
30457         this.el = btn;
30458         if(this.id){
30459             this.el.dom.id = this.el.id = this.id;
30460         }
30461         if(this.menu){
30462             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30463             this.menu.on("show", this.onMenuShow, this);
30464             this.menu.on("hide", this.onMenuHide, this);
30465         }
30466         btn.addClass("x-btn");
30467         if(Roo.isIE && !Roo.isIE7){
30468             this.autoWidth.defer(1, this);
30469         }else{
30470             this.autoWidth();
30471         }
30472         if(this.handleMouseEvents){
30473             btn.on("mouseover", this.onMouseOver, this);
30474             btn.on("mouseout", this.onMouseOut, this);
30475             btn.on("mousedown", this.onMouseDown, this);
30476         }
30477         btn.on(this.clickEvent, this.onClick, this);
30478         //btn.on("mouseup", this.onMouseUp, this);
30479         if(this.hidden){
30480             this.hide();
30481         }
30482         if(this.disabled){
30483             this.disable();
30484         }
30485         Roo.ButtonToggleMgr.register(this);
30486         if(this.pressed){
30487             this.el.addClass("x-btn-pressed");
30488         }
30489         if(this.repeat){
30490             var repeater = new Roo.util.ClickRepeater(btn,
30491                 typeof this.repeat == "object" ? this.repeat : {}
30492             );
30493             repeater.on("click", this.onClick,  this);
30494         }
30495         
30496         this.fireEvent('render', this);
30497         
30498     },
30499     /**
30500      * Returns the button's underlying element
30501      * @return {Roo.Element} The element
30502      */
30503     getEl : function(){
30504         return this.el;  
30505     },
30506     
30507     /**
30508      * Destroys this Button and removes any listeners.
30509      */
30510     destroy : function(){
30511         Roo.ButtonToggleMgr.unregister(this);
30512         this.el.removeAllListeners();
30513         this.purgeListeners();
30514         this.el.remove();
30515     },
30516
30517     // private
30518     autoWidth : function(){
30519         if(this.el){
30520             this.el.setWidth("auto");
30521             if(Roo.isIE7 && Roo.isStrict){
30522                 var ib = this.el.child('button');
30523                 if(ib && ib.getWidth() > 20){
30524                     ib.clip();
30525                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30526                 }
30527             }
30528             if(this.minWidth){
30529                 if(this.hidden){
30530                     this.el.beginMeasure();
30531                 }
30532                 if(this.el.getWidth() < this.minWidth){
30533                     this.el.setWidth(this.minWidth);
30534                 }
30535                 if(this.hidden){
30536                     this.el.endMeasure();
30537                 }
30538             }
30539         }
30540     },
30541
30542     /**
30543      * Assigns this button's click handler
30544      * @param {Function} handler The function to call when the button is clicked
30545      * @param {Object} scope (optional) Scope for the function passed in
30546      */
30547     setHandler : function(handler, scope){
30548         this.handler = handler;
30549         this.scope = scope;  
30550     },
30551     
30552     /**
30553      * Sets this button's text
30554      * @param {String} text The button text
30555      */
30556     setText : function(text){
30557         this.text = text;
30558         if(this.el){
30559             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30560         }
30561         this.autoWidth();
30562     },
30563     
30564     /**
30565      * Gets the text for this button
30566      * @return {String} The button text
30567      */
30568     getText : function(){
30569         return this.text;  
30570     },
30571     
30572     /**
30573      * Show this button
30574      */
30575     show: function(){
30576         this.hidden = false;
30577         if(this.el){
30578             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30579         }
30580     },
30581     
30582     /**
30583      * Hide this button
30584      */
30585     hide: function(){
30586         this.hidden = true;
30587         if(this.el){
30588             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30589         }
30590     },
30591     
30592     /**
30593      * Convenience function for boolean show/hide
30594      * @param {Boolean} visible True to show, false to hide
30595      */
30596     setVisible: function(visible){
30597         if(visible) {
30598             this.show();
30599         }else{
30600             this.hide();
30601         }
30602     },
30603     
30604     /**
30605      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30606      * @param {Boolean} state (optional) Force a particular state
30607      */
30608     toggle : function(state){
30609         state = state === undefined ? !this.pressed : state;
30610         if(state != this.pressed){
30611             if(state){
30612                 this.el.addClass("x-btn-pressed");
30613                 this.pressed = true;
30614                 this.fireEvent("toggle", this, true);
30615             }else{
30616                 this.el.removeClass("x-btn-pressed");
30617                 this.pressed = false;
30618                 this.fireEvent("toggle", this, false);
30619             }
30620             if(this.toggleHandler){
30621                 this.toggleHandler.call(this.scope || this, this, state);
30622             }
30623         }
30624     },
30625     
30626     /**
30627      * Focus the button
30628      */
30629     focus : function(){
30630         this.el.child('button:first').focus();
30631     },
30632     
30633     /**
30634      * Disable this button
30635      */
30636     disable : function(){
30637         if(this.el){
30638             this.el.addClass("x-btn-disabled");
30639         }
30640         this.disabled = true;
30641     },
30642     
30643     /**
30644      * Enable this button
30645      */
30646     enable : function(){
30647         if(this.el){
30648             this.el.removeClass("x-btn-disabled");
30649         }
30650         this.disabled = false;
30651     },
30652
30653     /**
30654      * Convenience function for boolean enable/disable
30655      * @param {Boolean} enabled True to enable, false to disable
30656      */
30657     setDisabled : function(v){
30658         this[v !== true ? "enable" : "disable"]();
30659     },
30660
30661     // private
30662     onClick : function(e)
30663     {
30664         if(e){
30665             e.preventDefault();
30666         }
30667         if(e.button != 0){
30668             return;
30669         }
30670         if(!this.disabled){
30671             if(this.enableToggle){
30672                 this.toggle();
30673             }
30674             if(this.menu && !this.menu.isVisible()){
30675                 this.menu.show(this.el, this.menuAlign);
30676             }
30677             this.fireEvent("click", this, e);
30678             if(this.handler){
30679                 this.el.removeClass("x-btn-over");
30680                 this.handler.call(this.scope || this, this, e);
30681             }
30682         }
30683     },
30684     // private
30685     onMouseOver : function(e){
30686         if(!this.disabled){
30687             this.el.addClass("x-btn-over");
30688             this.fireEvent('mouseover', this, e);
30689         }
30690     },
30691     // private
30692     onMouseOut : function(e){
30693         if(!e.within(this.el,  true)){
30694             this.el.removeClass("x-btn-over");
30695             this.fireEvent('mouseout', this, e);
30696         }
30697     },
30698     // private
30699     onFocus : function(e){
30700         if(!this.disabled){
30701             this.el.addClass("x-btn-focus");
30702         }
30703     },
30704     // private
30705     onBlur : function(e){
30706         this.el.removeClass("x-btn-focus");
30707     },
30708     // private
30709     onMouseDown : function(e){
30710         if(!this.disabled && e.button == 0){
30711             this.el.addClass("x-btn-click");
30712             Roo.get(document).on('mouseup', this.onMouseUp, this);
30713         }
30714     },
30715     // private
30716     onMouseUp : function(e){
30717         if(e.button == 0){
30718             this.el.removeClass("x-btn-click");
30719             Roo.get(document).un('mouseup', this.onMouseUp, this);
30720         }
30721     },
30722     // private
30723     onMenuShow : function(e){
30724         this.el.addClass("x-btn-menu-active");
30725     },
30726     // private
30727     onMenuHide : function(e){
30728         this.el.removeClass("x-btn-menu-active");
30729     }   
30730 });
30731
30732 // Private utility class used by Button
30733 Roo.ButtonToggleMgr = function(){
30734    var groups = {};
30735    
30736    function toggleGroup(btn, state){
30737        if(state){
30738            var g = groups[btn.toggleGroup];
30739            for(var i = 0, l = g.length; i < l; i++){
30740                if(g[i] != btn){
30741                    g[i].toggle(false);
30742                }
30743            }
30744        }
30745    }
30746    
30747    return {
30748        register : function(btn){
30749            if(!btn.toggleGroup){
30750                return;
30751            }
30752            var g = groups[btn.toggleGroup];
30753            if(!g){
30754                g = groups[btn.toggleGroup] = [];
30755            }
30756            g.push(btn);
30757            btn.on("toggle", toggleGroup);
30758        },
30759        
30760        unregister : function(btn){
30761            if(!btn.toggleGroup){
30762                return;
30763            }
30764            var g = groups[btn.toggleGroup];
30765            if(g){
30766                g.remove(btn);
30767                btn.un("toggle", toggleGroup);
30768            }
30769        }
30770    };
30771 }();/*
30772  * Based on:
30773  * Ext JS Library 1.1.1
30774  * Copyright(c) 2006-2007, Ext JS, LLC.
30775  *
30776  * Originally Released Under LGPL - original licence link has changed is not relivant.
30777  *
30778  * Fork - LGPL
30779  * <script type="text/javascript">
30780  */
30781  
30782 /**
30783  * @class Roo.SplitButton
30784  * @extends Roo.Button
30785  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30786  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30787  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30788  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30789  * @cfg {String} arrowTooltip The title attribute of the arrow
30790  * @constructor
30791  * Create a new menu button
30792  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30793  * @param {Object} config The config object
30794  */
30795 Roo.SplitButton = function(renderTo, config){
30796     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30797     /**
30798      * @event arrowclick
30799      * Fires when this button's arrow is clicked
30800      * @param {SplitButton} this
30801      * @param {EventObject} e The click event
30802      */
30803     this.addEvents({"arrowclick":true});
30804 };
30805
30806 Roo.extend(Roo.SplitButton, Roo.Button, {
30807     render : function(renderTo){
30808         // this is one sweet looking template!
30809         var tpl = new Roo.Template(
30810             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30811             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30812             '<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>',
30813             "</tbody></table></td><td>",
30814             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30815             '<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>',
30816             "</tbody></table></td></tr></table>"
30817         );
30818         var btn = tpl.append(renderTo, [this.text, this.type], true);
30819         var btnEl = btn.child("button");
30820         if(this.cls){
30821             btn.addClass(this.cls);
30822         }
30823         if(this.icon){
30824             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30825         }
30826         if(this.iconCls){
30827             btnEl.addClass(this.iconCls);
30828             if(!this.cls){
30829                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30830             }
30831         }
30832         this.el = btn;
30833         if(this.handleMouseEvents){
30834             btn.on("mouseover", this.onMouseOver, this);
30835             btn.on("mouseout", this.onMouseOut, this);
30836             btn.on("mousedown", this.onMouseDown, this);
30837             btn.on("mouseup", this.onMouseUp, this);
30838         }
30839         btn.on(this.clickEvent, this.onClick, this);
30840         if(this.tooltip){
30841             if(typeof this.tooltip == 'object'){
30842                 Roo.QuickTips.tips(Roo.apply({
30843                       target: btnEl.id
30844                 }, this.tooltip));
30845             } else {
30846                 btnEl.dom[this.tooltipType] = this.tooltip;
30847             }
30848         }
30849         if(this.arrowTooltip){
30850             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30851         }
30852         if(this.hidden){
30853             this.hide();
30854         }
30855         if(this.disabled){
30856             this.disable();
30857         }
30858         if(this.pressed){
30859             this.el.addClass("x-btn-pressed");
30860         }
30861         if(Roo.isIE && !Roo.isIE7){
30862             this.autoWidth.defer(1, this);
30863         }else{
30864             this.autoWidth();
30865         }
30866         if(this.menu){
30867             this.menu.on("show", this.onMenuShow, this);
30868             this.menu.on("hide", this.onMenuHide, this);
30869         }
30870         this.fireEvent('render', this);
30871     },
30872
30873     // private
30874     autoWidth : function(){
30875         if(this.el){
30876             var tbl = this.el.child("table:first");
30877             var tbl2 = this.el.child("table:last");
30878             this.el.setWidth("auto");
30879             tbl.setWidth("auto");
30880             if(Roo.isIE7 && Roo.isStrict){
30881                 var ib = this.el.child('button:first');
30882                 if(ib && ib.getWidth() > 20){
30883                     ib.clip();
30884                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30885                 }
30886             }
30887             if(this.minWidth){
30888                 if(this.hidden){
30889                     this.el.beginMeasure();
30890                 }
30891                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30892                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30893                 }
30894                 if(this.hidden){
30895                     this.el.endMeasure();
30896                 }
30897             }
30898             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30899         } 
30900     },
30901     /**
30902      * Sets this button's click handler
30903      * @param {Function} handler The function to call when the button is clicked
30904      * @param {Object} scope (optional) Scope for the function passed above
30905      */
30906     setHandler : function(handler, scope){
30907         this.handler = handler;
30908         this.scope = scope;  
30909     },
30910     
30911     /**
30912      * Sets this button's arrow click handler
30913      * @param {Function} handler The function to call when the arrow is clicked
30914      * @param {Object} scope (optional) Scope for the function passed above
30915      */
30916     setArrowHandler : function(handler, scope){
30917         this.arrowHandler = handler;
30918         this.scope = scope;  
30919     },
30920     
30921     /**
30922      * Focus the button
30923      */
30924     focus : function(){
30925         if(this.el){
30926             this.el.child("button:first").focus();
30927         }
30928     },
30929
30930     // private
30931     onClick : function(e){
30932         e.preventDefault();
30933         if(!this.disabled){
30934             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30935                 if(this.menu && !this.menu.isVisible()){
30936                     this.menu.show(this.el, this.menuAlign);
30937                 }
30938                 this.fireEvent("arrowclick", this, e);
30939                 if(this.arrowHandler){
30940                     this.arrowHandler.call(this.scope || this, this, e);
30941                 }
30942             }else{
30943                 this.fireEvent("click", this, e);
30944                 if(this.handler){
30945                     this.handler.call(this.scope || this, this, e);
30946                 }
30947             }
30948         }
30949     },
30950     // private
30951     onMouseDown : function(e){
30952         if(!this.disabled){
30953             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30954         }
30955     },
30956     // private
30957     onMouseUp : function(e){
30958         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30959     }   
30960 });
30961
30962
30963 // backwards compat
30964 Roo.MenuButton = Roo.SplitButton;/*
30965  * Based on:
30966  * Ext JS Library 1.1.1
30967  * Copyright(c) 2006-2007, Ext JS, LLC.
30968  *
30969  * Originally Released Under LGPL - original licence link has changed is not relivant.
30970  *
30971  * Fork - LGPL
30972  * <script type="text/javascript">
30973  */
30974
30975 /**
30976  * @class Roo.Toolbar
30977  * @children   Roo.Toolbar.Item Roo.form.Field
30978  * Basic Toolbar class.
30979  * @constructor
30980  * Creates a new Toolbar
30981  * @param {Object} container The config object
30982  */ 
30983 Roo.Toolbar = function(container, buttons, config)
30984 {
30985     /// old consturctor format still supported..
30986     if(container instanceof Array){ // omit the container for later rendering
30987         buttons = container;
30988         config = buttons;
30989         container = null;
30990     }
30991     if (typeof(container) == 'object' && container.xtype) {
30992         config = container;
30993         container = config.container;
30994         buttons = config.buttons || []; // not really - use items!!
30995     }
30996     var xitems = [];
30997     if (config && config.items) {
30998         xitems = config.items;
30999         delete config.items;
31000     }
31001     Roo.apply(this, config);
31002     this.buttons = buttons;
31003     
31004     if(container){
31005         this.render(container);
31006     }
31007     this.xitems = xitems;
31008     Roo.each(xitems, function(b) {
31009         this.add(b);
31010     }, this);
31011     
31012 };
31013
31014 Roo.Toolbar.prototype = {
31015     /**
31016      * @cfg {Array} items
31017      * array of button configs or elements to add (will be converted to a MixedCollection)
31018      */
31019     items: false,
31020     /**
31021      * @cfg {String/HTMLElement/Element} container
31022      * The id or element that will contain the toolbar
31023      */
31024     // private
31025     render : function(ct){
31026         this.el = Roo.get(ct);
31027         if(this.cls){
31028             this.el.addClass(this.cls);
31029         }
31030         // using a table allows for vertical alignment
31031         // 100% width is needed by Safari...
31032         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31033         this.tr = this.el.child("tr", true);
31034         var autoId = 0;
31035         this.items = new Roo.util.MixedCollection(false, function(o){
31036             return o.id || ("item" + (++autoId));
31037         });
31038         if(this.buttons){
31039             this.add.apply(this, this.buttons);
31040             delete this.buttons;
31041         }
31042     },
31043
31044     /**
31045      * Adds element(s) to the toolbar -- this function takes a variable number of 
31046      * arguments of mixed type and adds them to the toolbar.
31047      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31048      * <ul>
31049      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31050      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31051      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31052      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31053      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31054      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31055      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31056      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31057      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31058      * </ul>
31059      * @param {Mixed} arg2
31060      * @param {Mixed} etc.
31061      */
31062     add : function(){
31063         var a = arguments, l = a.length;
31064         for(var i = 0; i < l; i++){
31065             this._add(a[i]);
31066         }
31067     },
31068     // private..
31069     _add : function(el) {
31070         
31071         if (el.xtype) {
31072             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31073         }
31074         
31075         if (el.applyTo){ // some kind of form field
31076             return this.addField(el);
31077         } 
31078         if (el.render){ // some kind of Toolbar.Item
31079             return this.addItem(el);
31080         }
31081         if (typeof el == "string"){ // string
31082             if(el == "separator" || el == "-"){
31083                 return this.addSeparator();
31084             }
31085             if (el == " "){
31086                 return this.addSpacer();
31087             }
31088             if(el == "->"){
31089                 return this.addFill();
31090             }
31091             return this.addText(el);
31092             
31093         }
31094         if(el.tagName){ // element
31095             return this.addElement(el);
31096         }
31097         if(typeof el == "object"){ // must be button config?
31098             return this.addButton(el);
31099         }
31100         // and now what?!?!
31101         return false;
31102         
31103     },
31104     
31105     /**
31106      * Add an Xtype element
31107      * @param {Object} xtype Xtype Object
31108      * @return {Object} created Object
31109      */
31110     addxtype : function(e){
31111         return this.add(e);  
31112     },
31113     
31114     /**
31115      * Returns the Element for this toolbar.
31116      * @return {Roo.Element}
31117      */
31118     getEl : function(){
31119         return this.el;  
31120     },
31121     
31122     /**
31123      * Adds a separator
31124      * @return {Roo.Toolbar.Item} The separator item
31125      */
31126     addSeparator : function(){
31127         return this.addItem(new Roo.Toolbar.Separator());
31128     },
31129
31130     /**
31131      * Adds a spacer element
31132      * @return {Roo.Toolbar.Spacer} The spacer item
31133      */
31134     addSpacer : function(){
31135         return this.addItem(new Roo.Toolbar.Spacer());
31136     },
31137
31138     /**
31139      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31140      * @return {Roo.Toolbar.Fill} The fill item
31141      */
31142     addFill : function(){
31143         return this.addItem(new Roo.Toolbar.Fill());
31144     },
31145
31146     /**
31147      * Adds any standard HTML element to the toolbar
31148      * @param {String/HTMLElement/Element} el The element or id of the element to add
31149      * @return {Roo.Toolbar.Item} The element's item
31150      */
31151     addElement : function(el){
31152         return this.addItem(new Roo.Toolbar.Item(el));
31153     },
31154     /**
31155      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31156      * @type Roo.util.MixedCollection  
31157      */
31158     items : false,
31159      
31160     /**
31161      * Adds any Toolbar.Item or subclass
31162      * @param {Roo.Toolbar.Item} item
31163      * @return {Roo.Toolbar.Item} The item
31164      */
31165     addItem : function(item){
31166         var td = this.nextBlock();
31167         item.render(td);
31168         this.items.add(item);
31169         return item;
31170     },
31171     
31172     /**
31173      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31174      * @param {Object/Array} config A button config or array of configs
31175      * @return {Roo.Toolbar.Button/Array}
31176      */
31177     addButton : function(config){
31178         if(config instanceof Array){
31179             var buttons = [];
31180             for(var i = 0, len = config.length; i < len; i++) {
31181                 buttons.push(this.addButton(config[i]));
31182             }
31183             return buttons;
31184         }
31185         var b = config;
31186         if(!(config instanceof Roo.Toolbar.Button)){
31187             b = config.split ?
31188                 new Roo.Toolbar.SplitButton(config) :
31189                 new Roo.Toolbar.Button(config);
31190         }
31191         var td = this.nextBlock();
31192         b.render(td);
31193         this.items.add(b);
31194         return b;
31195     },
31196     
31197     /**
31198      * Adds text to the toolbar
31199      * @param {String} text The text to add
31200      * @return {Roo.Toolbar.Item} The element's item
31201      */
31202     addText : function(text){
31203         return this.addItem(new Roo.Toolbar.TextItem(text));
31204     },
31205     
31206     /**
31207      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31208      * @param {Number} index The index where the item is to be inserted
31209      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31210      * @return {Roo.Toolbar.Button/Item}
31211      */
31212     insertButton : function(index, item){
31213         if(item instanceof Array){
31214             var buttons = [];
31215             for(var i = 0, len = item.length; i < len; i++) {
31216                buttons.push(this.insertButton(index + i, item[i]));
31217             }
31218             return buttons;
31219         }
31220         if (!(item instanceof Roo.Toolbar.Button)){
31221            item = new Roo.Toolbar.Button(item);
31222         }
31223         var td = document.createElement("td");
31224         this.tr.insertBefore(td, this.tr.childNodes[index]);
31225         item.render(td);
31226         this.items.insert(index, item);
31227         return item;
31228     },
31229     
31230     /**
31231      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31232      * @param {Object} config
31233      * @return {Roo.Toolbar.Item} The element's item
31234      */
31235     addDom : function(config, returnEl){
31236         var td = this.nextBlock();
31237         Roo.DomHelper.overwrite(td, config);
31238         var ti = new Roo.Toolbar.Item(td.firstChild);
31239         ti.render(td);
31240         this.items.add(ti);
31241         return ti;
31242     },
31243
31244     /**
31245      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31246      * @type Roo.util.MixedCollection  
31247      */
31248     fields : false,
31249     
31250     /**
31251      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31252      * Note: the field should not have been rendered yet. For a field that has already been
31253      * rendered, use {@link #addElement}.
31254      * @param {Roo.form.Field} field
31255      * @return {Roo.ToolbarItem}
31256      */
31257      
31258       
31259     addField : function(field) {
31260         if (!this.fields) {
31261             var autoId = 0;
31262             this.fields = new Roo.util.MixedCollection(false, function(o){
31263                 return o.id || ("item" + (++autoId));
31264             });
31265
31266         }
31267         
31268         var td = this.nextBlock();
31269         field.render(td);
31270         var ti = new Roo.Toolbar.Item(td.firstChild);
31271         ti.render(td);
31272         this.items.add(ti);
31273         this.fields.add(field);
31274         return ti;
31275     },
31276     /**
31277      * Hide the toolbar
31278      * @method hide
31279      */
31280      
31281       
31282     hide : function()
31283     {
31284         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31285         this.el.child('div').hide();
31286     },
31287     /**
31288      * Show the toolbar
31289      * @method show
31290      */
31291     show : function()
31292     {
31293         this.el.child('div').show();
31294     },
31295       
31296     // private
31297     nextBlock : function(){
31298         var td = document.createElement("td");
31299         this.tr.appendChild(td);
31300         return td;
31301     },
31302
31303     // private
31304     destroy : function(){
31305         if(this.items){ // rendered?
31306             Roo.destroy.apply(Roo, this.items.items);
31307         }
31308         if(this.fields){ // rendered?
31309             Roo.destroy.apply(Roo, this.fields.items);
31310         }
31311         Roo.Element.uncache(this.el, this.tr);
31312     }
31313 };
31314
31315 /**
31316  * @class Roo.Toolbar.Item
31317  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31318  * @constructor
31319  * Creates a new Item
31320  * @param {HTMLElement} el 
31321  */
31322 Roo.Toolbar.Item = function(el){
31323     var cfg = {};
31324     if (typeof (el.xtype) != 'undefined') {
31325         cfg = el;
31326         el = cfg.el;
31327     }
31328     
31329     this.el = Roo.getDom(el);
31330     this.id = Roo.id(this.el);
31331     this.hidden = false;
31332     
31333     this.addEvents({
31334          /**
31335              * @event render
31336              * Fires when the button is rendered
31337              * @param {Button} this
31338              */
31339         'render': true
31340     });
31341     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31342 };
31343 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31344 //Roo.Toolbar.Item.prototype = {
31345     
31346     /**
31347      * Get this item's HTML Element
31348      * @return {HTMLElement}
31349      */
31350     getEl : function(){
31351        return this.el;  
31352     },
31353
31354     // private
31355     render : function(td){
31356         
31357          this.td = td;
31358         td.appendChild(this.el);
31359         
31360         this.fireEvent('render', this);
31361     },
31362     
31363     /**
31364      * Removes and destroys this item.
31365      */
31366     destroy : function(){
31367         this.td.parentNode.removeChild(this.td);
31368     },
31369     
31370     /**
31371      * Shows this item.
31372      */
31373     show: function(){
31374         this.hidden = false;
31375         this.td.style.display = "";
31376     },
31377     
31378     /**
31379      * Hides this item.
31380      */
31381     hide: function(){
31382         this.hidden = true;
31383         this.td.style.display = "none";
31384     },
31385     
31386     /**
31387      * Convenience function for boolean show/hide.
31388      * @param {Boolean} visible true to show/false to hide
31389      */
31390     setVisible: function(visible){
31391         if(visible) {
31392             this.show();
31393         }else{
31394             this.hide();
31395         }
31396     },
31397     
31398     /**
31399      * Try to focus this item.
31400      */
31401     focus : function(){
31402         Roo.fly(this.el).focus();
31403     },
31404     
31405     /**
31406      * Disables this item.
31407      */
31408     disable : function(){
31409         Roo.fly(this.td).addClass("x-item-disabled");
31410         this.disabled = true;
31411         this.el.disabled = true;
31412     },
31413     
31414     /**
31415      * Enables this item.
31416      */
31417     enable : function(){
31418         Roo.fly(this.td).removeClass("x-item-disabled");
31419         this.disabled = false;
31420         this.el.disabled = false;
31421     }
31422 });
31423
31424
31425 /**
31426  * @class Roo.Toolbar.Separator
31427  * @extends Roo.Toolbar.Item
31428  * A simple toolbar separator class
31429  * @constructor
31430  * Creates a new Separator
31431  */
31432 Roo.Toolbar.Separator = function(cfg){
31433     
31434     var s = document.createElement("span");
31435     s.className = "ytb-sep";
31436     if (cfg) {
31437         cfg.el = s;
31438     }
31439     
31440     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31441 };
31442 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31443     enable:Roo.emptyFn,
31444     disable:Roo.emptyFn,
31445     focus:Roo.emptyFn
31446 });
31447
31448 /**
31449  * @class Roo.Toolbar.Spacer
31450  * @extends Roo.Toolbar.Item
31451  * A simple element that adds extra horizontal space to a toolbar.
31452  * @constructor
31453  * Creates a new Spacer
31454  */
31455 Roo.Toolbar.Spacer = function(cfg){
31456     var s = document.createElement("div");
31457     s.className = "ytb-spacer";
31458     if (cfg) {
31459         cfg.el = s;
31460     }
31461     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31462 };
31463 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31464     enable:Roo.emptyFn,
31465     disable:Roo.emptyFn,
31466     focus:Roo.emptyFn
31467 });
31468
31469 /**
31470  * @class Roo.Toolbar.Fill
31471  * @extends Roo.Toolbar.Spacer
31472  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31473  * @constructor
31474  * Creates a new Spacer
31475  */
31476 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31477     // private
31478     render : function(td){
31479         td.style.width = '100%';
31480         Roo.Toolbar.Fill.superclass.render.call(this, td);
31481     }
31482 });
31483
31484 /**
31485  * @class Roo.Toolbar.TextItem
31486  * @extends Roo.Toolbar.Item
31487  * A simple class that renders text directly into a toolbar.
31488  * @constructor
31489  * Creates a new TextItem
31490  * @cfg {string} text 
31491  */
31492 Roo.Toolbar.TextItem = function(cfg){
31493     var  text = cfg || "";
31494     if (typeof(cfg) == 'object') {
31495         text = cfg.text || "";
31496     }  else {
31497         cfg = null;
31498     }
31499     var s = document.createElement("span");
31500     s.className = "ytb-text";
31501     s.innerHTML = text;
31502     if (cfg) {
31503         cfg.el  = s;
31504     }
31505     
31506     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31507 };
31508 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31509     
31510      
31511     enable:Roo.emptyFn,
31512     disable:Roo.emptyFn,
31513     focus:Roo.emptyFn
31514 });
31515
31516 /**
31517  * @class Roo.Toolbar.Button
31518  * @extends Roo.Button
31519  * A button that renders into a toolbar.
31520  * @constructor
31521  * Creates a new Button
31522  * @param {Object} config A standard {@link Roo.Button} config object
31523  */
31524 Roo.Toolbar.Button = function(config){
31525     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31526 };
31527 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31528 {
31529     
31530     
31531     render : function(td){
31532         this.td = td;
31533         Roo.Toolbar.Button.superclass.render.call(this, td);
31534     },
31535     
31536     /**
31537      * Removes and destroys this button
31538      */
31539     destroy : function(){
31540         Roo.Toolbar.Button.superclass.destroy.call(this);
31541         this.td.parentNode.removeChild(this.td);
31542     },
31543     
31544     /**
31545      * Shows this button
31546      */
31547     show: function(){
31548         this.hidden = false;
31549         this.td.style.display = "";
31550     },
31551     
31552     /**
31553      * Hides this button
31554      */
31555     hide: function(){
31556         this.hidden = true;
31557         this.td.style.display = "none";
31558     },
31559
31560     /**
31561      * Disables this item
31562      */
31563     disable : function(){
31564         Roo.fly(this.td).addClass("x-item-disabled");
31565         this.disabled = true;
31566     },
31567
31568     /**
31569      * Enables this item
31570      */
31571     enable : function(){
31572         Roo.fly(this.td).removeClass("x-item-disabled");
31573         this.disabled = false;
31574     }
31575 });
31576 // backwards compat
31577 Roo.ToolbarButton = Roo.Toolbar.Button;
31578
31579 /**
31580  * @class Roo.Toolbar.SplitButton
31581  * @extends Roo.SplitButton
31582  * A menu button that renders into a toolbar.
31583  * @constructor
31584  * Creates a new SplitButton
31585  * @param {Object} config A standard {@link Roo.SplitButton} config object
31586  */
31587 Roo.Toolbar.SplitButton = function(config){
31588     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31589 };
31590 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31591     render : function(td){
31592         this.td = td;
31593         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31594     },
31595     
31596     /**
31597      * Removes and destroys this button
31598      */
31599     destroy : function(){
31600         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31601         this.td.parentNode.removeChild(this.td);
31602     },
31603     
31604     /**
31605      * Shows this button
31606      */
31607     show: function(){
31608         this.hidden = false;
31609         this.td.style.display = "";
31610     },
31611     
31612     /**
31613      * Hides this button
31614      */
31615     hide: function(){
31616         this.hidden = true;
31617         this.td.style.display = "none";
31618     }
31619 });
31620
31621 // backwards compat
31622 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31623  * Based on:
31624  * Ext JS Library 1.1.1
31625  * Copyright(c) 2006-2007, Ext JS, LLC.
31626  *
31627  * Originally Released Under LGPL - original licence link has changed is not relivant.
31628  *
31629  * Fork - LGPL
31630  * <script type="text/javascript">
31631  */
31632  
31633 /**
31634  * @class Roo.PagingToolbar
31635  * @extends Roo.Toolbar
31636  * @children   Roo.Toolbar.Item Roo.form.Field
31637  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31638  * @constructor
31639  * Create a new PagingToolbar
31640  * @param {Object} config The config object
31641  */
31642 Roo.PagingToolbar = function(el, ds, config)
31643 {
31644     // old args format still supported... - xtype is prefered..
31645     if (typeof(el) == 'object' && el.xtype) {
31646         // created from xtype...
31647         config = el;
31648         ds = el.dataSource;
31649         el = config.container;
31650     }
31651     var items = [];
31652     if (config.items) {
31653         items = config.items;
31654         config.items = [];
31655     }
31656     
31657     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31658     this.ds = ds;
31659     this.cursor = 0;
31660     this.renderButtons(this.el);
31661     this.bind(ds);
31662     
31663     // supprot items array.
31664    
31665     Roo.each(items, function(e) {
31666         this.add(Roo.factory(e));
31667     },this);
31668     
31669 };
31670
31671 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31672    
31673     /**
31674      * @cfg {String/HTMLElement/Element} container
31675      * container The id or element that will contain the toolbar
31676      */
31677     /**
31678      * @cfg {Boolean} displayInfo
31679      * True to display the displayMsg (defaults to false)
31680      */
31681     
31682     
31683     /**
31684      * @cfg {Number} pageSize
31685      * The number of records to display per page (defaults to 20)
31686      */
31687     pageSize: 20,
31688     /**
31689      * @cfg {String} displayMsg
31690      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31691      */
31692     displayMsg : 'Displaying {0} - {1} of {2}',
31693     /**
31694      * @cfg {String} emptyMsg
31695      * The message to display when no records are found (defaults to "No data to display")
31696      */
31697     emptyMsg : 'No data to display',
31698     /**
31699      * Customizable piece of the default paging text (defaults to "Page")
31700      * @type String
31701      */
31702     beforePageText : "Page",
31703     /**
31704      * Customizable piece of the default paging text (defaults to "of %0")
31705      * @type String
31706      */
31707     afterPageText : "of {0}",
31708     /**
31709      * Customizable piece of the default paging text (defaults to "First Page")
31710      * @type String
31711      */
31712     firstText : "First Page",
31713     /**
31714      * Customizable piece of the default paging text (defaults to "Previous Page")
31715      * @type String
31716      */
31717     prevText : "Previous Page",
31718     /**
31719      * Customizable piece of the default paging text (defaults to "Next Page")
31720      * @type String
31721      */
31722     nextText : "Next Page",
31723     /**
31724      * Customizable piece of the default paging text (defaults to "Last Page")
31725      * @type String
31726      */
31727     lastText : "Last Page",
31728     /**
31729      * Customizable piece of the default paging text (defaults to "Refresh")
31730      * @type String
31731      */
31732     refreshText : "Refresh",
31733
31734     // private
31735     renderButtons : function(el){
31736         Roo.PagingToolbar.superclass.render.call(this, el);
31737         this.first = this.addButton({
31738             tooltip: this.firstText,
31739             cls: "x-btn-icon x-grid-page-first",
31740             disabled: true,
31741             handler: this.onClick.createDelegate(this, ["first"])
31742         });
31743         this.prev = this.addButton({
31744             tooltip: this.prevText,
31745             cls: "x-btn-icon x-grid-page-prev",
31746             disabled: true,
31747             handler: this.onClick.createDelegate(this, ["prev"])
31748         });
31749         //this.addSeparator();
31750         this.add(this.beforePageText);
31751         this.field = Roo.get(this.addDom({
31752            tag: "input",
31753            type: "text",
31754            size: "3",
31755            value: "1",
31756            cls: "x-grid-page-number"
31757         }).el);
31758         this.field.on("keydown", this.onPagingKeydown, this);
31759         this.field.on("focus", function(){this.dom.select();});
31760         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31761         this.field.setHeight(18);
31762         //this.addSeparator();
31763         this.next = this.addButton({
31764             tooltip: this.nextText,
31765             cls: "x-btn-icon x-grid-page-next",
31766             disabled: true,
31767             handler: this.onClick.createDelegate(this, ["next"])
31768         });
31769         this.last = this.addButton({
31770             tooltip: this.lastText,
31771             cls: "x-btn-icon x-grid-page-last",
31772             disabled: true,
31773             handler: this.onClick.createDelegate(this, ["last"])
31774         });
31775         //this.addSeparator();
31776         this.loading = this.addButton({
31777             tooltip: this.refreshText,
31778             cls: "x-btn-icon x-grid-loading",
31779             handler: this.onClick.createDelegate(this, ["refresh"])
31780         });
31781
31782         if(this.displayInfo){
31783             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31784         }
31785     },
31786
31787     // private
31788     updateInfo : function(){
31789         if(this.displayEl){
31790             var count = this.ds.getCount();
31791             var msg = count == 0 ?
31792                 this.emptyMsg :
31793                 String.format(
31794                     this.displayMsg,
31795                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31796                 );
31797             this.displayEl.update(msg);
31798         }
31799     },
31800
31801     // private
31802     onLoad : function(ds, r, o){
31803        this.cursor = o.params ? o.params.start : 0;
31804        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31805
31806        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31807        this.field.dom.value = ap;
31808        this.first.setDisabled(ap == 1);
31809        this.prev.setDisabled(ap == 1);
31810        this.next.setDisabled(ap == ps);
31811        this.last.setDisabled(ap == ps);
31812        this.loading.enable();
31813        this.updateInfo();
31814     },
31815
31816     // private
31817     getPageData : function(){
31818         var total = this.ds.getTotalCount();
31819         return {
31820             total : total,
31821             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31822             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31823         };
31824     },
31825
31826     // private
31827     onLoadError : function(){
31828         this.loading.enable();
31829     },
31830
31831     // private
31832     onPagingKeydown : function(e){
31833         var k = e.getKey();
31834         var d = this.getPageData();
31835         if(k == e.RETURN){
31836             var v = this.field.dom.value, pageNum;
31837             if(!v || isNaN(pageNum = parseInt(v, 10))){
31838                 this.field.dom.value = d.activePage;
31839                 return;
31840             }
31841             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31842             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31843             e.stopEvent();
31844         }
31845         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))
31846         {
31847           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31848           this.field.dom.value = pageNum;
31849           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31850           e.stopEvent();
31851         }
31852         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31853         {
31854           var v = this.field.dom.value, pageNum; 
31855           var increment = (e.shiftKey) ? 10 : 1;
31856           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31857             increment *= -1;
31858           }
31859           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31860             this.field.dom.value = d.activePage;
31861             return;
31862           }
31863           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31864           {
31865             this.field.dom.value = parseInt(v, 10) + increment;
31866             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31867             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31868           }
31869           e.stopEvent();
31870         }
31871     },
31872
31873     // private
31874     beforeLoad : function(){
31875         if(this.loading){
31876             this.loading.disable();
31877         }
31878     },
31879
31880     // private
31881     onClick : function(which){
31882         var ds = this.ds;
31883         switch(which){
31884             case "first":
31885                 ds.load({params:{start: 0, limit: this.pageSize}});
31886             break;
31887             case "prev":
31888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31889             break;
31890             case "next":
31891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31892             break;
31893             case "last":
31894                 var total = ds.getTotalCount();
31895                 var extra = total % this.pageSize;
31896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31898             break;
31899             case "refresh":
31900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31901             break;
31902         }
31903     },
31904
31905     /**
31906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31907      * @param {Roo.data.Store} store The data store to unbind
31908      */
31909     unbind : function(ds){
31910         ds.un("beforeload", this.beforeLoad, this);
31911         ds.un("load", this.onLoad, this);
31912         ds.un("loadexception", this.onLoadError, this);
31913         ds.un("remove", this.updateInfo, this);
31914         ds.un("add", this.updateInfo, this);
31915         this.ds = undefined;
31916     },
31917
31918     /**
31919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31920      * @param {Roo.data.Store} store The data store to bind
31921      */
31922     bind : function(ds){
31923         ds.on("beforeload", this.beforeLoad, this);
31924         ds.on("load", this.onLoad, this);
31925         ds.on("loadexception", this.onLoadError, this);
31926         ds.on("remove", this.updateInfo, this);
31927         ds.on("add", this.updateInfo, this);
31928         this.ds = ds;
31929     }
31930 });/*
31931  * Based on:
31932  * Ext JS Library 1.1.1
31933  * Copyright(c) 2006-2007, Ext JS, LLC.
31934  *
31935  * Originally Released Under LGPL - original licence link has changed is not relivant.
31936  *
31937  * Fork - LGPL
31938  * <script type="text/javascript">
31939  */
31940
31941 /**
31942  * @class Roo.Resizable
31943  * @extends Roo.util.Observable
31944  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31945  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31946  * 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
31947  * the element will be wrapped for you automatically.</p>
31948  * <p>Here is the list of valid resize handles:</p>
31949  * <pre>
31950 Value   Description
31951 ------  -------------------
31952  'n'     north
31953  's'     south
31954  'e'     east
31955  'w'     west
31956  'nw'    northwest
31957  'sw'    southwest
31958  'se'    southeast
31959  'ne'    northeast
31960  'hd'    horizontal drag
31961  'all'   all
31962 </pre>
31963  * <p>Here's an example showing the creation of a typical Resizable:</p>
31964  * <pre><code>
31965 var resizer = new Roo.Resizable("element-id", {
31966     handles: 'all',
31967     minWidth: 200,
31968     minHeight: 100,
31969     maxWidth: 500,
31970     maxHeight: 400,
31971     pinned: true
31972 });
31973 resizer.on("resize", myHandler);
31974 </code></pre>
31975  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31976  * resizer.east.setDisplayed(false);</p>
31977  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31978  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31979  * resize operation's new size (defaults to [0, 0])
31980  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31981  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31982  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31983  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31984  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31985  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31986  * @cfg {Number} width The width of the element in pixels (defaults to null)
31987  * @cfg {Number} height The height of the element in pixels (defaults to null)
31988  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31989  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31990  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31991  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31992  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31993  * in favor of the handles config option (defaults to false)
31994  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31995  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31996  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31997  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31998  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31999  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32000  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32001  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32002  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32003  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32004  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32005  * @constructor
32006  * Create a new resizable component
32007  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32008  * @param {Object} config configuration options
32009   */
32010 Roo.Resizable = function(el, config)
32011 {
32012     this.el = Roo.get(el);
32013
32014     if(config && config.wrap){
32015         config.resizeChild = this.el;
32016         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32017         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32018         this.el.setStyle("overflow", "hidden");
32019         this.el.setPositioning(config.resizeChild.getPositioning());
32020         config.resizeChild.clearPositioning();
32021         if(!config.width || !config.height){
32022             var csize = config.resizeChild.getSize();
32023             this.el.setSize(csize.width, csize.height);
32024         }
32025         if(config.pinned && !config.adjustments){
32026             config.adjustments = "auto";
32027         }
32028     }
32029
32030     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32031     this.proxy.unselectable();
32032     this.proxy.enableDisplayMode('block');
32033
32034     Roo.apply(this, config);
32035
32036     if(this.pinned){
32037         this.disableTrackOver = true;
32038         this.el.addClass("x-resizable-pinned");
32039     }
32040     // if the element isn't positioned, make it relative
32041     var position = this.el.getStyle("position");
32042     if(position != "absolute" && position != "fixed"){
32043         this.el.setStyle("position", "relative");
32044     }
32045     if(!this.handles){ // no handles passed, must be legacy style
32046         this.handles = 's,e,se';
32047         if(this.multiDirectional){
32048             this.handles += ',n,w';
32049         }
32050     }
32051     if(this.handles == "all"){
32052         this.handles = "n s e w ne nw se sw";
32053     }
32054     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32055     var ps = Roo.Resizable.positions;
32056     for(var i = 0, len = hs.length; i < len; i++){
32057         if(hs[i] && ps[hs[i]]){
32058             var pos = ps[hs[i]];
32059             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32060         }
32061     }
32062     // legacy
32063     this.corner = this.southeast;
32064     
32065     // updateBox = the box can move..
32066     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32067         this.updateBox = true;
32068     }
32069
32070     this.activeHandle = null;
32071
32072     if(this.resizeChild){
32073         if(typeof this.resizeChild == "boolean"){
32074             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32075         }else{
32076             this.resizeChild = Roo.get(this.resizeChild, true);
32077         }
32078     }
32079     
32080     if(this.adjustments == "auto"){
32081         var rc = this.resizeChild;
32082         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32083         if(rc && (hw || hn)){
32084             rc.position("relative");
32085             rc.setLeft(hw ? hw.el.getWidth() : 0);
32086             rc.setTop(hn ? hn.el.getHeight() : 0);
32087         }
32088         this.adjustments = [
32089             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32090             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32091         ];
32092     }
32093
32094     if(this.draggable){
32095         this.dd = this.dynamic ?
32096             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32097         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32098     }
32099
32100     // public events
32101     this.addEvents({
32102         /**
32103          * @event beforeresize
32104          * Fired before resize is allowed. Set enabled to false to cancel resize.
32105          * @param {Roo.Resizable} this
32106          * @param {Roo.EventObject} e The mousedown event
32107          */
32108         "beforeresize" : true,
32109         /**
32110          * @event resizing
32111          * Fired a resizing.
32112          * @param {Roo.Resizable} this
32113          * @param {Number} x The new x position
32114          * @param {Number} y The new y position
32115          * @param {Number} w The new w width
32116          * @param {Number} h The new h hight
32117          * @param {Roo.EventObject} e The mouseup event
32118          */
32119         "resizing" : true,
32120         /**
32121          * @event resize
32122          * Fired after a resize.
32123          * @param {Roo.Resizable} this
32124          * @param {Number} width The new width
32125          * @param {Number} height The new height
32126          * @param {Roo.EventObject} e The mouseup event
32127          */
32128         "resize" : true
32129     });
32130
32131     if(this.width !== null && this.height !== null){
32132         this.resizeTo(this.width, this.height);
32133     }else{
32134         this.updateChildSize();
32135     }
32136     if(Roo.isIE){
32137         this.el.dom.style.zoom = 1;
32138     }
32139     Roo.Resizable.superclass.constructor.call(this);
32140 };
32141
32142 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32143         resizeChild : false,
32144         adjustments : [0, 0],
32145         minWidth : 5,
32146         minHeight : 5,
32147         maxWidth : 10000,
32148         maxHeight : 10000,
32149         enabled : true,
32150         animate : false,
32151         duration : .35,
32152         dynamic : false,
32153         handles : false,
32154         multiDirectional : false,
32155         disableTrackOver : false,
32156         easing : 'easeOutStrong',
32157         widthIncrement : 0,
32158         heightIncrement : 0,
32159         pinned : false,
32160         width : null,
32161         height : null,
32162         preserveRatio : false,
32163         transparent: false,
32164         minX: 0,
32165         minY: 0,
32166         draggable: false,
32167
32168         /**
32169          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32170          */
32171         constrainTo: undefined,
32172         /**
32173          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32174          */
32175         resizeRegion: undefined,
32176
32177
32178     /**
32179      * Perform a manual resize
32180      * @param {Number} width
32181      * @param {Number} height
32182      */
32183     resizeTo : function(width, height){
32184         this.el.setSize(width, height);
32185         this.updateChildSize();
32186         this.fireEvent("resize", this, width, height, null);
32187     },
32188
32189     // private
32190     startSizing : function(e, handle){
32191         this.fireEvent("beforeresize", this, e);
32192         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32193
32194             if(!this.overlay){
32195                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32196                 this.overlay.unselectable();
32197                 this.overlay.enableDisplayMode("block");
32198                 this.overlay.on("mousemove", this.onMouseMove, this);
32199                 this.overlay.on("mouseup", this.onMouseUp, this);
32200             }
32201             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32202
32203             this.resizing = true;
32204             this.startBox = this.el.getBox();
32205             this.startPoint = e.getXY();
32206             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32207                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32208
32209             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32210             this.overlay.show();
32211
32212             if(this.constrainTo) {
32213                 var ct = Roo.get(this.constrainTo);
32214                 this.resizeRegion = ct.getRegion().adjust(
32215                     ct.getFrameWidth('t'),
32216                     ct.getFrameWidth('l'),
32217                     -ct.getFrameWidth('b'),
32218                     -ct.getFrameWidth('r')
32219                 );
32220             }
32221
32222             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32223             this.proxy.show();
32224             this.proxy.setBox(this.startBox);
32225             if(!this.dynamic){
32226                 this.proxy.setStyle('visibility', 'visible');
32227             }
32228         }
32229     },
32230
32231     // private
32232     onMouseDown : function(handle, e){
32233         if(this.enabled){
32234             e.stopEvent();
32235             this.activeHandle = handle;
32236             this.startSizing(e, handle);
32237         }
32238     },
32239
32240     // private
32241     onMouseUp : function(e){
32242         var size = this.resizeElement();
32243         this.resizing = false;
32244         this.handleOut();
32245         this.overlay.hide();
32246         this.proxy.hide();
32247         this.fireEvent("resize", this, size.width, size.height, e);
32248     },
32249
32250     // private
32251     updateChildSize : function(){
32252         
32253         if(this.resizeChild){
32254             var el = this.el;
32255             var child = this.resizeChild;
32256             var adj = this.adjustments;
32257             if(el.dom.offsetWidth){
32258                 var b = el.getSize(true);
32259                 child.setSize(b.width+adj[0], b.height+adj[1]);
32260             }
32261             // Second call here for IE
32262             // The first call enables instant resizing and
32263             // the second call corrects scroll bars if they
32264             // exist
32265             if(Roo.isIE){
32266                 setTimeout(function(){
32267                     if(el.dom.offsetWidth){
32268                         var b = el.getSize(true);
32269                         child.setSize(b.width+adj[0], b.height+adj[1]);
32270                     }
32271                 }, 10);
32272             }
32273         }
32274     },
32275
32276     // private
32277     snap : function(value, inc, min){
32278         if(!inc || !value) {
32279             return value;
32280         }
32281         var newValue = value;
32282         var m = value % inc;
32283         if(m > 0){
32284             if(m > (inc/2)){
32285                 newValue = value + (inc-m);
32286             }else{
32287                 newValue = value - m;
32288             }
32289         }
32290         return Math.max(min, newValue);
32291     },
32292
32293     // private
32294     resizeElement : function(){
32295         var box = this.proxy.getBox();
32296         if(this.updateBox){
32297             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32298         }else{
32299             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32300         }
32301         this.updateChildSize();
32302         if(!this.dynamic){
32303             this.proxy.hide();
32304         }
32305         return box;
32306     },
32307
32308     // private
32309     constrain : function(v, diff, m, mx){
32310         if(v - diff < m){
32311             diff = v - m;
32312         }else if(v - diff > mx){
32313             diff = mx - v;
32314         }
32315         return diff;
32316     },
32317
32318     // private
32319     onMouseMove : function(e){
32320         
32321         if(this.enabled){
32322             try{// try catch so if something goes wrong the user doesn't get hung
32323
32324             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32325                 return;
32326             }
32327
32328             //var curXY = this.startPoint;
32329             var curSize = this.curSize || this.startBox;
32330             var x = this.startBox.x, y = this.startBox.y;
32331             var ox = x, oy = y;
32332             var w = curSize.width, h = curSize.height;
32333             var ow = w, oh = h;
32334             var mw = this.minWidth, mh = this.minHeight;
32335             var mxw = this.maxWidth, mxh = this.maxHeight;
32336             var wi = this.widthIncrement;
32337             var hi = this.heightIncrement;
32338
32339             var eventXY = e.getXY();
32340             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32341             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32342
32343             var pos = this.activeHandle.position;
32344
32345             switch(pos){
32346                 case "east":
32347                     w += diffX;
32348                     w = Math.min(Math.max(mw, w), mxw);
32349                     break;
32350              
32351                 case "south":
32352                     h += diffY;
32353                     h = Math.min(Math.max(mh, h), mxh);
32354                     break;
32355                 case "southeast":
32356                     w += diffX;
32357                     h += diffY;
32358                     w = Math.min(Math.max(mw, w), mxw);
32359                     h = Math.min(Math.max(mh, h), mxh);
32360                     break;
32361                 case "north":
32362                     diffY = this.constrain(h, diffY, mh, mxh);
32363                     y += diffY;
32364                     h -= diffY;
32365                     break;
32366                 case "hdrag":
32367                     
32368                     if (wi) {
32369                         var adiffX = Math.abs(diffX);
32370                         var sub = (adiffX % wi); // how much 
32371                         if (sub > (wi/2)) { // far enough to snap
32372                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32373                         } else {
32374                             // remove difference.. 
32375                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32376                         }
32377                     }
32378                     x += diffX;
32379                     x = Math.max(this.minX, x);
32380                     break;
32381                 case "west":
32382                     diffX = this.constrain(w, diffX, mw, mxw);
32383                     x += diffX;
32384                     w -= diffX;
32385                     break;
32386                 case "northeast":
32387                     w += diffX;
32388                     w = Math.min(Math.max(mw, w), mxw);
32389                     diffY = this.constrain(h, diffY, mh, mxh);
32390                     y += diffY;
32391                     h -= diffY;
32392                     break;
32393                 case "northwest":
32394                     diffX = this.constrain(w, diffX, mw, mxw);
32395                     diffY = this.constrain(h, diffY, mh, mxh);
32396                     y += diffY;
32397                     h -= diffY;
32398                     x += diffX;
32399                     w -= diffX;
32400                     break;
32401                case "southwest":
32402                     diffX = this.constrain(w, diffX, mw, mxw);
32403                     h += diffY;
32404                     h = Math.min(Math.max(mh, h), mxh);
32405                     x += diffX;
32406                     w -= diffX;
32407                     break;
32408             }
32409
32410             var sw = this.snap(w, wi, mw);
32411             var sh = this.snap(h, hi, mh);
32412             if(sw != w || sh != h){
32413                 switch(pos){
32414                     case "northeast":
32415                         y -= sh - h;
32416                     break;
32417                     case "north":
32418                         y -= sh - h;
32419                         break;
32420                     case "southwest":
32421                         x -= sw - w;
32422                     break;
32423                     case "west":
32424                         x -= sw - w;
32425                         break;
32426                     case "northwest":
32427                         x -= sw - w;
32428                         y -= sh - h;
32429                     break;
32430                 }
32431                 w = sw;
32432                 h = sh;
32433             }
32434
32435             if(this.preserveRatio){
32436                 switch(pos){
32437                     case "southeast":
32438                     case "east":
32439                         h = oh * (w/ow);
32440                         h = Math.min(Math.max(mh, h), mxh);
32441                         w = ow * (h/oh);
32442                        break;
32443                     case "south":
32444                         w = ow * (h/oh);
32445                         w = Math.min(Math.max(mw, w), mxw);
32446                         h = oh * (w/ow);
32447                         break;
32448                     case "northeast":
32449                         w = ow * (h/oh);
32450                         w = Math.min(Math.max(mw, w), mxw);
32451                         h = oh * (w/ow);
32452                     break;
32453                     case "north":
32454                         var tw = w;
32455                         w = ow * (h/oh);
32456                         w = Math.min(Math.max(mw, w), mxw);
32457                         h = oh * (w/ow);
32458                         x += (tw - w) / 2;
32459                         break;
32460                     case "southwest":
32461                         h = oh * (w/ow);
32462                         h = Math.min(Math.max(mh, h), mxh);
32463                         var tw = w;
32464                         w = ow * (h/oh);
32465                         x += tw - w;
32466                         break;
32467                     case "west":
32468                         var th = h;
32469                         h = oh * (w/ow);
32470                         h = Math.min(Math.max(mh, h), mxh);
32471                         y += (th - h) / 2;
32472                         var tw = w;
32473                         w = ow * (h/oh);
32474                         x += tw - w;
32475                        break;
32476                     case "northwest":
32477                         var tw = w;
32478                         var th = h;
32479                         h = oh * (w/ow);
32480                         h = Math.min(Math.max(mh, h), mxh);
32481                         w = ow * (h/oh);
32482                         y += th - h;
32483                         x += tw - w;
32484                        break;
32485
32486                 }
32487             }
32488             if (pos == 'hdrag') {
32489                 w = ow;
32490             }
32491             this.proxy.setBounds(x, y, w, h);
32492             if(this.dynamic){
32493                 this.resizeElement();
32494             }
32495             }catch(e){}
32496         }
32497         this.fireEvent("resizing", this, x, y, w, h, e);
32498     },
32499
32500     // private
32501     handleOver : function(){
32502         if(this.enabled){
32503             this.el.addClass("x-resizable-over");
32504         }
32505     },
32506
32507     // private
32508     handleOut : function(){
32509         if(!this.resizing){
32510             this.el.removeClass("x-resizable-over");
32511         }
32512     },
32513
32514     /**
32515      * Returns the element this component is bound to.
32516      * @return {Roo.Element}
32517      */
32518     getEl : function(){
32519         return this.el;
32520     },
32521
32522     /**
32523      * Returns the resizeChild element (or null).
32524      * @return {Roo.Element}
32525      */
32526     getResizeChild : function(){
32527         return this.resizeChild;
32528     },
32529     groupHandler : function()
32530     {
32531         
32532     },
32533     /**
32534      * Destroys this resizable. If the element was wrapped and
32535      * removeEl is not true then the element remains.
32536      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32537      */
32538     destroy : function(removeEl){
32539         this.proxy.remove();
32540         if(this.overlay){
32541             this.overlay.removeAllListeners();
32542             this.overlay.remove();
32543         }
32544         var ps = Roo.Resizable.positions;
32545         for(var k in ps){
32546             if(typeof ps[k] != "function" && this[ps[k]]){
32547                 var h = this[ps[k]];
32548                 h.el.removeAllListeners();
32549                 h.el.remove();
32550             }
32551         }
32552         if(removeEl){
32553             this.el.update("");
32554             this.el.remove();
32555         }
32556     }
32557 });
32558
32559 // private
32560 // hash to map config positions to true positions
32561 Roo.Resizable.positions = {
32562     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32563     hd: "hdrag"
32564 };
32565
32566 // private
32567 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32568     if(!this.tpl){
32569         // only initialize the template if resizable is used
32570         var tpl = Roo.DomHelper.createTemplate(
32571             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32572         );
32573         tpl.compile();
32574         Roo.Resizable.Handle.prototype.tpl = tpl;
32575     }
32576     this.position = pos;
32577     this.rz = rz;
32578     // show north drag fro topdra
32579     var handlepos = pos == 'hdrag' ? 'north' : pos;
32580     
32581     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32582     if (pos == 'hdrag') {
32583         this.el.setStyle('cursor', 'pointer');
32584     }
32585     this.el.unselectable();
32586     if(transparent){
32587         this.el.setOpacity(0);
32588     }
32589     this.el.on("mousedown", this.onMouseDown, this);
32590     if(!disableTrackOver){
32591         this.el.on("mouseover", this.onMouseOver, this);
32592         this.el.on("mouseout", this.onMouseOut, this);
32593     }
32594 };
32595
32596 // private
32597 Roo.Resizable.Handle.prototype = {
32598     afterResize : function(rz){
32599         Roo.log('after?');
32600         // do nothing
32601     },
32602     // private
32603     onMouseDown : function(e){
32604         this.rz.onMouseDown(this, e);
32605     },
32606     // private
32607     onMouseOver : function(e){
32608         this.rz.handleOver(this, e);
32609     },
32610     // private
32611     onMouseOut : function(e){
32612         this.rz.handleOut(this, e);
32613     }
32614 };/*
32615  * Based on:
32616  * Ext JS Library 1.1.1
32617  * Copyright(c) 2006-2007, Ext JS, LLC.
32618  *
32619  * Originally Released Under LGPL - original licence link has changed is not relivant.
32620  *
32621  * Fork - LGPL
32622  * <script type="text/javascript">
32623  */
32624
32625 /**
32626  * @class Roo.Editor
32627  * @extends Roo.Component
32628  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32629  * @constructor
32630  * Create a new Editor
32631  * @param {Roo.form.Field} field The Field object (or descendant)
32632  * @param {Object} config The config object
32633  */
32634 Roo.Editor = function(field, config){
32635     Roo.Editor.superclass.constructor.call(this, config);
32636     this.field = field;
32637     this.addEvents({
32638         /**
32639              * @event beforestartedit
32640              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32641              * false from the handler of this event.
32642              * @param {Editor} this
32643              * @param {Roo.Element} boundEl The underlying element bound to this editor
32644              * @param {Mixed} value The field value being set
32645              */
32646         "beforestartedit" : true,
32647         /**
32648              * @event startedit
32649              * Fires when this editor is displayed
32650              * @param {Roo.Element} boundEl The underlying element bound to this editor
32651              * @param {Mixed} value The starting field value
32652              */
32653         "startedit" : true,
32654         /**
32655              * @event beforecomplete
32656              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32657              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32658              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32659              * event will not fire since no edit actually occurred.
32660              * @param {Editor} this
32661              * @param {Mixed} value The current field value
32662              * @param {Mixed} startValue The original field value
32663              */
32664         "beforecomplete" : true,
32665         /**
32666              * @event complete
32667              * Fires after editing is complete and any changed value has been written to the underlying field.
32668              * @param {Editor} this
32669              * @param {Mixed} value The current field value
32670              * @param {Mixed} startValue The original field value
32671              */
32672         "complete" : true,
32673         /**
32674          * @event specialkey
32675          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32676          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32677          * @param {Roo.form.Field} this
32678          * @param {Roo.EventObject} e The event object
32679          */
32680         "specialkey" : true
32681     });
32682 };
32683
32684 Roo.extend(Roo.Editor, Roo.Component, {
32685     /**
32686      * @cfg {Boolean/String} autosize
32687      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32688      * or "height" to adopt the height only (defaults to false)
32689      */
32690     /**
32691      * @cfg {Boolean} revertInvalid
32692      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32693      * validation fails (defaults to true)
32694      */
32695     /**
32696      * @cfg {Boolean} ignoreNoChange
32697      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32698      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32699      * will never be ignored.
32700      */
32701     /**
32702      * @cfg {Boolean} hideEl
32703      * False to keep the bound element visible while the editor is displayed (defaults to true)
32704      */
32705     /**
32706      * @cfg {Mixed} value
32707      * The data value of the underlying field (defaults to "")
32708      */
32709     value : "",
32710     /**
32711      * @cfg {String} alignment
32712      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32713      */
32714     alignment: "c-c?",
32715     /**
32716      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32717      * for bottom-right shadow (defaults to "frame")
32718      */
32719     shadow : "frame",
32720     /**
32721      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32722      */
32723     constrain : false,
32724     /**
32725      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32726      */
32727     completeOnEnter : false,
32728     /**
32729      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32730      */
32731     cancelOnEsc : false,
32732     /**
32733      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32734      */
32735     updateEl : false,
32736
32737     // private
32738     onRender : function(ct, position){
32739         this.el = new Roo.Layer({
32740             shadow: this.shadow,
32741             cls: "x-editor",
32742             parentEl : ct,
32743             shim : this.shim,
32744             shadowOffset:4,
32745             id: this.id,
32746             constrain: this.constrain
32747         });
32748         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32749         if(this.field.msgTarget != 'title'){
32750             this.field.msgTarget = 'qtip';
32751         }
32752         this.field.render(this.el);
32753         if(Roo.isGecko){
32754             this.field.el.dom.setAttribute('autocomplete', 'off');
32755         }
32756         this.field.on("specialkey", this.onSpecialKey, this);
32757         if(this.swallowKeys){
32758             this.field.el.swallowEvent(['keydown','keypress']);
32759         }
32760         this.field.show();
32761         this.field.on("blur", this.onBlur, this);
32762         if(this.field.grow){
32763             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32764         }
32765     },
32766
32767     onSpecialKey : function(field, e)
32768     {
32769         //Roo.log('editor onSpecialKey');
32770         if(this.completeOnEnter && e.getKey() == e.ENTER){
32771             e.stopEvent();
32772             this.completeEdit();
32773             return;
32774         }
32775         // do not fire special key otherwise it might hide close the editor...
32776         if(e.getKey() == e.ENTER){    
32777             return;
32778         }
32779         if(this.cancelOnEsc && e.getKey() == e.ESC){
32780             this.cancelEdit();
32781             return;
32782         } 
32783         this.fireEvent('specialkey', field, e);
32784     
32785     },
32786
32787     /**
32788      * Starts the editing process and shows the editor.
32789      * @param {String/HTMLElement/Element} el The element to edit
32790      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32791       * to the innerHTML of el.
32792      */
32793     startEdit : function(el, value){
32794         if(this.editing){
32795             this.completeEdit();
32796         }
32797         this.boundEl = Roo.get(el);
32798         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32799         if(!this.rendered){
32800             this.render(this.parentEl || document.body);
32801         }
32802         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32803             return;
32804         }
32805         this.startValue = v;
32806         this.field.setValue(v);
32807         if(this.autoSize){
32808             var sz = this.boundEl.getSize();
32809             switch(this.autoSize){
32810                 case "width":
32811                 this.setSize(sz.width,  "");
32812                 break;
32813                 case "height":
32814                 this.setSize("",  sz.height);
32815                 break;
32816                 default:
32817                 this.setSize(sz.width,  sz.height);
32818             }
32819         }
32820         this.el.alignTo(this.boundEl, this.alignment);
32821         this.editing = true;
32822         if(Roo.QuickTips){
32823             Roo.QuickTips.disable();
32824         }
32825         this.show();
32826     },
32827
32828     /**
32829      * Sets the height and width of this editor.
32830      * @param {Number} width The new width
32831      * @param {Number} height The new height
32832      */
32833     setSize : function(w, h){
32834         this.field.setSize(w, h);
32835         if(this.el){
32836             this.el.sync();
32837         }
32838     },
32839
32840     /**
32841      * Realigns the editor to the bound field based on the current alignment config value.
32842      */
32843     realign : function(){
32844         this.el.alignTo(this.boundEl, this.alignment);
32845     },
32846
32847     /**
32848      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32849      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32850      */
32851     completeEdit : function(remainVisible){
32852         if(!this.editing){
32853             return;
32854         }
32855         var v = this.getValue();
32856         if(this.revertInvalid !== false && !this.field.isValid()){
32857             v = this.startValue;
32858             this.cancelEdit(true);
32859         }
32860         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32861             this.editing = false;
32862             this.hide();
32863             return;
32864         }
32865         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32866             this.editing = false;
32867             if(this.updateEl && this.boundEl){
32868                 this.boundEl.update(v);
32869             }
32870             if(remainVisible !== true){
32871                 this.hide();
32872             }
32873             this.fireEvent("complete", this, v, this.startValue);
32874         }
32875     },
32876
32877     // private
32878     onShow : function(){
32879         this.el.show();
32880         if(this.hideEl !== false){
32881             this.boundEl.hide();
32882         }
32883         this.field.show();
32884         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32885             this.fixIEFocus = true;
32886             this.deferredFocus.defer(50, this);
32887         }else{
32888             this.field.focus();
32889         }
32890         this.fireEvent("startedit", this.boundEl, this.startValue);
32891     },
32892
32893     deferredFocus : function(){
32894         if(this.editing){
32895             this.field.focus();
32896         }
32897     },
32898
32899     /**
32900      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32901      * reverted to the original starting value.
32902      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32903      * cancel (defaults to false)
32904      */
32905     cancelEdit : function(remainVisible){
32906         if(this.editing){
32907             this.setValue(this.startValue);
32908             if(remainVisible !== true){
32909                 this.hide();
32910             }
32911         }
32912     },
32913
32914     // private
32915     onBlur : function(){
32916         if(this.allowBlur !== true && this.editing){
32917             this.completeEdit();
32918         }
32919     },
32920
32921     // private
32922     onHide : function(){
32923         if(this.editing){
32924             this.completeEdit();
32925             return;
32926         }
32927         this.field.blur();
32928         if(this.field.collapse){
32929             this.field.collapse();
32930         }
32931         this.el.hide();
32932         if(this.hideEl !== false){
32933             this.boundEl.show();
32934         }
32935         if(Roo.QuickTips){
32936             Roo.QuickTips.enable();
32937         }
32938     },
32939
32940     /**
32941      * Sets the data value of the editor
32942      * @param {Mixed} value Any valid value supported by the underlying field
32943      */
32944     setValue : function(v){
32945         this.field.setValue(v);
32946     },
32947
32948     /**
32949      * Gets the data value of the editor
32950      * @return {Mixed} The data value
32951      */
32952     getValue : function(){
32953         return this.field.getValue();
32954     }
32955 });/*
32956  * Based on:
32957  * Ext JS Library 1.1.1
32958  * Copyright(c) 2006-2007, Ext JS, LLC.
32959  *
32960  * Originally Released Under LGPL - original licence link has changed is not relivant.
32961  *
32962  * Fork - LGPL
32963  * <script type="text/javascript">
32964  */
32965  
32966 /**
32967  * @class Roo.BasicDialog
32968  * @extends Roo.util.Observable
32969  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32970  * <pre><code>
32971 var dlg = new Roo.BasicDialog("my-dlg", {
32972     height: 200,
32973     width: 300,
32974     minHeight: 100,
32975     minWidth: 150,
32976     modal: true,
32977     proxyDrag: true,
32978     shadow: true
32979 });
32980 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32981 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32982 dlg.addButton('Cancel', dlg.hide, dlg);
32983 dlg.show();
32984 </code></pre>
32985   <b>A Dialog should always be a direct child of the body element.</b>
32986  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32987  * @cfg {String} title Default text to display in the title bar (defaults to null)
32988  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32989  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32990  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32991  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32992  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32993  * (defaults to null with no animation)
32994  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32995  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32996  * property for valid values (defaults to 'all')
32997  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32998  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32999  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33000  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33001  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33002  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33003  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33004  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33005  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33006  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33007  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33008  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33009  * draggable = true (defaults to false)
33010  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33011  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33012  * shadow (defaults to false)
33013  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33014  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33015  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33016  * @cfg {Array} buttons Array of buttons
33017  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33018  * @constructor
33019  * Create a new BasicDialog.
33020  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33021  * @param {Object} config Configuration options
33022  */
33023 Roo.BasicDialog = function(el, config){
33024     this.el = Roo.get(el);
33025     var dh = Roo.DomHelper;
33026     if(!this.el && config && config.autoCreate){
33027         if(typeof config.autoCreate == "object"){
33028             if(!config.autoCreate.id){
33029                 config.autoCreate.id = el;
33030             }
33031             this.el = dh.append(document.body,
33032                         config.autoCreate, true);
33033         }else{
33034             this.el = dh.append(document.body,
33035                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33036         }
33037     }
33038     el = this.el;
33039     el.setDisplayed(true);
33040     el.hide = this.hideAction;
33041     this.id = el.id;
33042     el.addClass("x-dlg");
33043
33044     Roo.apply(this, config);
33045
33046     this.proxy = el.createProxy("x-dlg-proxy");
33047     this.proxy.hide = this.hideAction;
33048     this.proxy.setOpacity(.5);
33049     this.proxy.hide();
33050
33051     if(config.width){
33052         el.setWidth(config.width);
33053     }
33054     if(config.height){
33055         el.setHeight(config.height);
33056     }
33057     this.size = el.getSize();
33058     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33059         this.xy = [config.x,config.y];
33060     }else{
33061         this.xy = el.getCenterXY(true);
33062     }
33063     /** The header element @type Roo.Element */
33064     this.header = el.child("> .x-dlg-hd");
33065     /** The body element @type Roo.Element */
33066     this.body = el.child("> .x-dlg-bd");
33067     /** The footer element @type Roo.Element */
33068     this.footer = el.child("> .x-dlg-ft");
33069
33070     if(!this.header){
33071         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33072     }
33073     if(!this.body){
33074         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33075     }
33076
33077     this.header.unselectable();
33078     if(this.title){
33079         this.header.update(this.title);
33080     }
33081     // this element allows the dialog to be focused for keyboard event
33082     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33083     this.focusEl.swallowEvent("click", true);
33084
33085     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33086
33087     // wrap the body and footer for special rendering
33088     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33089     if(this.footer){
33090         this.bwrap.dom.appendChild(this.footer.dom);
33091     }
33092
33093     this.bg = this.el.createChild({
33094         tag: "div", cls:"x-dlg-bg",
33095         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33096     });
33097     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33098
33099
33100     if(this.autoScroll !== false && !this.autoTabs){
33101         this.body.setStyle("overflow", "auto");
33102     }
33103
33104     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33105
33106     if(this.closable !== false){
33107         this.el.addClass("x-dlg-closable");
33108         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33109         this.close.on("click", this.closeClick, this);
33110         this.close.addClassOnOver("x-dlg-close-over");
33111     }
33112     if(this.collapsible !== false){
33113         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33114         this.collapseBtn.on("click", this.collapseClick, this);
33115         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33116         this.header.on("dblclick", this.collapseClick, this);
33117     }
33118     if(this.resizable !== false){
33119         this.el.addClass("x-dlg-resizable");
33120         this.resizer = new Roo.Resizable(el, {
33121             minWidth: this.minWidth || 80,
33122             minHeight:this.minHeight || 80,
33123             handles: this.resizeHandles || "all",
33124             pinned: true
33125         });
33126         this.resizer.on("beforeresize", this.beforeResize, this);
33127         this.resizer.on("resize", this.onResize, this);
33128     }
33129     if(this.draggable !== false){
33130         el.addClass("x-dlg-draggable");
33131         if (!this.proxyDrag) {
33132             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33133         }
33134         else {
33135             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33136         }
33137         dd.setHandleElId(this.header.id);
33138         dd.endDrag = this.endMove.createDelegate(this);
33139         dd.startDrag = this.startMove.createDelegate(this);
33140         dd.onDrag = this.onDrag.createDelegate(this);
33141         dd.scroll = false;
33142         this.dd = dd;
33143     }
33144     if(this.modal){
33145         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33146         this.mask.enableDisplayMode("block");
33147         this.mask.hide();
33148         this.el.addClass("x-dlg-modal");
33149     }
33150     if(this.shadow){
33151         this.shadow = new Roo.Shadow({
33152             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33153             offset : this.shadowOffset
33154         });
33155     }else{
33156         this.shadowOffset = 0;
33157     }
33158     if(Roo.useShims && this.shim !== false){
33159         this.shim = this.el.createShim();
33160         this.shim.hide = this.hideAction;
33161         this.shim.hide();
33162     }else{
33163         this.shim = false;
33164     }
33165     if(this.autoTabs){
33166         this.initTabs();
33167     }
33168     if (this.buttons) { 
33169         var bts= this.buttons;
33170         this.buttons = [];
33171         Roo.each(bts, function(b) {
33172             this.addButton(b);
33173         }, this);
33174     }
33175     
33176     
33177     this.addEvents({
33178         /**
33179          * @event keydown
33180          * Fires when a key is pressed
33181          * @param {Roo.BasicDialog} this
33182          * @param {Roo.EventObject} e
33183          */
33184         "keydown" : true,
33185         /**
33186          * @event move
33187          * Fires when this dialog is moved by the user.
33188          * @param {Roo.BasicDialog} this
33189          * @param {Number} x The new page X
33190          * @param {Number} y The new page Y
33191          */
33192         "move" : true,
33193         /**
33194          * @event resize
33195          * Fires when this dialog is resized by the user.
33196          * @param {Roo.BasicDialog} this
33197          * @param {Number} width The new width
33198          * @param {Number} height The new height
33199          */
33200         "resize" : true,
33201         /**
33202          * @event beforehide
33203          * Fires before this dialog is hidden.
33204          * @param {Roo.BasicDialog} this
33205          */
33206         "beforehide" : true,
33207         /**
33208          * @event hide
33209          * Fires when this dialog is hidden.
33210          * @param {Roo.BasicDialog} this
33211          */
33212         "hide" : true,
33213         /**
33214          * @event beforeshow
33215          * Fires before this dialog is shown.
33216          * @param {Roo.BasicDialog} this
33217          */
33218         "beforeshow" : true,
33219         /**
33220          * @event show
33221          * Fires when this dialog is shown.
33222          * @param {Roo.BasicDialog} this
33223          */
33224         "show" : true
33225     });
33226     el.on("keydown", this.onKeyDown, this);
33227     el.on("mousedown", this.toFront, this);
33228     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33229     this.el.hide();
33230     Roo.DialogManager.register(this);
33231     Roo.BasicDialog.superclass.constructor.call(this);
33232 };
33233
33234 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33235     shadowOffset: Roo.isIE ? 6 : 5,
33236     minHeight: 80,
33237     minWidth: 200,
33238     minButtonWidth: 75,
33239     defaultButton: null,
33240     buttonAlign: "right",
33241     tabTag: 'div',
33242     firstShow: true,
33243
33244     /**
33245      * Sets the dialog title text
33246      * @param {String} text The title text to display
33247      * @return {Roo.BasicDialog} this
33248      */
33249     setTitle : function(text){
33250         this.header.update(text);
33251         return this;
33252     },
33253
33254     // private
33255     closeClick : function(){
33256         this.hide();
33257     },
33258
33259     // private
33260     collapseClick : function(){
33261         this[this.collapsed ? "expand" : "collapse"]();
33262     },
33263
33264     /**
33265      * Collapses the dialog to its minimized state (only the title bar is visible).
33266      * Equivalent to the user clicking the collapse dialog button.
33267      */
33268     collapse : function(){
33269         if(!this.collapsed){
33270             this.collapsed = true;
33271             this.el.addClass("x-dlg-collapsed");
33272             this.restoreHeight = this.el.getHeight();
33273             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33274         }
33275     },
33276
33277     /**
33278      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33279      * clicking the expand dialog button.
33280      */
33281     expand : function(){
33282         if(this.collapsed){
33283             this.collapsed = false;
33284             this.el.removeClass("x-dlg-collapsed");
33285             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33286         }
33287     },
33288
33289     /**
33290      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33291      * @return {Roo.TabPanel} The tabs component
33292      */
33293     initTabs : function(){
33294         var tabs = this.getTabs();
33295         while(tabs.getTab(0)){
33296             tabs.removeTab(0);
33297         }
33298         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33299             var dom = el.dom;
33300             tabs.addTab(Roo.id(dom), dom.title);
33301             dom.title = "";
33302         });
33303         tabs.activate(0);
33304         return tabs;
33305     },
33306
33307     // private
33308     beforeResize : function(){
33309         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33310     },
33311
33312     // private
33313     onResize : function(){
33314         this.refreshSize();
33315         this.syncBodyHeight();
33316         this.adjustAssets();
33317         this.focus();
33318         this.fireEvent("resize", this, this.size.width, this.size.height);
33319     },
33320
33321     // private
33322     onKeyDown : function(e){
33323         if(this.isVisible()){
33324             this.fireEvent("keydown", this, e);
33325         }
33326     },
33327
33328     /**
33329      * Resizes the dialog.
33330      * @param {Number} width
33331      * @param {Number} height
33332      * @return {Roo.BasicDialog} this
33333      */
33334     resizeTo : function(width, height){
33335         this.el.setSize(width, height);
33336         this.size = {width: width, height: height};
33337         this.syncBodyHeight();
33338         if(this.fixedcenter){
33339             this.center();
33340         }
33341         if(this.isVisible()){
33342             this.constrainXY();
33343             this.adjustAssets();
33344         }
33345         this.fireEvent("resize", this, width, height);
33346         return this;
33347     },
33348
33349
33350     /**
33351      * Resizes the dialog to fit the specified content size.
33352      * @param {Number} width
33353      * @param {Number} height
33354      * @return {Roo.BasicDialog} this
33355      */
33356     setContentSize : function(w, h){
33357         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33358         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33359         //if(!this.el.isBorderBox()){
33360             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33361             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33362         //}
33363         if(this.tabs){
33364             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33365             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33366         }
33367         this.resizeTo(w, h);
33368         return this;
33369     },
33370
33371     /**
33372      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33373      * executed in response to a particular key being pressed while the dialog is active.
33374      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33375      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33376      * @param {Function} fn The function to call
33377      * @param {Object} scope (optional) The scope of the function
33378      * @return {Roo.BasicDialog} this
33379      */
33380     addKeyListener : function(key, fn, scope){
33381         var keyCode, shift, ctrl, alt;
33382         if(typeof key == "object" && !(key instanceof Array)){
33383             keyCode = key["key"];
33384             shift = key["shift"];
33385             ctrl = key["ctrl"];
33386             alt = key["alt"];
33387         }else{
33388             keyCode = key;
33389         }
33390         var handler = function(dlg, e){
33391             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33392                 var k = e.getKey();
33393                 if(keyCode instanceof Array){
33394                     for(var i = 0, len = keyCode.length; i < len; i++){
33395                         if(keyCode[i] == k){
33396                           fn.call(scope || window, dlg, k, e);
33397                           return;
33398                         }
33399                     }
33400                 }else{
33401                     if(k == keyCode){
33402                         fn.call(scope || window, dlg, k, e);
33403                     }
33404                 }
33405             }
33406         };
33407         this.on("keydown", handler);
33408         return this;
33409     },
33410
33411     /**
33412      * Returns the TabPanel component (creates it if it doesn't exist).
33413      * Note: If you wish to simply check for the existence of tabs without creating them,
33414      * check for a null 'tabs' property.
33415      * @return {Roo.TabPanel} The tabs component
33416      */
33417     getTabs : function(){
33418         if(!this.tabs){
33419             this.el.addClass("x-dlg-auto-tabs");
33420             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33421             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33422         }
33423         return this.tabs;
33424     },
33425
33426     /**
33427      * Adds a button to the footer section of the dialog.
33428      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33429      * object or a valid Roo.DomHelper element config
33430      * @param {Function} handler The function called when the button is clicked
33431      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33432      * @return {Roo.Button} The new button
33433      */
33434     addButton : function(config, handler, scope){
33435         var dh = Roo.DomHelper;
33436         if(!this.footer){
33437             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33438         }
33439         if(!this.btnContainer){
33440             var tb = this.footer.createChild({
33441
33442                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33443                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33444             }, null, true);
33445             this.btnContainer = tb.firstChild.firstChild.firstChild;
33446         }
33447         var bconfig = {
33448             handler: handler,
33449             scope: scope,
33450             minWidth: this.minButtonWidth,
33451             hideParent:true
33452         };
33453         if(typeof config == "string"){
33454             bconfig.text = config;
33455         }else{
33456             if(config.tag){
33457                 bconfig.dhconfig = config;
33458             }else{
33459                 Roo.apply(bconfig, config);
33460             }
33461         }
33462         var fc = false;
33463         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33464             bconfig.position = Math.max(0, bconfig.position);
33465             fc = this.btnContainer.childNodes[bconfig.position];
33466         }
33467          
33468         var btn = new Roo.Button(
33469             fc ? 
33470                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33471                 : this.btnContainer.appendChild(document.createElement("td")),
33472             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33473             bconfig
33474         );
33475         this.syncBodyHeight();
33476         if(!this.buttons){
33477             /**
33478              * Array of all the buttons that have been added to this dialog via addButton
33479              * @type Array
33480              */
33481             this.buttons = [];
33482         }
33483         this.buttons.push(btn);
33484         return btn;
33485     },
33486
33487     /**
33488      * Sets the default button to be focused when the dialog is displayed.
33489      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33490      * @return {Roo.BasicDialog} this
33491      */
33492     setDefaultButton : function(btn){
33493         this.defaultButton = btn;
33494         return this;
33495     },
33496
33497     // private
33498     getHeaderFooterHeight : function(safe){
33499         var height = 0;
33500         if(this.header){
33501            height += this.header.getHeight();
33502         }
33503         if(this.footer){
33504            var fm = this.footer.getMargins();
33505             height += (this.footer.getHeight()+fm.top+fm.bottom);
33506         }
33507         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33508         height += this.centerBg.getPadding("tb");
33509         return height;
33510     },
33511
33512     // private
33513     syncBodyHeight : function()
33514     {
33515         var bd = this.body, // the text
33516             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33517             bw = this.bwrap;
33518         var height = this.size.height - this.getHeaderFooterHeight(false);
33519         bd.setHeight(height-bd.getMargins("tb"));
33520         var hh = this.header.getHeight();
33521         var h = this.size.height-hh;
33522         cb.setHeight(h);
33523         
33524         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33525         bw.setHeight(h-cb.getPadding("tb"));
33526         
33527         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33528         bd.setWidth(bw.getWidth(true));
33529         if(this.tabs){
33530             this.tabs.syncHeight();
33531             if(Roo.isIE){
33532                 this.tabs.el.repaint();
33533             }
33534         }
33535     },
33536
33537     /**
33538      * Restores the previous state of the dialog if Roo.state is configured.
33539      * @return {Roo.BasicDialog} this
33540      */
33541     restoreState : function(){
33542         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33543         if(box && box.width){
33544             this.xy = [box.x, box.y];
33545             this.resizeTo(box.width, box.height);
33546         }
33547         return this;
33548     },
33549
33550     // private
33551     beforeShow : function(){
33552         this.expand();
33553         if(this.fixedcenter){
33554             this.xy = this.el.getCenterXY(true);
33555         }
33556         if(this.modal){
33557             Roo.get(document.body).addClass("x-body-masked");
33558             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33559             this.mask.show();
33560         }
33561         this.constrainXY();
33562     },
33563
33564     // private
33565     animShow : function(){
33566         var b = Roo.get(this.animateTarget).getBox();
33567         this.proxy.setSize(b.width, b.height);
33568         this.proxy.setLocation(b.x, b.y);
33569         this.proxy.show();
33570         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33571                     true, .35, this.showEl.createDelegate(this));
33572     },
33573
33574     /**
33575      * Shows the dialog.
33576      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33577      * @return {Roo.BasicDialog} this
33578      */
33579     show : function(animateTarget){
33580         if (this.fireEvent("beforeshow", this) === false){
33581             return;
33582         }
33583         if(this.syncHeightBeforeShow){
33584             this.syncBodyHeight();
33585         }else if(this.firstShow){
33586             this.firstShow = false;
33587             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33588         }
33589         this.animateTarget = animateTarget || this.animateTarget;
33590         if(!this.el.isVisible()){
33591             this.beforeShow();
33592             if(this.animateTarget && Roo.get(this.animateTarget)){
33593                 this.animShow();
33594             }else{
33595                 this.showEl();
33596             }
33597         }
33598         return this;
33599     },
33600
33601     // private
33602     showEl : function(){
33603         this.proxy.hide();
33604         this.el.setXY(this.xy);
33605         this.el.show();
33606         this.adjustAssets(true);
33607         this.toFront();
33608         this.focus();
33609         // IE peekaboo bug - fix found by Dave Fenwick
33610         if(Roo.isIE){
33611             this.el.repaint();
33612         }
33613         this.fireEvent("show", this);
33614     },
33615
33616     /**
33617      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33618      * dialog itself will receive focus.
33619      */
33620     focus : function(){
33621         if(this.defaultButton){
33622             this.defaultButton.focus();
33623         }else{
33624             this.focusEl.focus();
33625         }
33626     },
33627
33628     // private
33629     constrainXY : function(){
33630         if(this.constraintoviewport !== false){
33631             if(!this.viewSize){
33632                 if(this.container){
33633                     var s = this.container.getSize();
33634                     this.viewSize = [s.width, s.height];
33635                 }else{
33636                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33637                 }
33638             }
33639             var s = Roo.get(this.container||document).getScroll();
33640
33641             var x = this.xy[0], y = this.xy[1];
33642             var w = this.size.width, h = this.size.height;
33643             var vw = this.viewSize[0], vh = this.viewSize[1];
33644             // only move it if it needs it
33645             var moved = false;
33646             // first validate right/bottom
33647             if(x + w > vw+s.left){
33648                 x = vw - w;
33649                 moved = true;
33650             }
33651             if(y + h > vh+s.top){
33652                 y = vh - h;
33653                 moved = true;
33654             }
33655             // then make sure top/left isn't negative
33656             if(x < s.left){
33657                 x = s.left;
33658                 moved = true;
33659             }
33660             if(y < s.top){
33661                 y = s.top;
33662                 moved = true;
33663             }
33664             if(moved){
33665                 // cache xy
33666                 this.xy = [x, y];
33667                 if(this.isVisible()){
33668                     this.el.setLocation(x, y);
33669                     this.adjustAssets();
33670                 }
33671             }
33672         }
33673     },
33674
33675     // private
33676     onDrag : function(){
33677         if(!this.proxyDrag){
33678             this.xy = this.el.getXY();
33679             this.adjustAssets();
33680         }
33681     },
33682
33683     // private
33684     adjustAssets : function(doShow){
33685         var x = this.xy[0], y = this.xy[1];
33686         var w = this.size.width, h = this.size.height;
33687         if(doShow === true){
33688             if(this.shadow){
33689                 this.shadow.show(this.el);
33690             }
33691             if(this.shim){
33692                 this.shim.show();
33693             }
33694         }
33695         if(this.shadow && this.shadow.isVisible()){
33696             this.shadow.show(this.el);
33697         }
33698         if(this.shim && this.shim.isVisible()){
33699             this.shim.setBounds(x, y, w, h);
33700         }
33701     },
33702
33703     // private
33704     adjustViewport : function(w, h){
33705         if(!w || !h){
33706             w = Roo.lib.Dom.getViewWidth();
33707             h = Roo.lib.Dom.getViewHeight();
33708         }
33709         // cache the size
33710         this.viewSize = [w, h];
33711         if(this.modal && this.mask.isVisible()){
33712             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33713             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33714         }
33715         if(this.isVisible()){
33716             this.constrainXY();
33717         }
33718     },
33719
33720     /**
33721      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33722      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33723      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33724      */
33725     destroy : function(removeEl){
33726         if(this.isVisible()){
33727             this.animateTarget = null;
33728             this.hide();
33729         }
33730         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33731         if(this.tabs){
33732             this.tabs.destroy(removeEl);
33733         }
33734         Roo.destroy(
33735              this.shim,
33736              this.proxy,
33737              this.resizer,
33738              this.close,
33739              this.mask
33740         );
33741         if(this.dd){
33742             this.dd.unreg();
33743         }
33744         if(this.buttons){
33745            for(var i = 0, len = this.buttons.length; i < len; i++){
33746                this.buttons[i].destroy();
33747            }
33748         }
33749         this.el.removeAllListeners();
33750         if(removeEl === true){
33751             this.el.update("");
33752             this.el.remove();
33753         }
33754         Roo.DialogManager.unregister(this);
33755     },
33756
33757     // private
33758     startMove : function(){
33759         if(this.proxyDrag){
33760             this.proxy.show();
33761         }
33762         if(this.constraintoviewport !== false){
33763             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33764         }
33765     },
33766
33767     // private
33768     endMove : function(){
33769         if(!this.proxyDrag){
33770             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33771         }else{
33772             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33773             this.proxy.hide();
33774         }
33775         this.refreshSize();
33776         this.adjustAssets();
33777         this.focus();
33778         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33779     },
33780
33781     /**
33782      * Brings this dialog to the front of any other visible dialogs
33783      * @return {Roo.BasicDialog} this
33784      */
33785     toFront : function(){
33786         Roo.DialogManager.bringToFront(this);
33787         return this;
33788     },
33789
33790     /**
33791      * Sends this dialog to the back (under) of any other visible dialogs
33792      * @return {Roo.BasicDialog} this
33793      */
33794     toBack : function(){
33795         Roo.DialogManager.sendToBack(this);
33796         return this;
33797     },
33798
33799     /**
33800      * Centers this dialog in the viewport
33801      * @return {Roo.BasicDialog} this
33802      */
33803     center : function(){
33804         var xy = this.el.getCenterXY(true);
33805         this.moveTo(xy[0], xy[1]);
33806         return this;
33807     },
33808
33809     /**
33810      * Moves the dialog's top-left corner to the specified point
33811      * @param {Number} x
33812      * @param {Number} y
33813      * @return {Roo.BasicDialog} this
33814      */
33815     moveTo : function(x, y){
33816         this.xy = [x,y];
33817         if(this.isVisible()){
33818             this.el.setXY(this.xy);
33819             this.adjustAssets();
33820         }
33821         return this;
33822     },
33823
33824     /**
33825      * Aligns the dialog to the specified element
33826      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33827      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33828      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33829      * @return {Roo.BasicDialog} this
33830      */
33831     alignTo : function(element, position, offsets){
33832         this.xy = this.el.getAlignToXY(element, position, offsets);
33833         if(this.isVisible()){
33834             this.el.setXY(this.xy);
33835             this.adjustAssets();
33836         }
33837         return this;
33838     },
33839
33840     /**
33841      * Anchors an element to another element and realigns it when the window is resized.
33842      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33843      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33844      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33845      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33846      * is a number, it is used as the buffer delay (defaults to 50ms).
33847      * @return {Roo.BasicDialog} this
33848      */
33849     anchorTo : function(el, alignment, offsets, monitorScroll){
33850         var action = function(){
33851             this.alignTo(el, alignment, offsets);
33852         };
33853         Roo.EventManager.onWindowResize(action, this);
33854         var tm = typeof monitorScroll;
33855         if(tm != 'undefined'){
33856             Roo.EventManager.on(window, 'scroll', action, this,
33857                 {buffer: tm == 'number' ? monitorScroll : 50});
33858         }
33859         action.call(this);
33860         return this;
33861     },
33862
33863     /**
33864      * Returns true if the dialog is visible
33865      * @return {Boolean}
33866      */
33867     isVisible : function(){
33868         return this.el.isVisible();
33869     },
33870
33871     // private
33872     animHide : function(callback){
33873         var b = Roo.get(this.animateTarget).getBox();
33874         this.proxy.show();
33875         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33876         this.el.hide();
33877         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33878                     this.hideEl.createDelegate(this, [callback]));
33879     },
33880
33881     /**
33882      * Hides the dialog.
33883      * @param {Function} callback (optional) Function to call when the dialog is hidden
33884      * @return {Roo.BasicDialog} this
33885      */
33886     hide : function(callback){
33887         if (this.fireEvent("beforehide", this) === false){
33888             return;
33889         }
33890         if(this.shadow){
33891             this.shadow.hide();
33892         }
33893         if(this.shim) {
33894           this.shim.hide();
33895         }
33896         // sometimes animateTarget seems to get set.. causing problems...
33897         // this just double checks..
33898         if(this.animateTarget && Roo.get(this.animateTarget)) {
33899            this.animHide(callback);
33900         }else{
33901             this.el.hide();
33902             this.hideEl(callback);
33903         }
33904         return this;
33905     },
33906
33907     // private
33908     hideEl : function(callback){
33909         this.proxy.hide();
33910         if(this.modal){
33911             this.mask.hide();
33912             Roo.get(document.body).removeClass("x-body-masked");
33913         }
33914         this.fireEvent("hide", this);
33915         if(typeof callback == "function"){
33916             callback();
33917         }
33918     },
33919
33920     // private
33921     hideAction : function(){
33922         this.setLeft("-10000px");
33923         this.setTop("-10000px");
33924         this.setStyle("visibility", "hidden");
33925     },
33926
33927     // private
33928     refreshSize : function(){
33929         this.size = this.el.getSize();
33930         this.xy = this.el.getXY();
33931         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33932     },
33933
33934     // private
33935     // z-index is managed by the DialogManager and may be overwritten at any time
33936     setZIndex : function(index){
33937         if(this.modal){
33938             this.mask.setStyle("z-index", index);
33939         }
33940         if(this.shim){
33941             this.shim.setStyle("z-index", ++index);
33942         }
33943         if(this.shadow){
33944             this.shadow.setZIndex(++index);
33945         }
33946         this.el.setStyle("z-index", ++index);
33947         if(this.proxy){
33948             this.proxy.setStyle("z-index", ++index);
33949         }
33950         if(this.resizer){
33951             this.resizer.proxy.setStyle("z-index", ++index);
33952         }
33953
33954         this.lastZIndex = index;
33955     },
33956
33957     /**
33958      * Returns the element for this dialog
33959      * @return {Roo.Element} The underlying dialog Element
33960      */
33961     getEl : function(){
33962         return this.el;
33963     }
33964 });
33965
33966 /**
33967  * @class Roo.DialogManager
33968  * Provides global access to BasicDialogs that have been created and
33969  * support for z-indexing (layering) multiple open dialogs.
33970  */
33971 Roo.DialogManager = function(){
33972     var list = {};
33973     var accessList = [];
33974     var front = null;
33975
33976     // private
33977     var sortDialogs = function(d1, d2){
33978         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33979     };
33980
33981     // private
33982     var orderDialogs = function(){
33983         accessList.sort(sortDialogs);
33984         var seed = Roo.DialogManager.zseed;
33985         for(var i = 0, len = accessList.length; i < len; i++){
33986             var dlg = accessList[i];
33987             if(dlg){
33988                 dlg.setZIndex(seed + (i*10));
33989             }
33990         }
33991     };
33992
33993     return {
33994         /**
33995          * The starting z-index for BasicDialogs (defaults to 9000)
33996          * @type Number The z-index value
33997          */
33998         zseed : 9000,
33999
34000         // private
34001         register : function(dlg){
34002             list[dlg.id] = dlg;
34003             accessList.push(dlg);
34004         },
34005
34006         // private
34007         unregister : function(dlg){
34008             delete list[dlg.id];
34009             var i=0;
34010             var len=0;
34011             if(!accessList.indexOf){
34012                 for(  i = 0, len = accessList.length; i < len; i++){
34013                     if(accessList[i] == dlg){
34014                         accessList.splice(i, 1);
34015                         return;
34016                     }
34017                 }
34018             }else{
34019                  i = accessList.indexOf(dlg);
34020                 if(i != -1){
34021                     accessList.splice(i, 1);
34022                 }
34023             }
34024         },
34025
34026         /**
34027          * Gets a registered dialog by id
34028          * @param {String/Object} id The id of the dialog or a dialog
34029          * @return {Roo.BasicDialog} this
34030          */
34031         get : function(id){
34032             return typeof id == "object" ? id : list[id];
34033         },
34034
34035         /**
34036          * Brings the specified dialog to the front
34037          * @param {String/Object} dlg The id of the dialog or a dialog
34038          * @return {Roo.BasicDialog} this
34039          */
34040         bringToFront : function(dlg){
34041             dlg = this.get(dlg);
34042             if(dlg != front){
34043                 front = dlg;
34044                 dlg._lastAccess = new Date().getTime();
34045                 orderDialogs();
34046             }
34047             return dlg;
34048         },
34049
34050         /**
34051          * Sends the specified dialog to the back
34052          * @param {String/Object} dlg The id of the dialog or a dialog
34053          * @return {Roo.BasicDialog} this
34054          */
34055         sendToBack : function(dlg){
34056             dlg = this.get(dlg);
34057             dlg._lastAccess = -(new Date().getTime());
34058             orderDialogs();
34059             return dlg;
34060         },
34061
34062         /**
34063          * Hides all dialogs
34064          */
34065         hideAll : function(){
34066             for(var id in list){
34067                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34068                     list[id].hide();
34069                 }
34070             }
34071         }
34072     };
34073 }();
34074
34075 /**
34076  * @class Roo.LayoutDialog
34077  * @extends Roo.BasicDialog
34078  * @children Roo.ContentPanel
34079  * @parent builder none
34080  * Dialog which provides adjustments for working with a layout in a Dialog.
34081  * Add your necessary layout config options to the dialog's config.<br>
34082  * Example usage (including a nested layout):
34083  * <pre><code>
34084 if(!dialog){
34085     dialog = new Roo.LayoutDialog("download-dlg", {
34086         modal: true,
34087         width:600,
34088         height:450,
34089         shadow:true,
34090         minWidth:500,
34091         minHeight:350,
34092         autoTabs:true,
34093         proxyDrag:true,
34094         // layout config merges with the dialog config
34095         center:{
34096             tabPosition: "top",
34097             alwaysShowTabs: true
34098         }
34099     });
34100     dialog.addKeyListener(27, dialog.hide, dialog);
34101     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34102     dialog.addButton("Build It!", this.getDownload, this);
34103
34104     // we can even add nested layouts
34105     var innerLayout = new Roo.BorderLayout("dl-inner", {
34106         east: {
34107             initialSize: 200,
34108             autoScroll:true,
34109             split:true
34110         },
34111         center: {
34112             autoScroll:true
34113         }
34114     });
34115     innerLayout.beginUpdate();
34116     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34117     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34118     innerLayout.endUpdate(true);
34119
34120     var layout = dialog.getLayout();
34121     layout.beginUpdate();
34122     layout.add("center", new Roo.ContentPanel("standard-panel",
34123                         {title: "Download the Source", fitToFrame:true}));
34124     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34125                {title: "Build your own roo.js"}));
34126     layout.getRegion("center").showPanel(sp);
34127     layout.endUpdate();
34128 }
34129 </code></pre>
34130     * @constructor
34131     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34132     * @param {Object} config configuration options
34133   */
34134 Roo.LayoutDialog = function(el, cfg){
34135     
34136     var config=  cfg;
34137     if (typeof(cfg) == 'undefined') {
34138         config = Roo.apply({}, el);
34139         // not sure why we use documentElement here.. - it should always be body.
34140         // IE7 borks horribly if we use documentElement.
34141         // webkit also does not like documentElement - it creates a body element...
34142         el = Roo.get( document.body || document.documentElement ).createChild();
34143         //config.autoCreate = true;
34144     }
34145     
34146     
34147     config.autoTabs = false;
34148     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34149     this.body.setStyle({overflow:"hidden", position:"relative"});
34150     this.layout = new Roo.BorderLayout(this.body.dom, config);
34151     this.layout.monitorWindowResize = false;
34152     this.el.addClass("x-dlg-auto-layout");
34153     // fix case when center region overwrites center function
34154     this.center = Roo.BasicDialog.prototype.center;
34155     this.on("show", this.layout.layout, this.layout, true);
34156     if (config.items) {
34157         var xitems = config.items;
34158         delete config.items;
34159         Roo.each(xitems, this.addxtype, this);
34160     }
34161     
34162     
34163 };
34164 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34165     
34166     
34167     /**
34168      * @cfg {Roo.LayoutRegion} east  
34169      */
34170     /**
34171      * @cfg {Roo.LayoutRegion} west
34172      */
34173     /**
34174      * @cfg {Roo.LayoutRegion} south
34175      */
34176     /**
34177      * @cfg {Roo.LayoutRegion} north
34178      */
34179     /**
34180      * @cfg {Roo.LayoutRegion} center
34181      */
34182     /**
34183      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34184      */
34185     
34186     
34187     /**
34188      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34189      * @deprecated
34190      */
34191     endUpdate : function(){
34192         this.layout.endUpdate();
34193     },
34194
34195     /**
34196      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34197      *  @deprecated
34198      */
34199     beginUpdate : function(){
34200         this.layout.beginUpdate();
34201     },
34202
34203     /**
34204      * Get the BorderLayout for this dialog
34205      * @return {Roo.BorderLayout}
34206      */
34207     getLayout : function(){
34208         return this.layout;
34209     },
34210
34211     showEl : function(){
34212         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34213         if(Roo.isIE7){
34214             this.layout.layout();
34215         }
34216     },
34217
34218     // private
34219     // Use the syncHeightBeforeShow config option to control this automatically
34220     syncBodyHeight : function(){
34221         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34222         if(this.layout){this.layout.layout();}
34223     },
34224     
34225       /**
34226      * Add an xtype element (actually adds to the layout.)
34227      * @return {Object} xdata xtype object data.
34228      */
34229     
34230     addxtype : function(c) {
34231         return this.layout.addxtype(c);
34232     }
34233 });/*
34234  * Based on:
34235  * Ext JS Library 1.1.1
34236  * Copyright(c) 2006-2007, Ext JS, LLC.
34237  *
34238  * Originally Released Under LGPL - original licence link has changed is not relivant.
34239  *
34240  * Fork - LGPL
34241  * <script type="text/javascript">
34242  */
34243  
34244 /**
34245  * @class Roo.MessageBox
34246  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34247  * Example usage:
34248  *<pre><code>
34249 // Basic alert:
34250 Roo.Msg.alert('Status', 'Changes saved successfully.');
34251
34252 // Prompt for user data:
34253 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34254     if (btn == 'ok'){
34255         // process text value...
34256     }
34257 });
34258
34259 // Show a dialog using config options:
34260 Roo.Msg.show({
34261    title:'Save Changes?',
34262    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34263    buttons: Roo.Msg.YESNOCANCEL,
34264    fn: processResult,
34265    animEl: 'elId'
34266 });
34267 </code></pre>
34268  * @static
34269  */
34270 Roo.MessageBox = function(){
34271     var dlg, opt, mask, waitTimer;
34272     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34273     var buttons, activeTextEl, bwidth;
34274
34275     // private
34276     var handleButton = function(button){
34277         dlg.hide();
34278         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34279     };
34280
34281     // private
34282     var handleHide = function(){
34283         if(opt && opt.cls){
34284             dlg.el.removeClass(opt.cls);
34285         }
34286         if(waitTimer){
34287             Roo.TaskMgr.stop(waitTimer);
34288             waitTimer = null;
34289         }
34290     };
34291
34292     // private
34293     var updateButtons = function(b){
34294         var width = 0;
34295         if(!b){
34296             buttons["ok"].hide();
34297             buttons["cancel"].hide();
34298             buttons["yes"].hide();
34299             buttons["no"].hide();
34300             dlg.footer.dom.style.display = 'none';
34301             return width;
34302         }
34303         dlg.footer.dom.style.display = '';
34304         for(var k in buttons){
34305             if(typeof buttons[k] != "function"){
34306                 if(b[k]){
34307                     buttons[k].show();
34308                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34309                     width += buttons[k].el.getWidth()+15;
34310                 }else{
34311                     buttons[k].hide();
34312                 }
34313             }
34314         }
34315         return width;
34316     };
34317
34318     // private
34319     var handleEsc = function(d, k, e){
34320         if(opt && opt.closable !== false){
34321             dlg.hide();
34322         }
34323         if(e){
34324             e.stopEvent();
34325         }
34326     };
34327
34328     return {
34329         /**
34330          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34331          * @return {Roo.BasicDialog} The BasicDialog element
34332          */
34333         getDialog : function(){
34334            if(!dlg){
34335                 dlg = new Roo.BasicDialog("x-msg-box", {
34336                     autoCreate : true,
34337                     shadow: true,
34338                     draggable: true,
34339                     resizable:false,
34340                     constraintoviewport:false,
34341                     fixedcenter:true,
34342                     collapsible : false,
34343                     shim:true,
34344                     modal: true,
34345                     width:400, height:100,
34346                     buttonAlign:"center",
34347                     closeClick : function(){
34348                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34349                             handleButton("no");
34350                         }else{
34351                             handleButton("cancel");
34352                         }
34353                     }
34354                 });
34355                 dlg.on("hide", handleHide);
34356                 mask = dlg.mask;
34357                 dlg.addKeyListener(27, handleEsc);
34358                 buttons = {};
34359                 var bt = this.buttonText;
34360                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34361                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34362                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34363                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34364                 bodyEl = dlg.body.createChild({
34365
34366                     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>'
34367                 });
34368                 msgEl = bodyEl.dom.firstChild;
34369                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34370                 textboxEl.enableDisplayMode();
34371                 textboxEl.addKeyListener([10,13], function(){
34372                     if(dlg.isVisible() && opt && opt.buttons){
34373                         if(opt.buttons.ok){
34374                             handleButton("ok");
34375                         }else if(opt.buttons.yes){
34376                             handleButton("yes");
34377                         }
34378                     }
34379                 });
34380                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34381                 textareaEl.enableDisplayMode();
34382                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34383                 progressEl.enableDisplayMode();
34384                 var pf = progressEl.dom.firstChild;
34385                 if (pf) {
34386                     pp = Roo.get(pf.firstChild);
34387                     pp.setHeight(pf.offsetHeight);
34388                 }
34389                 
34390             }
34391             return dlg;
34392         },
34393
34394         /**
34395          * Updates the message box body text
34396          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34397          * the XHTML-compliant non-breaking space character '&amp;#160;')
34398          * @return {Roo.MessageBox} This message box
34399          */
34400         updateText : function(text){
34401             if(!dlg.isVisible() && !opt.width){
34402                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34403             }
34404             msgEl.innerHTML = text || '&#160;';
34405       
34406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34408             var w = Math.max(
34409                     Math.min(opt.width || cw , this.maxWidth), 
34410                     Math.max(opt.minWidth || this.minWidth, bwidth)
34411             );
34412             if(opt.prompt){
34413                 activeTextEl.setWidth(w);
34414             }
34415             if(dlg.isVisible()){
34416                 dlg.fixedcenter = false;
34417             }
34418             // to big, make it scroll. = But as usual stupid IE does not support
34419             // !important..
34420             
34421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34424             } else {
34425                 bodyEl.dom.style.height = '';
34426                 bodyEl.dom.style.overflowY = '';
34427             }
34428             if (cw > w) {
34429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34430             } else {
34431                 bodyEl.dom.style.overflowX = '';
34432             }
34433             
34434             dlg.setContentSize(w, bodyEl.getHeight());
34435             if(dlg.isVisible()){
34436                 dlg.fixedcenter = true;
34437             }
34438             return this;
34439         },
34440
34441         /**
34442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34446          * @return {Roo.MessageBox} This message box
34447          */
34448         updateProgress : function(value, text){
34449             if(text){
34450                 this.updateText(text);
34451             }
34452             if (pp) { // weird bug on my firefox - for some reason this is not defined
34453                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34454             }
34455             return this;
34456         },        
34457
34458         /**
34459          * Returns true if the message box is currently displayed
34460          * @return {Boolean} True if the message box is visible, else false
34461          */
34462         isVisible : function(){
34463             return dlg && dlg.isVisible();  
34464         },
34465
34466         /**
34467          * Hides the message box if it is displayed
34468          */
34469         hide : function(){
34470             if(this.isVisible()){
34471                 dlg.hide();
34472             }  
34473         },
34474
34475         /**
34476          * Displays a new message box, or reinitializes an existing message box, based on the config options
34477          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34478          * The following config object properties are supported:
34479          * <pre>
34480 Property    Type             Description
34481 ----------  ---------------  ------------------------------------------------------------------------------------
34482 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34483                                    closes (defaults to undefined)
34484 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34485                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34486 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34487                                    progress and wait dialogs will ignore this property and always hide the
34488                                    close button as they can only be closed programmatically.
34489 cls               String           A custom CSS class to apply to the message box element
34490 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34491                                    displayed (defaults to 75)
34492 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34493                                    function will be btn (the name of the button that was clicked, if applicable,
34494                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34495                                    Progress and wait dialogs will ignore this option since they do not respond to
34496                                    user actions and can only be closed programmatically, so any required function
34497                                    should be called by the same code after it closes the dialog.
34498 icon              String           A CSS class that provides a background image to be used as an icon for
34499                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34500 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34501 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34502 modal             Boolean          False to allow user interaction with the page while the message box is
34503                                    displayed (defaults to true)
34504 msg               String           A string that will replace the existing message box body text (defaults
34505                                    to the XHTML-compliant non-breaking space character '&#160;')
34506 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34507 progress          Boolean          True to display a progress bar (defaults to false)
34508 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34509 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34510 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34511 title             String           The title text
34512 value             String           The string value to set into the active textbox element if displayed
34513 wait              Boolean          True to display a progress bar (defaults to false)
34514 width             Number           The width of the dialog in pixels
34515 </pre>
34516          *
34517          * Example usage:
34518          * <pre><code>
34519 Roo.Msg.show({
34520    title: 'Address',
34521    msg: 'Please enter your address:',
34522    width: 300,
34523    buttons: Roo.MessageBox.OKCANCEL,
34524    multiline: true,
34525    fn: saveAddress,
34526    animEl: 'addAddressBtn'
34527 });
34528 </code></pre>
34529          * @param {Object} config Configuration options
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         show : function(options)
34533         {
34534             
34535             // this causes nightmares if you show one dialog after another
34536             // especially on callbacks..
34537              
34538             if(this.isVisible()){
34539                 
34540                 this.hide();
34541                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34542                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34543                 Roo.log("New Dialog Message:" +  options.msg )
34544                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34545                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34546                 
34547             }
34548             var d = this.getDialog();
34549             opt = options;
34550             d.setTitle(opt.title || "&#160;");
34551             d.close.setDisplayed(opt.closable !== false);
34552             activeTextEl = textboxEl;
34553             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34554             if(opt.prompt){
34555                 if(opt.multiline){
34556                     textboxEl.hide();
34557                     textareaEl.show();
34558                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34559                         opt.multiline : this.defaultTextHeight);
34560                     activeTextEl = textareaEl;
34561                 }else{
34562                     textboxEl.show();
34563                     textareaEl.hide();
34564                 }
34565             }else{
34566                 textboxEl.hide();
34567                 textareaEl.hide();
34568             }
34569             progressEl.setDisplayed(opt.progress === true);
34570             this.updateProgress(0);
34571             activeTextEl.dom.value = opt.value || "";
34572             if(opt.prompt){
34573                 dlg.setDefaultButton(activeTextEl);
34574             }else{
34575                 var bs = opt.buttons;
34576                 var db = null;
34577                 if(bs && bs.ok){
34578                     db = buttons["ok"];
34579                 }else if(bs && bs.yes){
34580                     db = buttons["yes"];
34581                 }
34582                 dlg.setDefaultButton(db);
34583             }
34584             bwidth = updateButtons(opt.buttons);
34585             this.updateText(opt.msg);
34586             if(opt.cls){
34587                 d.el.addClass(opt.cls);
34588             }
34589             d.proxyDrag = opt.proxyDrag === true;
34590             d.modal = opt.modal !== false;
34591             d.mask = opt.modal !== false ? mask : false;
34592             if(!d.isVisible()){
34593                 // force it to the end of the z-index stack so it gets a cursor in FF
34594                 document.body.appendChild(dlg.el.dom);
34595                 d.animateTarget = null;
34596                 d.show(options.animEl);
34597             }
34598             return this;
34599         },
34600
34601         /**
34602          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34603          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34604          * and closing the message box when the process is complete.
34605          * @param {String} title The title bar text
34606          * @param {String} msg The message box body text
34607          * @return {Roo.MessageBox} This message box
34608          */
34609         progress : function(title, msg){
34610             this.show({
34611                 title : title,
34612                 msg : msg,
34613                 buttons: false,
34614                 progress:true,
34615                 closable:false,
34616                 minWidth: this.minProgressWidth,
34617                 modal : true
34618             });
34619             return this;
34620         },
34621
34622         /**
34623          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34624          * If a callback function is passed it will be called after the user clicks the button, and the
34625          * id of the button that was clicked will be passed as the only parameter to the callback
34626          * (could also be the top-right close button).
34627          * @param {String} title The title bar text
34628          * @param {String} msg The message box body text
34629          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34630          * @param {Object} scope (optional) The scope of the callback function
34631          * @return {Roo.MessageBox} This message box
34632          */
34633         alert : function(title, msg, fn, scope){
34634             this.show({
34635                 title : title,
34636                 msg : msg,
34637                 buttons: this.OK,
34638                 fn: fn,
34639                 scope : scope,
34640                 modal : true
34641             });
34642             return this;
34643         },
34644
34645         /**
34646          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34647          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34648          * You are responsible for closing the message box when the process is complete.
34649          * @param {String} msg The message box body text
34650          * @param {String} title (optional) The title bar text
34651          * @return {Roo.MessageBox} This message box
34652          */
34653         wait : function(msg, title){
34654             this.show({
34655                 title : title,
34656                 msg : msg,
34657                 buttons: false,
34658                 closable:false,
34659                 progress:true,
34660                 modal:true,
34661                 width:300,
34662                 wait:true
34663             });
34664             waitTimer = Roo.TaskMgr.start({
34665                 run: function(i){
34666                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34667                 },
34668                 interval: 1000
34669             });
34670             return this;
34671         },
34672
34673         /**
34674          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34675          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34676          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34677          * @param {String} title The title bar text
34678          * @param {String} msg The message box body text
34679          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34680          * @param {Object} scope (optional) The scope of the callback function
34681          * @return {Roo.MessageBox} This message box
34682          */
34683         confirm : function(title, msg, fn, scope){
34684             this.show({
34685                 title : title,
34686                 msg : msg,
34687                 buttons: this.YESNO,
34688                 fn: fn,
34689                 scope : scope,
34690                 modal : true
34691             });
34692             return this;
34693         },
34694
34695         /**
34696          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34697          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34698          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34699          * (could also be the top-right close button) and the text that was entered will be passed as the two
34700          * parameters to the callback.
34701          * @param {String} title The title bar text
34702          * @param {String} msg The message box body text
34703          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34704          * @param {Object} scope (optional) The scope of the callback function
34705          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34706          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34707          * @return {Roo.MessageBox} This message box
34708          */
34709         prompt : function(title, msg, fn, scope, multiline){
34710             this.show({
34711                 title : title,
34712                 msg : msg,
34713                 buttons: this.OKCANCEL,
34714                 fn: fn,
34715                 minWidth:250,
34716                 scope : scope,
34717                 prompt:true,
34718                 multiline: multiline,
34719                 modal : true
34720             });
34721             return this;
34722         },
34723
34724         /**
34725          * Button config that displays a single OK button
34726          * @type Object
34727          */
34728         OK : {ok:true},
34729         /**
34730          * Button config that displays Yes and No buttons
34731          * @type Object
34732          */
34733         YESNO : {yes:true, no:true},
34734         /**
34735          * Button config that displays OK and Cancel buttons
34736          * @type Object
34737          */
34738         OKCANCEL : {ok:true, cancel:true},
34739         /**
34740          * Button config that displays Yes, No and Cancel buttons
34741          * @type Object
34742          */
34743         YESNOCANCEL : {yes:true, no:true, cancel:true},
34744
34745         /**
34746          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34747          * @type Number
34748          */
34749         defaultTextHeight : 75,
34750         /**
34751          * The maximum width in pixels of the message box (defaults to 600)
34752          * @type Number
34753          */
34754         maxWidth : 600,
34755         /**
34756          * The minimum width in pixels of the message box (defaults to 100)
34757          * @type Number
34758          */
34759         minWidth : 100,
34760         /**
34761          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34762          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34763          * @type Number
34764          */
34765         minProgressWidth : 250,
34766         /**
34767          * An object containing the default button text strings that can be overriden for localized language support.
34768          * Supported properties are: ok, cancel, yes and no.
34769          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34770          * @type Object
34771          */
34772         buttonText : {
34773             ok : "OK",
34774             cancel : "Cancel",
34775             yes : "Yes",
34776             no : "No"
34777         }
34778     };
34779 }();
34780
34781 /**
34782  * Shorthand for {@link Roo.MessageBox}
34783  */
34784 Roo.Msg = Roo.MessageBox;/*
34785  * Based on:
34786  * Ext JS Library 1.1.1
34787  * Copyright(c) 2006-2007, Ext JS, LLC.
34788  *
34789  * Originally Released Under LGPL - original licence link has changed is not relivant.
34790  *
34791  * Fork - LGPL
34792  * <script type="text/javascript">
34793  */
34794 /**
34795  * @class Roo.QuickTips
34796  * Provides attractive and customizable tooltips for any element.
34797  * @static
34798  */
34799 Roo.QuickTips = function(){
34800     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34801     var ce, bd, xy, dd;
34802     var visible = false, disabled = true, inited = false;
34803     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34804     
34805     var onOver = function(e){
34806         if(disabled){
34807             return;
34808         }
34809         var t = e.getTarget();
34810         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34811             return;
34812         }
34813         if(ce && t == ce.el){
34814             clearTimeout(hideProc);
34815             return;
34816         }
34817         if(t && tagEls[t.id]){
34818             tagEls[t.id].el = t;
34819             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34820             return;
34821         }
34822         var ttp, et = Roo.fly(t);
34823         var ns = cfg.namespace;
34824         if(tm.interceptTitles && t.title){
34825             ttp = t.title;
34826             t.qtip = ttp;
34827             t.removeAttribute("title");
34828             e.preventDefault();
34829         }else{
34830             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34831         }
34832         if(ttp){
34833             showProc = show.defer(tm.showDelay, tm, [{
34834                 el: t, 
34835                 text: ttp.replace(/\\n/g,'<br/>'),
34836                 width: et.getAttributeNS(ns, cfg.width),
34837                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34838                 title: et.getAttributeNS(ns, cfg.title),
34839                     cls: et.getAttributeNS(ns, cfg.cls)
34840             }]);
34841         }
34842     };
34843     
34844     var onOut = function(e){
34845         clearTimeout(showProc);
34846         var t = e.getTarget();
34847         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34848             hideProc = setTimeout(hide, tm.hideDelay);
34849         }
34850     };
34851     
34852     var onMove = function(e){
34853         if(disabled){
34854             return;
34855         }
34856         xy = e.getXY();
34857         xy[1] += 18;
34858         if(tm.trackMouse && ce){
34859             el.setXY(xy);
34860         }
34861     };
34862     
34863     var onDown = function(e){
34864         clearTimeout(showProc);
34865         clearTimeout(hideProc);
34866         if(!e.within(el)){
34867             if(tm.hideOnClick){
34868                 hide();
34869                 tm.disable();
34870                 tm.enable.defer(100, tm);
34871             }
34872         }
34873     };
34874     
34875     var getPad = function(){
34876         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34877     };
34878
34879     var show = function(o){
34880         if(disabled){
34881             return;
34882         }
34883         clearTimeout(dismissProc);
34884         ce = o;
34885         if(removeCls){ // in case manually hidden
34886             el.removeClass(removeCls);
34887             removeCls = null;
34888         }
34889         if(ce.cls){
34890             el.addClass(ce.cls);
34891             removeCls = ce.cls;
34892         }
34893         if(ce.title){
34894             tipTitle.update(ce.title);
34895             tipTitle.show();
34896         }else{
34897             tipTitle.update('');
34898             tipTitle.hide();
34899         }
34900         el.dom.style.width  = tm.maxWidth+'px';
34901         //tipBody.dom.style.width = '';
34902         tipBodyText.update(o.text);
34903         var p = getPad(), w = ce.width;
34904         if(!w){
34905             var td = tipBodyText.dom;
34906             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34907             if(aw > tm.maxWidth){
34908                 w = tm.maxWidth;
34909             }else if(aw < tm.minWidth){
34910                 w = tm.minWidth;
34911             }else{
34912                 w = aw;
34913             }
34914         }
34915         //tipBody.setWidth(w);
34916         el.setWidth(parseInt(w, 10) + p);
34917         if(ce.autoHide === false){
34918             close.setDisplayed(true);
34919             if(dd){
34920                 dd.unlock();
34921             }
34922         }else{
34923             close.setDisplayed(false);
34924             if(dd){
34925                 dd.lock();
34926             }
34927         }
34928         if(xy){
34929             el.avoidY = xy[1]-18;
34930             el.setXY(xy);
34931         }
34932         if(tm.animate){
34933             el.setOpacity(.1);
34934             el.setStyle("visibility", "visible");
34935             el.fadeIn({callback: afterShow});
34936         }else{
34937             afterShow();
34938         }
34939     };
34940     
34941     var afterShow = function(){
34942         if(ce){
34943             el.show();
34944             esc.enable();
34945             if(tm.autoDismiss && ce.autoHide !== false){
34946                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34947             }
34948         }
34949     };
34950     
34951     var hide = function(noanim){
34952         clearTimeout(dismissProc);
34953         clearTimeout(hideProc);
34954         ce = null;
34955         if(el.isVisible()){
34956             esc.disable();
34957             if(noanim !== true && tm.animate){
34958                 el.fadeOut({callback: afterHide});
34959             }else{
34960                 afterHide();
34961             } 
34962         }
34963     };
34964     
34965     var afterHide = function(){
34966         el.hide();
34967         if(removeCls){
34968             el.removeClass(removeCls);
34969             removeCls = null;
34970         }
34971     };
34972     
34973     return {
34974         /**
34975         * @cfg {Number} minWidth
34976         * The minimum width of the quick tip (defaults to 40)
34977         */
34978        minWidth : 40,
34979         /**
34980         * @cfg {Number} maxWidth
34981         * The maximum width of the quick tip (defaults to 300)
34982         */
34983        maxWidth : 300,
34984         /**
34985         * @cfg {Boolean} interceptTitles
34986         * True to automatically use the element's DOM title value if available (defaults to false)
34987         */
34988        interceptTitles : false,
34989         /**
34990         * @cfg {Boolean} trackMouse
34991         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34992         */
34993        trackMouse : false,
34994         /**
34995         * @cfg {Boolean} hideOnClick
34996         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34997         */
34998        hideOnClick : true,
34999         /**
35000         * @cfg {Number} showDelay
35001         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35002         */
35003        showDelay : 500,
35004         /**
35005         * @cfg {Number} hideDelay
35006         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35007         */
35008        hideDelay : 200,
35009         /**
35010         * @cfg {Boolean} autoHide
35011         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35012         * Used in conjunction with hideDelay.
35013         */
35014        autoHide : true,
35015         /**
35016         * @cfg {Boolean}
35017         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35018         * (defaults to true).  Used in conjunction with autoDismissDelay.
35019         */
35020        autoDismiss : true,
35021         /**
35022         * @cfg {Number}
35023         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35024         */
35025        autoDismissDelay : 5000,
35026        /**
35027         * @cfg {Boolean} animate
35028         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35029         */
35030        animate : false,
35031
35032        /**
35033         * @cfg {String} title
35034         * Title text to display (defaults to '').  This can be any valid HTML markup.
35035         */
35036         title: '',
35037        /**
35038         * @cfg {String} text
35039         * Body text to display (defaults to '').  This can be any valid HTML markup.
35040         */
35041         text : '',
35042        /**
35043         * @cfg {String} cls
35044         * A CSS class to apply to the base quick tip element (defaults to '').
35045         */
35046         cls : '',
35047        /**
35048         * @cfg {Number} width
35049         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35050         * minWidth or maxWidth.
35051         */
35052         width : null,
35053
35054     /**
35055      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35056      * or display QuickTips in a page.
35057      */
35058        init : function(){
35059           tm = Roo.QuickTips;
35060           cfg = tm.tagConfig;
35061           if(!inited){
35062               if(!Roo.isReady){ // allow calling of init() before onReady
35063                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35064                   return;
35065               }
35066               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35067               el.fxDefaults = {stopFx: true};
35068               // maximum custom styling
35069               //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>');
35070               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>');              
35071               tipTitle = el.child('h3');
35072               tipTitle.enableDisplayMode("block");
35073               tipBody = el.child('div.x-tip-bd');
35074               tipBodyText = el.child('div.x-tip-bd-inner');
35075               //bdLeft = el.child('div.x-tip-bd-left');
35076               //bdRight = el.child('div.x-tip-bd-right');
35077               close = el.child('div.x-tip-close');
35078               close.enableDisplayMode("block");
35079               close.on("click", hide);
35080               var d = Roo.get(document);
35081               d.on("mousedown", onDown);
35082               d.on("mouseover", onOver);
35083               d.on("mouseout", onOut);
35084               d.on("mousemove", onMove);
35085               esc = d.addKeyListener(27, hide);
35086               esc.disable();
35087               if(Roo.dd.DD){
35088                   dd = el.initDD("default", null, {
35089                       onDrag : function(){
35090                           el.sync();  
35091                       }
35092                   });
35093                   dd.setHandleElId(tipTitle.id);
35094                   dd.lock();
35095               }
35096               inited = true;
35097           }
35098           this.enable(); 
35099        },
35100
35101     /**
35102      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35103      * are supported:
35104      * <pre>
35105 Property    Type                   Description
35106 ----------  ---------------------  ------------------------------------------------------------------------
35107 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35108      * </ul>
35109      * @param {Object} config The config object
35110      */
35111        register : function(config){
35112            var cs = config instanceof Array ? config : arguments;
35113            for(var i = 0, len = cs.length; i < len; i++) {
35114                var c = cs[i];
35115                var target = c.target;
35116                if(target){
35117                    if(target instanceof Array){
35118                        for(var j = 0, jlen = target.length; j < jlen; j++){
35119                            tagEls[target[j]] = c;
35120                        }
35121                    }else{
35122                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35123                    }
35124                }
35125            }
35126        },
35127
35128     /**
35129      * Removes this quick tip from its element and destroys it.
35130      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35131      */
35132        unregister : function(el){
35133            delete tagEls[Roo.id(el)];
35134        },
35135
35136     /**
35137      * Enable this quick tip.
35138      */
35139        enable : function(){
35140            if(inited && disabled){
35141                locks.pop();
35142                if(locks.length < 1){
35143                    disabled = false;
35144                }
35145            }
35146        },
35147
35148     /**
35149      * Disable this quick tip.
35150      */
35151        disable : function(){
35152           disabled = true;
35153           clearTimeout(showProc);
35154           clearTimeout(hideProc);
35155           clearTimeout(dismissProc);
35156           if(ce){
35157               hide(true);
35158           }
35159           locks.push(1);
35160        },
35161
35162     /**
35163      * Returns true if the quick tip is enabled, else false.
35164      */
35165        isEnabled : function(){
35166             return !disabled;
35167        },
35168
35169         // private
35170        tagConfig : {
35171            namespace : "roo", // was ext?? this may break..
35172            alt_namespace : "ext",
35173            attribute : "qtip",
35174            width : "width",
35175            target : "target",
35176            title : "qtitle",
35177            hide : "hide",
35178            cls : "qclass"
35179        }
35180    };
35181 }();
35182
35183 // backwards compat
35184 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35185  * Based on:
35186  * Ext JS Library 1.1.1
35187  * Copyright(c) 2006-2007, Ext JS, LLC.
35188  *
35189  * Originally Released Under LGPL - original licence link has changed is not relivant.
35190  *
35191  * Fork - LGPL
35192  * <script type="text/javascript">
35193  */
35194  
35195
35196 /**
35197  * @class Roo.tree.TreePanel
35198  * @extends Roo.data.Tree
35199  * @cfg {Roo.tree.TreeNode} root The root node
35200  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35201  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35202  * @cfg {Boolean} enableDD true to enable drag and drop
35203  * @cfg {Boolean} enableDrag true to enable just drag
35204  * @cfg {Boolean} enableDrop true to enable just drop
35205  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35206  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35207  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35208  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35209  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35210  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35211  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35212  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35213  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35214  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35215  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35216  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35217  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35218  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35219  * @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>
35220  * @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>
35221  * 
35222  * @constructor
35223  * @param {String/HTMLElement/Element} el The container element
35224  * @param {Object} config
35225  */
35226 Roo.tree.TreePanel = function(el, config){
35227     var root = false;
35228     var loader = false;
35229     if (config.root) {
35230         root = config.root;
35231         delete config.root;
35232     }
35233     if (config.loader) {
35234         loader = config.loader;
35235         delete config.loader;
35236     }
35237     
35238     Roo.apply(this, config);
35239     Roo.tree.TreePanel.superclass.constructor.call(this);
35240     this.el = Roo.get(el);
35241     this.el.addClass('x-tree');
35242     //console.log(root);
35243     if (root) {
35244         this.setRootNode( Roo.factory(root, Roo.tree));
35245     }
35246     if (loader) {
35247         this.loader = Roo.factory(loader, Roo.tree);
35248     }
35249    /**
35250     * Read-only. The id of the container element becomes this TreePanel's id.
35251     */
35252     this.id = this.el.id;
35253     this.addEvents({
35254         /**
35255         * @event beforeload
35256         * Fires before a node is loaded, return false to cancel
35257         * @param {Node} node The node being loaded
35258         */
35259         "beforeload" : true,
35260         /**
35261         * @event load
35262         * Fires when a node is loaded
35263         * @param {Node} node The node that was loaded
35264         */
35265         "load" : true,
35266         /**
35267         * @event textchange
35268         * Fires when the text for a node is changed
35269         * @param {Node} node The node
35270         * @param {String} text The new text
35271         * @param {String} oldText The old text
35272         */
35273         "textchange" : true,
35274         /**
35275         * @event beforeexpand
35276         * Fires before a node is expanded, return false to cancel.
35277         * @param {Node} node The node
35278         * @param {Boolean} deep
35279         * @param {Boolean} anim
35280         */
35281         "beforeexpand" : true,
35282         /**
35283         * @event beforecollapse
35284         * Fires before a node is collapsed, return false to cancel.
35285         * @param {Node} node The node
35286         * @param {Boolean} deep
35287         * @param {Boolean} anim
35288         */
35289         "beforecollapse" : true,
35290         /**
35291         * @event expand
35292         * Fires when a node is expanded
35293         * @param {Node} node The node
35294         */
35295         "expand" : true,
35296         /**
35297         * @event disabledchange
35298         * Fires when the disabled status of a node changes
35299         * @param {Node} node The node
35300         * @param {Boolean} disabled
35301         */
35302         "disabledchange" : true,
35303         /**
35304         * @event collapse
35305         * Fires when a node is collapsed
35306         * @param {Node} node The node
35307         */
35308         "collapse" : true,
35309         /**
35310         * @event beforeclick
35311         * Fires before click processing on a node. Return false to cancel the default action.
35312         * @param {Node} node The node
35313         * @param {Roo.EventObject} e The event object
35314         */
35315         "beforeclick":true,
35316         /**
35317         * @event checkchange
35318         * Fires when a node with a checkbox's checked property changes
35319         * @param {Node} this This node
35320         * @param {Boolean} checked
35321         */
35322         "checkchange":true,
35323         /**
35324         * @event click
35325         * Fires when a node is clicked
35326         * @param {Node} node The node
35327         * @param {Roo.EventObject} e The event object
35328         */
35329         "click":true,
35330         /**
35331         * @event dblclick
35332         * Fires when a node is double clicked
35333         * @param {Node} node The node
35334         * @param {Roo.EventObject} e The event object
35335         */
35336         "dblclick":true,
35337         /**
35338         * @event contextmenu
35339         * Fires when a node is right clicked
35340         * @param {Node} node The node
35341         * @param {Roo.EventObject} e The event object
35342         */
35343         "contextmenu":true,
35344         /**
35345         * @event beforechildrenrendered
35346         * Fires right before the child nodes for a node are rendered
35347         * @param {Node} node The node
35348         */
35349         "beforechildrenrendered":true,
35350         /**
35351         * @event startdrag
35352         * Fires when a node starts being dragged
35353         * @param {Roo.tree.TreePanel} this
35354         * @param {Roo.tree.TreeNode} node
35355         * @param {event} e The raw browser event
35356         */ 
35357        "startdrag" : true,
35358        /**
35359         * @event enddrag
35360         * Fires when a drag operation is complete
35361         * @param {Roo.tree.TreePanel} this
35362         * @param {Roo.tree.TreeNode} node
35363         * @param {event} e The raw browser event
35364         */
35365        "enddrag" : true,
35366        /**
35367         * @event dragdrop
35368         * Fires when a dragged node is dropped on a valid DD target
35369         * @param {Roo.tree.TreePanel} this
35370         * @param {Roo.tree.TreeNode} node
35371         * @param {DD} dd The dd it was dropped on
35372         * @param {event} e The raw browser event
35373         */
35374        "dragdrop" : true,
35375        /**
35376         * @event beforenodedrop
35377         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35378         * passed to handlers has the following properties:<br />
35379         * <ul style="padding:5px;padding-left:16px;">
35380         * <li>tree - The TreePanel</li>
35381         * <li>target - The node being targeted for the drop</li>
35382         * <li>data - The drag data from the drag source</li>
35383         * <li>point - The point of the drop - append, above or below</li>
35384         * <li>source - The drag source</li>
35385         * <li>rawEvent - Raw mouse event</li>
35386         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35387         * to be inserted by setting them on this object.</li>
35388         * <li>cancel - Set this to true to cancel the drop.</li>
35389         * </ul>
35390         * @param {Object} dropEvent
35391         */
35392        "beforenodedrop" : true,
35393        /**
35394         * @event nodedrop
35395         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35396         * passed to handlers has the following properties:<br />
35397         * <ul style="padding:5px;padding-left:16px;">
35398         * <li>tree - The TreePanel</li>
35399         * <li>target - The node being targeted for the drop</li>
35400         * <li>data - The drag data from the drag source</li>
35401         * <li>point - The point of the drop - append, above or below</li>
35402         * <li>source - The drag source</li>
35403         * <li>rawEvent - Raw mouse event</li>
35404         * <li>dropNode - Dropped node(s).</li>
35405         * </ul>
35406         * @param {Object} dropEvent
35407         */
35408        "nodedrop" : true,
35409         /**
35410         * @event nodedragover
35411         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35412         * passed to handlers has the following properties:<br />
35413         * <ul style="padding:5px;padding-left:16px;">
35414         * <li>tree - The TreePanel</li>
35415         * <li>target - The node being targeted for the drop</li>
35416         * <li>data - The drag data from the drag source</li>
35417         * <li>point - The point of the drop - append, above or below</li>
35418         * <li>source - The drag source</li>
35419         * <li>rawEvent - Raw mouse event</li>
35420         * <li>dropNode - Drop node(s) provided by the source.</li>
35421         * <li>cancel - Set this to true to signal drop not allowed.</li>
35422         * </ul>
35423         * @param {Object} dragOverEvent
35424         */
35425        "nodedragover" : true,
35426        /**
35427         * @event appendnode
35428         * Fires when append node to the tree
35429         * @param {Roo.tree.TreePanel} this
35430         * @param {Roo.tree.TreeNode} node
35431         * @param {Number} index The index of the newly appended node
35432         */
35433        "appendnode" : true
35434         
35435     });
35436     if(this.singleExpand){
35437        this.on("beforeexpand", this.restrictExpand, this);
35438     }
35439     if (this.editor) {
35440         this.editor.tree = this;
35441         this.editor = Roo.factory(this.editor, Roo.tree);
35442     }
35443     
35444     if (this.selModel) {
35445         this.selModel = Roo.factory(this.selModel, Roo.tree);
35446     }
35447    
35448 };
35449 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35450     rootVisible : true,
35451     animate: Roo.enableFx,
35452     lines : true,
35453     enableDD : false,
35454     hlDrop : Roo.enableFx,
35455   
35456     renderer: false,
35457     
35458     rendererTip: false,
35459     // private
35460     restrictExpand : function(node){
35461         var p = node.parentNode;
35462         if(p){
35463             if(p.expandedChild && p.expandedChild.parentNode == p){
35464                 p.expandedChild.collapse();
35465             }
35466             p.expandedChild = node;
35467         }
35468     },
35469
35470     // private override
35471     setRootNode : function(node){
35472         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35473         if(!this.rootVisible){
35474             node.ui = new Roo.tree.RootTreeNodeUI(node);
35475         }
35476         return node;
35477     },
35478
35479     /**
35480      * Returns the container element for this TreePanel
35481      */
35482     getEl : function(){
35483         return this.el;
35484     },
35485
35486     /**
35487      * Returns the default TreeLoader for this TreePanel
35488      */
35489     getLoader : function(){
35490         return this.loader;
35491     },
35492
35493     /**
35494      * Expand all nodes
35495      */
35496     expandAll : function(){
35497         this.root.expand(true);
35498     },
35499
35500     /**
35501      * Collapse all nodes
35502      */
35503     collapseAll : function(){
35504         this.root.collapse(true);
35505     },
35506
35507     /**
35508      * Returns the selection model used by this TreePanel
35509      */
35510     getSelectionModel : function(){
35511         if(!this.selModel){
35512             this.selModel = new Roo.tree.DefaultSelectionModel();
35513         }
35514         return this.selModel;
35515     },
35516
35517     /**
35518      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35519      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35520      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35521      * @return {Array}
35522      */
35523     getChecked : function(a, startNode){
35524         startNode = startNode || this.root;
35525         var r = [];
35526         var f = function(){
35527             if(this.attributes.checked){
35528                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35529             }
35530         }
35531         startNode.cascade(f);
35532         return r;
35533     },
35534
35535     /**
35536      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35537      * @param {String} path
35538      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35539      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35540      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35541      */
35542     expandPath : function(path, attr, callback){
35543         attr = attr || "id";
35544         var keys = path.split(this.pathSeparator);
35545         var curNode = this.root;
35546         if(curNode.attributes[attr] != keys[1]){ // invalid root
35547             if(callback){
35548                 callback(false, null);
35549             }
35550             return;
35551         }
35552         var index = 1;
35553         var f = function(){
35554             if(++index == keys.length){
35555                 if(callback){
35556                     callback(true, curNode);
35557                 }
35558                 return;
35559             }
35560             var c = curNode.findChild(attr, keys[index]);
35561             if(!c){
35562                 if(callback){
35563                     callback(false, curNode);
35564                 }
35565                 return;
35566             }
35567             curNode = c;
35568             c.expand(false, false, f);
35569         };
35570         curNode.expand(false, false, f);
35571     },
35572
35573     /**
35574      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35575      * @param {String} path
35576      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35577      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35578      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35579      */
35580     selectPath : function(path, attr, callback){
35581         attr = attr || "id";
35582         var keys = path.split(this.pathSeparator);
35583         var v = keys.pop();
35584         if(keys.length > 0){
35585             var f = function(success, node){
35586                 if(success && node){
35587                     var n = node.findChild(attr, v);
35588                     if(n){
35589                         n.select();
35590                         if(callback){
35591                             callback(true, n);
35592                         }
35593                     }else if(callback){
35594                         callback(false, n);
35595                     }
35596                 }else{
35597                     if(callback){
35598                         callback(false, n);
35599                     }
35600                 }
35601             };
35602             this.expandPath(keys.join(this.pathSeparator), attr, f);
35603         }else{
35604             this.root.select();
35605             if(callback){
35606                 callback(true, this.root);
35607             }
35608         }
35609     },
35610
35611     getTreeEl : function(){
35612         return this.el;
35613     },
35614
35615     /**
35616      * Trigger rendering of this TreePanel
35617      */
35618     render : function(){
35619         if (this.innerCt) {
35620             return this; // stop it rendering more than once!!
35621         }
35622         
35623         this.innerCt = this.el.createChild({tag:"ul",
35624                cls:"x-tree-root-ct " +
35625                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35626
35627         if(this.containerScroll){
35628             Roo.dd.ScrollManager.register(this.el);
35629         }
35630         if((this.enableDD || this.enableDrop) && !this.dropZone){
35631            /**
35632             * The dropZone used by this tree if drop is enabled
35633             * @type Roo.tree.TreeDropZone
35634             */
35635              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35636                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35637            });
35638         }
35639         if((this.enableDD || this.enableDrag) && !this.dragZone){
35640            /**
35641             * The dragZone used by this tree if drag is enabled
35642             * @type Roo.tree.TreeDragZone
35643             */
35644             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35645                ddGroup: this.ddGroup || "TreeDD",
35646                scroll: this.ddScroll
35647            });
35648         }
35649         this.getSelectionModel().init(this);
35650         if (!this.root) {
35651             Roo.log("ROOT not set in tree");
35652             return this;
35653         }
35654         this.root.render();
35655         if(!this.rootVisible){
35656             this.root.renderChildren();
35657         }
35658         return this;
35659     }
35660 });/*
35661  * Based on:
35662  * Ext JS Library 1.1.1
35663  * Copyright(c) 2006-2007, Ext JS, LLC.
35664  *
35665  * Originally Released Under LGPL - original licence link has changed is not relivant.
35666  *
35667  * Fork - LGPL
35668  * <script type="text/javascript">
35669  */
35670  
35671
35672 /**
35673  * @class Roo.tree.DefaultSelectionModel
35674  * @extends Roo.util.Observable
35675  * The default single selection for a TreePanel.
35676  * @param {Object} cfg Configuration
35677  */
35678 Roo.tree.DefaultSelectionModel = function(cfg){
35679    this.selNode = null;
35680    
35681    
35682    
35683    this.addEvents({
35684        /**
35685         * @event selectionchange
35686         * Fires when the selected node changes
35687         * @param {DefaultSelectionModel} this
35688         * @param {TreeNode} node the new selection
35689         */
35690        "selectionchange" : true,
35691
35692        /**
35693         * @event beforeselect
35694         * Fires before the selected node changes, return false to cancel the change
35695         * @param {DefaultSelectionModel} this
35696         * @param {TreeNode} node the new selection
35697         * @param {TreeNode} node the old selection
35698         */
35699        "beforeselect" : true
35700    });
35701    
35702     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35703 };
35704
35705 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35706     init : function(tree){
35707         this.tree = tree;
35708         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35709         tree.on("click", this.onNodeClick, this);
35710     },
35711     
35712     onNodeClick : function(node, e){
35713         if (e.ctrlKey && this.selNode == node)  {
35714             this.unselect(node);
35715             return;
35716         }
35717         this.select(node);
35718     },
35719     
35720     /**
35721      * Select a node.
35722      * @param {TreeNode} node The node to select
35723      * @return {TreeNode} The selected node
35724      */
35725     select : function(node){
35726         var last = this.selNode;
35727         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35728             if(last){
35729                 last.ui.onSelectedChange(false);
35730             }
35731             this.selNode = node;
35732             node.ui.onSelectedChange(true);
35733             this.fireEvent("selectionchange", this, node, last);
35734         }
35735         return node;
35736     },
35737     
35738     /**
35739      * Deselect a node.
35740      * @param {TreeNode} node The node to unselect
35741      */
35742     unselect : function(node){
35743         if(this.selNode == node){
35744             this.clearSelections();
35745         }    
35746     },
35747     
35748     /**
35749      * Clear all selections
35750      */
35751     clearSelections : function(){
35752         var n = this.selNode;
35753         if(n){
35754             n.ui.onSelectedChange(false);
35755             this.selNode = null;
35756             this.fireEvent("selectionchange", this, null);
35757         }
35758         return n;
35759     },
35760     
35761     /**
35762      * Get the selected node
35763      * @return {TreeNode} The selected node
35764      */
35765     getSelectedNode : function(){
35766         return this.selNode;    
35767     },
35768     
35769     /**
35770      * Returns true if the node is selected
35771      * @param {TreeNode} node The node to check
35772      * @return {Boolean}
35773      */
35774     isSelected : function(node){
35775         return this.selNode == node;  
35776     },
35777
35778     /**
35779      * Selects the node above the selected node in the tree, intelligently walking the nodes
35780      * @return TreeNode The new selection
35781      */
35782     selectPrevious : function(){
35783         var s = this.selNode || this.lastSelNode;
35784         if(!s){
35785             return null;
35786         }
35787         var ps = s.previousSibling;
35788         if(ps){
35789             if(!ps.isExpanded() || ps.childNodes.length < 1){
35790                 return this.select(ps);
35791             } else{
35792                 var lc = ps.lastChild;
35793                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35794                     lc = lc.lastChild;
35795                 }
35796                 return this.select(lc);
35797             }
35798         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35799             return this.select(s.parentNode);
35800         }
35801         return null;
35802     },
35803
35804     /**
35805      * Selects the node above the selected node in the tree, intelligently walking the nodes
35806      * @return TreeNode The new selection
35807      */
35808     selectNext : function(){
35809         var s = this.selNode || this.lastSelNode;
35810         if(!s){
35811             return null;
35812         }
35813         if(s.firstChild && s.isExpanded()){
35814              return this.select(s.firstChild);
35815          }else if(s.nextSibling){
35816              return this.select(s.nextSibling);
35817          }else if(s.parentNode){
35818             var newS = null;
35819             s.parentNode.bubble(function(){
35820                 if(this.nextSibling){
35821                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35822                     return false;
35823                 }
35824             });
35825             return newS;
35826          }
35827         return null;
35828     },
35829
35830     onKeyDown : function(e){
35831         var s = this.selNode || this.lastSelNode;
35832         // undesirable, but required
35833         var sm = this;
35834         if(!s){
35835             return;
35836         }
35837         var k = e.getKey();
35838         switch(k){
35839              case e.DOWN:
35840                  e.stopEvent();
35841                  this.selectNext();
35842              break;
35843              case e.UP:
35844                  e.stopEvent();
35845                  this.selectPrevious();
35846              break;
35847              case e.RIGHT:
35848                  e.preventDefault();
35849                  if(s.hasChildNodes()){
35850                      if(!s.isExpanded()){
35851                          s.expand();
35852                      }else if(s.firstChild){
35853                          this.select(s.firstChild, e);
35854                      }
35855                  }
35856              break;
35857              case e.LEFT:
35858                  e.preventDefault();
35859                  if(s.hasChildNodes() && s.isExpanded()){
35860                      s.collapse();
35861                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35862                      this.select(s.parentNode, e);
35863                  }
35864              break;
35865         };
35866     }
35867 });
35868
35869 /**
35870  * @class Roo.tree.MultiSelectionModel
35871  * @extends Roo.util.Observable
35872  * Multi selection for a TreePanel.
35873  * @param {Object} cfg Configuration
35874  */
35875 Roo.tree.MultiSelectionModel = function(){
35876    this.selNodes = [];
35877    this.selMap = {};
35878    this.addEvents({
35879        /**
35880         * @event selectionchange
35881         * Fires when the selected nodes change
35882         * @param {MultiSelectionModel} this
35883         * @param {Array} nodes Array of the selected nodes
35884         */
35885        "selectionchange" : true
35886    });
35887    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35888    
35889 };
35890
35891 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35892     init : function(tree){
35893         this.tree = tree;
35894         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35895         tree.on("click", this.onNodeClick, this);
35896     },
35897     
35898     onNodeClick : function(node, e){
35899         this.select(node, e, e.ctrlKey);
35900     },
35901     
35902     /**
35903      * Select a node.
35904      * @param {TreeNode} node The node to select
35905      * @param {EventObject} e (optional) An event associated with the selection
35906      * @param {Boolean} keepExisting True to retain existing selections
35907      * @return {TreeNode} The selected node
35908      */
35909     select : function(node, e, keepExisting){
35910         if(keepExisting !== true){
35911             this.clearSelections(true);
35912         }
35913         if(this.isSelected(node)){
35914             this.lastSelNode = node;
35915             return node;
35916         }
35917         this.selNodes.push(node);
35918         this.selMap[node.id] = node;
35919         this.lastSelNode = node;
35920         node.ui.onSelectedChange(true);
35921         this.fireEvent("selectionchange", this, this.selNodes);
35922         return node;
35923     },
35924     
35925     /**
35926      * Deselect a node.
35927      * @param {TreeNode} node The node to unselect
35928      */
35929     unselect : function(node){
35930         if(this.selMap[node.id]){
35931             node.ui.onSelectedChange(false);
35932             var sn = this.selNodes;
35933             var index = -1;
35934             if(sn.indexOf){
35935                 index = sn.indexOf(node);
35936             }else{
35937                 for(var i = 0, len = sn.length; i < len; i++){
35938                     if(sn[i] == node){
35939                         index = i;
35940                         break;
35941                     }
35942                 }
35943             }
35944             if(index != -1){
35945                 this.selNodes.splice(index, 1);
35946             }
35947             delete this.selMap[node.id];
35948             this.fireEvent("selectionchange", this, this.selNodes);
35949         }
35950     },
35951     
35952     /**
35953      * Clear all selections
35954      */
35955     clearSelections : function(suppressEvent){
35956         var sn = this.selNodes;
35957         if(sn.length > 0){
35958             for(var i = 0, len = sn.length; i < len; i++){
35959                 sn[i].ui.onSelectedChange(false);
35960             }
35961             this.selNodes = [];
35962             this.selMap = {};
35963             if(suppressEvent !== true){
35964                 this.fireEvent("selectionchange", this, this.selNodes);
35965             }
35966         }
35967     },
35968     
35969     /**
35970      * Returns true if the node is selected
35971      * @param {TreeNode} node The node to check
35972      * @return {Boolean}
35973      */
35974     isSelected : function(node){
35975         return this.selMap[node.id] ? true : false;  
35976     },
35977     
35978     /**
35979      * Returns an array of the selected nodes
35980      * @return {Array}
35981      */
35982     getSelectedNodes : function(){
35983         return this.selNodes;    
35984     },
35985
35986     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35987
35988     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35989
35990     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35991 });/*
35992  * Based on:
35993  * Ext JS Library 1.1.1
35994  * Copyright(c) 2006-2007, Ext JS, LLC.
35995  *
35996  * Originally Released Under LGPL - original licence link has changed is not relivant.
35997  *
35998  * Fork - LGPL
35999  * <script type="text/javascript">
36000  */
36001  
36002 /**
36003  * @class Roo.tree.TreeNode
36004  * @extends Roo.data.Node
36005  * @cfg {String} text The text for this node
36006  * @cfg {Boolean} expanded true to start the node expanded
36007  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36008  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36009  * @cfg {Boolean} disabled true to start the node disabled
36010  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36011  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36012  * @cfg {String} cls A css class to be added to the node
36013  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36014  * @cfg {String} href URL of the link used for the node (defaults to #)
36015  * @cfg {String} hrefTarget target frame for the link
36016  * @cfg {String} qtip An Ext QuickTip for the node
36017  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36018  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36019  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36020  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36021  * (defaults to undefined with no checkbox rendered)
36022  * @constructor
36023  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36024  */
36025 Roo.tree.TreeNode = function(attributes){
36026     attributes = attributes || {};
36027     if(typeof attributes == "string"){
36028         attributes = {text: attributes};
36029     }
36030     this.childrenRendered = false;
36031     this.rendered = false;
36032     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36033     this.expanded = attributes.expanded === true;
36034     this.isTarget = attributes.isTarget !== false;
36035     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36036     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36037
36038     /**
36039      * Read-only. The text for this node. To change it use setText().
36040      * @type String
36041      */
36042     this.text = attributes.text;
36043     /**
36044      * True if this node is disabled.
36045      * @type Boolean
36046      */
36047     this.disabled = attributes.disabled === true;
36048
36049     this.addEvents({
36050         /**
36051         * @event textchange
36052         * Fires when the text for this node is changed
36053         * @param {Node} this This node
36054         * @param {String} text The new text
36055         * @param {String} oldText The old text
36056         */
36057         "textchange" : true,
36058         /**
36059         * @event beforeexpand
36060         * Fires before this node is expanded, return false to cancel.
36061         * @param {Node} this This node
36062         * @param {Boolean} deep
36063         * @param {Boolean} anim
36064         */
36065         "beforeexpand" : true,
36066         /**
36067         * @event beforecollapse
36068         * Fires before this node is collapsed, return false to cancel.
36069         * @param {Node} this This node
36070         * @param {Boolean} deep
36071         * @param {Boolean} anim
36072         */
36073         "beforecollapse" : true,
36074         /**
36075         * @event expand
36076         * Fires when this node is expanded
36077         * @param {Node} this This node
36078         */
36079         "expand" : true,
36080         /**
36081         * @event disabledchange
36082         * Fires when the disabled status of this node changes
36083         * @param {Node} this This node
36084         * @param {Boolean} disabled
36085         */
36086         "disabledchange" : true,
36087         /**
36088         * @event collapse
36089         * Fires when this node is collapsed
36090         * @param {Node} this This node
36091         */
36092         "collapse" : true,
36093         /**
36094         * @event beforeclick
36095         * Fires before click processing. Return false to cancel the default action.
36096         * @param {Node} this This node
36097         * @param {Roo.EventObject} e The event object
36098         */
36099         "beforeclick":true,
36100         /**
36101         * @event checkchange
36102         * Fires when a node with a checkbox's checked property changes
36103         * @param {Node} this This node
36104         * @param {Boolean} checked
36105         */
36106         "checkchange":true,
36107         /**
36108         * @event click
36109         * Fires when this node is clicked
36110         * @param {Node} this This node
36111         * @param {Roo.EventObject} e The event object
36112         */
36113         "click":true,
36114         /**
36115         * @event dblclick
36116         * Fires when this node is double clicked
36117         * @param {Node} this This node
36118         * @param {Roo.EventObject} e The event object
36119         */
36120         "dblclick":true,
36121         /**
36122         * @event contextmenu
36123         * Fires when this node is right clicked
36124         * @param {Node} this This node
36125         * @param {Roo.EventObject} e The event object
36126         */
36127         "contextmenu":true,
36128         /**
36129         * @event beforechildrenrendered
36130         * Fires right before the child nodes for this node are rendered
36131         * @param {Node} this This node
36132         */
36133         "beforechildrenrendered":true
36134     });
36135
36136     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36137
36138     /**
36139      * Read-only. The UI for this node
36140      * @type TreeNodeUI
36141      */
36142     this.ui = new uiClass(this);
36143     
36144     // finally support items[]
36145     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36146         return;
36147     }
36148     
36149     
36150     Roo.each(this.attributes.items, function(c) {
36151         this.appendChild(Roo.factory(c,Roo.Tree));
36152     }, this);
36153     delete this.attributes.items;
36154     
36155     
36156     
36157 };
36158 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36159     preventHScroll: true,
36160     /**
36161      * Returns true if this node is expanded
36162      * @return {Boolean}
36163      */
36164     isExpanded : function(){
36165         return this.expanded;
36166     },
36167
36168     /**
36169      * Returns the UI object for this node
36170      * @return {TreeNodeUI}
36171      */
36172     getUI : function(){
36173         return this.ui;
36174     },
36175
36176     // private override
36177     setFirstChild : function(node){
36178         var of = this.firstChild;
36179         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36180         if(this.childrenRendered && of && node != of){
36181             of.renderIndent(true, true);
36182         }
36183         if(this.rendered){
36184             this.renderIndent(true, true);
36185         }
36186     },
36187
36188     // private override
36189     setLastChild : function(node){
36190         var ol = this.lastChild;
36191         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36192         if(this.childrenRendered && ol && node != ol){
36193             ol.renderIndent(true, true);
36194         }
36195         if(this.rendered){
36196             this.renderIndent(true, true);
36197         }
36198     },
36199
36200     // these methods are overridden to provide lazy rendering support
36201     // private override
36202     appendChild : function()
36203     {
36204         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36205         if(node && this.childrenRendered){
36206             node.render();
36207         }
36208         this.ui.updateExpandIcon();
36209         return node;
36210     },
36211
36212     // private override
36213     removeChild : function(node){
36214         this.ownerTree.getSelectionModel().unselect(node);
36215         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36216         // if it's been rendered remove dom node
36217         if(this.childrenRendered){
36218             node.ui.remove();
36219         }
36220         if(this.childNodes.length < 1){
36221             this.collapse(false, false);
36222         }else{
36223             this.ui.updateExpandIcon();
36224         }
36225         if(!this.firstChild) {
36226             this.childrenRendered = false;
36227         }
36228         return node;
36229     },
36230
36231     // private override
36232     insertBefore : function(node, refNode){
36233         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36234         if(newNode && refNode && this.childrenRendered){
36235             node.render();
36236         }
36237         this.ui.updateExpandIcon();
36238         return newNode;
36239     },
36240
36241     /**
36242      * Sets the text for this node
36243      * @param {String} text
36244      */
36245     setText : function(text){
36246         var oldText = this.text;
36247         this.text = text;
36248         this.attributes.text = text;
36249         if(this.rendered){ // event without subscribing
36250             this.ui.onTextChange(this, text, oldText);
36251         }
36252         this.fireEvent("textchange", this, text, oldText);
36253     },
36254
36255     /**
36256      * Triggers selection of this node
36257      */
36258     select : function(){
36259         this.getOwnerTree().getSelectionModel().select(this);
36260     },
36261
36262     /**
36263      * Triggers deselection of this node
36264      */
36265     unselect : function(){
36266         this.getOwnerTree().getSelectionModel().unselect(this);
36267     },
36268
36269     /**
36270      * Returns true if this node is selected
36271      * @return {Boolean}
36272      */
36273     isSelected : function(){
36274         return this.getOwnerTree().getSelectionModel().isSelected(this);
36275     },
36276
36277     /**
36278      * Expand this node.
36279      * @param {Boolean} deep (optional) True to expand all children as well
36280      * @param {Boolean} anim (optional) false to cancel the default animation
36281      * @param {Function} callback (optional) A callback to be called when
36282      * expanding this node completes (does not wait for deep expand to complete).
36283      * Called with 1 parameter, this node.
36284      */
36285     expand : function(deep, anim, callback){
36286         if(!this.expanded){
36287             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36288                 return;
36289             }
36290             if(!this.childrenRendered){
36291                 this.renderChildren();
36292             }
36293             this.expanded = true;
36294             
36295             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36296                 this.ui.animExpand(function(){
36297                     this.fireEvent("expand", this);
36298                     if(typeof callback == "function"){
36299                         callback(this);
36300                     }
36301                     if(deep === true){
36302                         this.expandChildNodes(true);
36303                     }
36304                 }.createDelegate(this));
36305                 return;
36306             }else{
36307                 this.ui.expand();
36308                 this.fireEvent("expand", this);
36309                 if(typeof callback == "function"){
36310                     callback(this);
36311                 }
36312             }
36313         }else{
36314            if(typeof callback == "function"){
36315                callback(this);
36316            }
36317         }
36318         if(deep === true){
36319             this.expandChildNodes(true);
36320         }
36321     },
36322
36323     isHiddenRoot : function(){
36324         return this.isRoot && !this.getOwnerTree().rootVisible;
36325     },
36326
36327     /**
36328      * Collapse this node.
36329      * @param {Boolean} deep (optional) True to collapse all children as well
36330      * @param {Boolean} anim (optional) false to cancel the default animation
36331      */
36332     collapse : function(deep, anim){
36333         if(this.expanded && !this.isHiddenRoot()){
36334             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36335                 return;
36336             }
36337             this.expanded = false;
36338             if((this.getOwnerTree().animate && anim !== false) || anim){
36339                 this.ui.animCollapse(function(){
36340                     this.fireEvent("collapse", this);
36341                     if(deep === true){
36342                         this.collapseChildNodes(true);
36343                     }
36344                 }.createDelegate(this));
36345                 return;
36346             }else{
36347                 this.ui.collapse();
36348                 this.fireEvent("collapse", this);
36349             }
36350         }
36351         if(deep === true){
36352             var cs = this.childNodes;
36353             for(var i = 0, len = cs.length; i < len; i++) {
36354                 cs[i].collapse(true, false);
36355             }
36356         }
36357     },
36358
36359     // private
36360     delayedExpand : function(delay){
36361         if(!this.expandProcId){
36362             this.expandProcId = this.expand.defer(delay, this);
36363         }
36364     },
36365
36366     // private
36367     cancelExpand : function(){
36368         if(this.expandProcId){
36369             clearTimeout(this.expandProcId);
36370         }
36371         this.expandProcId = false;
36372     },
36373
36374     /**
36375      * Toggles expanded/collapsed state of the node
36376      */
36377     toggle : function(){
36378         if(this.expanded){
36379             this.collapse();
36380         }else{
36381             this.expand();
36382         }
36383     },
36384
36385     /**
36386      * Ensures all parent nodes are expanded
36387      */
36388     ensureVisible : function(callback){
36389         var tree = this.getOwnerTree();
36390         tree.expandPath(this.parentNode.getPath(), false, function(){
36391             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36392             Roo.callback(callback);
36393         }.createDelegate(this));
36394     },
36395
36396     /**
36397      * Expand all child nodes
36398      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36399      */
36400     expandChildNodes : function(deep){
36401         var cs = this.childNodes;
36402         for(var i = 0, len = cs.length; i < len; i++) {
36403                 cs[i].expand(deep);
36404         }
36405     },
36406
36407     /**
36408      * Collapse all child nodes
36409      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36410      */
36411     collapseChildNodes : function(deep){
36412         var cs = this.childNodes;
36413         for(var i = 0, len = cs.length; i < len; i++) {
36414                 cs[i].collapse(deep);
36415         }
36416     },
36417
36418     /**
36419      * Disables this node
36420      */
36421     disable : function(){
36422         this.disabled = true;
36423         this.unselect();
36424         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36425             this.ui.onDisableChange(this, true);
36426         }
36427         this.fireEvent("disabledchange", this, true);
36428     },
36429
36430     /**
36431      * Enables this node
36432      */
36433     enable : function(){
36434         this.disabled = false;
36435         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36436             this.ui.onDisableChange(this, false);
36437         }
36438         this.fireEvent("disabledchange", this, false);
36439     },
36440
36441     // private
36442     renderChildren : function(suppressEvent){
36443         if(suppressEvent !== false){
36444             this.fireEvent("beforechildrenrendered", this);
36445         }
36446         var cs = this.childNodes;
36447         for(var i = 0, len = cs.length; i < len; i++){
36448             cs[i].render(true);
36449         }
36450         this.childrenRendered = true;
36451     },
36452
36453     // private
36454     sort : function(fn, scope){
36455         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36456         if(this.childrenRendered){
36457             var cs = this.childNodes;
36458             for(var i = 0, len = cs.length; i < len; i++){
36459                 cs[i].render(true);
36460             }
36461         }
36462     },
36463
36464     // private
36465     render : function(bulkRender){
36466         this.ui.render(bulkRender);
36467         if(!this.rendered){
36468             this.rendered = true;
36469             if(this.expanded){
36470                 this.expanded = false;
36471                 this.expand(false, false);
36472             }
36473         }
36474     },
36475
36476     // private
36477     renderIndent : function(deep, refresh){
36478         if(refresh){
36479             this.ui.childIndent = null;
36480         }
36481         this.ui.renderIndent();
36482         if(deep === true && this.childrenRendered){
36483             var cs = this.childNodes;
36484             for(var i = 0, len = cs.length; i < len; i++){
36485                 cs[i].renderIndent(true, refresh);
36486             }
36487         }
36488     }
36489 });/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499  
36500 /**
36501  * @class Roo.tree.AsyncTreeNode
36502  * @extends Roo.tree.TreeNode
36503  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36504  * @constructor
36505  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36506  */
36507  Roo.tree.AsyncTreeNode = function(config){
36508     this.loaded = false;
36509     this.loading = false;
36510     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36511     /**
36512     * @event beforeload
36513     * Fires before this node is loaded, return false to cancel
36514     * @param {Node} this This node
36515     */
36516     this.addEvents({'beforeload':true, 'load': true});
36517     /**
36518     * @event load
36519     * Fires when this node is loaded
36520     * @param {Node} this This node
36521     */
36522     /**
36523      * The loader used by this node (defaults to using the tree's defined loader)
36524      * @type TreeLoader
36525      * @property loader
36526      */
36527 };
36528 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36529     expand : function(deep, anim, callback){
36530         if(this.loading){ // if an async load is already running, waiting til it's done
36531             var timer;
36532             var f = function(){
36533                 if(!this.loading){ // done loading
36534                     clearInterval(timer);
36535                     this.expand(deep, anim, callback);
36536                 }
36537             }.createDelegate(this);
36538             timer = setInterval(f, 200);
36539             return;
36540         }
36541         if(!this.loaded){
36542             if(this.fireEvent("beforeload", this) === false){
36543                 return;
36544             }
36545             this.loading = true;
36546             this.ui.beforeLoad(this);
36547             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36548             if(loader){
36549                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36550                 return;
36551             }
36552         }
36553         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36554     },
36555     
36556     /**
36557      * Returns true if this node is currently loading
36558      * @return {Boolean}
36559      */
36560     isLoading : function(){
36561         return this.loading;  
36562     },
36563     
36564     loadComplete : function(deep, anim, callback){
36565         this.loading = false;
36566         this.loaded = true;
36567         this.ui.afterLoad(this);
36568         this.fireEvent("load", this);
36569         this.expand(deep, anim, callback);
36570     },
36571     
36572     /**
36573      * Returns true if this node has been loaded
36574      * @return {Boolean}
36575      */
36576     isLoaded : function(){
36577         return this.loaded;
36578     },
36579     
36580     hasChildNodes : function(){
36581         if(!this.isLeaf() && !this.loaded){
36582             return true;
36583         }else{
36584             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36585         }
36586     },
36587
36588     /**
36589      * Trigger a reload for this node
36590      * @param {Function} callback
36591      */
36592     reload : function(callback){
36593         this.collapse(false, false);
36594         while(this.firstChild){
36595             this.removeChild(this.firstChild);
36596         }
36597         this.childrenRendered = false;
36598         this.loaded = false;
36599         if(this.isHiddenRoot()){
36600             this.expanded = false;
36601         }
36602         this.expand(false, false, callback);
36603     }
36604 });/*
36605  * Based on:
36606  * Ext JS Library 1.1.1
36607  * Copyright(c) 2006-2007, Ext JS, LLC.
36608  *
36609  * Originally Released Under LGPL - original licence link has changed is not relivant.
36610  *
36611  * Fork - LGPL
36612  * <script type="text/javascript">
36613  */
36614  
36615 /**
36616  * @class Roo.tree.TreeNodeUI
36617  * @constructor
36618  * @param {Object} node The node to render
36619  * The TreeNode UI implementation is separate from the
36620  * tree implementation. Unless you are customizing the tree UI,
36621  * you should never have to use this directly.
36622  */
36623 Roo.tree.TreeNodeUI = function(node){
36624     this.node = node;
36625     this.rendered = false;
36626     this.animating = false;
36627     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36628 };
36629
36630 Roo.tree.TreeNodeUI.prototype = {
36631     removeChild : function(node){
36632         if(this.rendered){
36633             this.ctNode.removeChild(node.ui.getEl());
36634         }
36635     },
36636
36637     beforeLoad : function(){
36638          this.addClass("x-tree-node-loading");
36639     },
36640
36641     afterLoad : function(){
36642          this.removeClass("x-tree-node-loading");
36643     },
36644
36645     onTextChange : function(node, text, oldText){
36646         if(this.rendered){
36647             this.textNode.innerHTML = text;
36648         }
36649     },
36650
36651     onDisableChange : function(node, state){
36652         this.disabled = state;
36653         if(state){
36654             this.addClass("x-tree-node-disabled");
36655         }else{
36656             this.removeClass("x-tree-node-disabled");
36657         }
36658     },
36659
36660     onSelectedChange : function(state){
36661         if(state){
36662             this.focus();
36663             this.addClass("x-tree-selected");
36664         }else{
36665             //this.blur();
36666             this.removeClass("x-tree-selected");
36667         }
36668     },
36669
36670     onMove : function(tree, node, oldParent, newParent, index, refNode){
36671         this.childIndent = null;
36672         if(this.rendered){
36673             var targetNode = newParent.ui.getContainer();
36674             if(!targetNode){//target not rendered
36675                 this.holder = document.createElement("div");
36676                 this.holder.appendChild(this.wrap);
36677                 return;
36678             }
36679             var insertBefore = refNode ? refNode.ui.getEl() : null;
36680             if(insertBefore){
36681                 targetNode.insertBefore(this.wrap, insertBefore);
36682             }else{
36683                 targetNode.appendChild(this.wrap);
36684             }
36685             this.node.renderIndent(true);
36686         }
36687     },
36688
36689     addClass : function(cls){
36690         if(this.elNode){
36691             Roo.fly(this.elNode).addClass(cls);
36692         }
36693     },
36694
36695     removeClass : function(cls){
36696         if(this.elNode){
36697             Roo.fly(this.elNode).removeClass(cls);
36698         }
36699     },
36700
36701     remove : function(){
36702         if(this.rendered){
36703             this.holder = document.createElement("div");
36704             this.holder.appendChild(this.wrap);
36705         }
36706     },
36707
36708     fireEvent : function(){
36709         return this.node.fireEvent.apply(this.node, arguments);
36710     },
36711
36712     initEvents : function(){
36713         this.node.on("move", this.onMove, this);
36714         var E = Roo.EventManager;
36715         var a = this.anchor;
36716
36717         var el = Roo.fly(a, '_treeui');
36718
36719         if(Roo.isOpera){ // opera render bug ignores the CSS
36720             el.setStyle("text-decoration", "none");
36721         }
36722
36723         el.on("click", this.onClick, this);
36724         el.on("dblclick", this.onDblClick, this);
36725
36726         if(this.checkbox){
36727             Roo.EventManager.on(this.checkbox,
36728                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36729         }
36730
36731         el.on("contextmenu", this.onContextMenu, this);
36732
36733         var icon = Roo.fly(this.iconNode);
36734         icon.on("click", this.onClick, this);
36735         icon.on("dblclick", this.onDblClick, this);
36736         icon.on("contextmenu", this.onContextMenu, this);
36737         E.on(this.ecNode, "click", this.ecClick, this, true);
36738
36739         if(this.node.disabled){
36740             this.addClass("x-tree-node-disabled");
36741         }
36742         if(this.node.hidden){
36743             this.addClass("x-tree-node-disabled");
36744         }
36745         var ot = this.node.getOwnerTree();
36746         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36747         if(dd && (!this.node.isRoot || ot.rootVisible)){
36748             Roo.dd.Registry.register(this.elNode, {
36749                 node: this.node,
36750                 handles: this.getDDHandles(),
36751                 isHandle: false
36752             });
36753         }
36754     },
36755
36756     getDDHandles : function(){
36757         return [this.iconNode, this.textNode];
36758     },
36759
36760     hide : function(){
36761         if(this.rendered){
36762             this.wrap.style.display = "none";
36763         }
36764     },
36765
36766     show : function(){
36767         if(this.rendered){
36768             this.wrap.style.display = "";
36769         }
36770     },
36771
36772     onContextMenu : function(e){
36773         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36774             e.preventDefault();
36775             this.focus();
36776             this.fireEvent("contextmenu", this.node, e);
36777         }
36778     },
36779
36780     onClick : function(e){
36781         if(this.dropping){
36782             e.stopEvent();
36783             return;
36784         }
36785         if(this.fireEvent("beforeclick", this.node, e) !== false){
36786             if(!this.disabled && this.node.attributes.href){
36787                 this.fireEvent("click", this.node, e);
36788                 return;
36789             }
36790             e.preventDefault();
36791             if(this.disabled){
36792                 return;
36793             }
36794
36795             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36796                 this.node.toggle();
36797             }
36798
36799             this.fireEvent("click", this.node, e);
36800         }else{
36801             e.stopEvent();
36802         }
36803     },
36804
36805     onDblClick : function(e){
36806         e.preventDefault();
36807         if(this.disabled){
36808             return;
36809         }
36810         if(this.checkbox){
36811             this.toggleCheck();
36812         }
36813         if(!this.animating && this.node.hasChildNodes()){
36814             this.node.toggle();
36815         }
36816         this.fireEvent("dblclick", this.node, e);
36817     },
36818
36819     onCheckChange : function(){
36820         var checked = this.checkbox.checked;
36821         this.node.attributes.checked = checked;
36822         this.fireEvent('checkchange', this.node, checked);
36823     },
36824
36825     ecClick : function(e){
36826         if(!this.animating && this.node.hasChildNodes()){
36827             this.node.toggle();
36828         }
36829     },
36830
36831     startDrop : function(){
36832         this.dropping = true;
36833     },
36834
36835     // delayed drop so the click event doesn't get fired on a drop
36836     endDrop : function(){
36837        setTimeout(function(){
36838            this.dropping = false;
36839        }.createDelegate(this), 50);
36840     },
36841
36842     expand : function(){
36843         this.updateExpandIcon();
36844         this.ctNode.style.display = "";
36845     },
36846
36847     focus : function(){
36848         if(!this.node.preventHScroll){
36849             try{this.anchor.focus();
36850             }catch(e){}
36851         }else if(!Roo.isIE){
36852             try{
36853                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36854                 var l = noscroll.scrollLeft;
36855                 this.anchor.focus();
36856                 noscroll.scrollLeft = l;
36857             }catch(e){}
36858         }
36859     },
36860
36861     toggleCheck : function(value){
36862         var cb = this.checkbox;
36863         if(cb){
36864             cb.checked = (value === undefined ? !cb.checked : value);
36865         }
36866     },
36867
36868     blur : function(){
36869         try{
36870             this.anchor.blur();
36871         }catch(e){}
36872     },
36873
36874     animExpand : function(callback){
36875         var ct = Roo.get(this.ctNode);
36876         ct.stopFx();
36877         if(!this.node.hasChildNodes()){
36878             this.updateExpandIcon();
36879             this.ctNode.style.display = "";
36880             Roo.callback(callback);
36881             return;
36882         }
36883         this.animating = true;
36884         this.updateExpandIcon();
36885
36886         ct.slideIn('t', {
36887            callback : function(){
36888                this.animating = false;
36889                Roo.callback(callback);
36890             },
36891             scope: this,
36892             duration: this.node.ownerTree.duration || .25
36893         });
36894     },
36895
36896     highlight : function(){
36897         var tree = this.node.getOwnerTree();
36898         Roo.fly(this.wrap).highlight(
36899             tree.hlColor || "C3DAF9",
36900             {endColor: tree.hlBaseColor}
36901         );
36902     },
36903
36904     collapse : function(){
36905         this.updateExpandIcon();
36906         this.ctNode.style.display = "none";
36907     },
36908
36909     animCollapse : function(callback){
36910         var ct = Roo.get(this.ctNode);
36911         ct.enableDisplayMode('block');
36912         ct.stopFx();
36913
36914         this.animating = true;
36915         this.updateExpandIcon();
36916
36917         ct.slideOut('t', {
36918             callback : function(){
36919                this.animating = false;
36920                Roo.callback(callback);
36921             },
36922             scope: this,
36923             duration: this.node.ownerTree.duration || .25
36924         });
36925     },
36926
36927     getContainer : function(){
36928         return this.ctNode;
36929     },
36930
36931     getEl : function(){
36932         return this.wrap;
36933     },
36934
36935     appendDDGhost : function(ghostNode){
36936         ghostNode.appendChild(this.elNode.cloneNode(true));
36937     },
36938
36939     getDDRepairXY : function(){
36940         return Roo.lib.Dom.getXY(this.iconNode);
36941     },
36942
36943     onRender : function(){
36944         this.render();
36945     },
36946
36947     render : function(bulkRender){
36948         var n = this.node, a = n.attributes;
36949         var targetNode = n.parentNode ?
36950               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36951
36952         if(!this.rendered){
36953             this.rendered = true;
36954
36955             this.renderElements(n, a, targetNode, bulkRender);
36956
36957             if(a.qtip){
36958                if(this.textNode.setAttributeNS){
36959                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36960                    if(a.qtipTitle){
36961                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36962                    }
36963                }else{
36964                    this.textNode.setAttribute("ext:qtip", a.qtip);
36965                    if(a.qtipTitle){
36966                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36967                    }
36968                }
36969             }else if(a.qtipCfg){
36970                 a.qtipCfg.target = Roo.id(this.textNode);
36971                 Roo.QuickTips.register(a.qtipCfg);
36972             }
36973             this.initEvents();
36974             if(!this.node.expanded){
36975                 this.updateExpandIcon();
36976             }
36977         }else{
36978             if(bulkRender === true) {
36979                 targetNode.appendChild(this.wrap);
36980             }
36981         }
36982     },
36983
36984     renderElements : function(n, a, targetNode, bulkRender)
36985     {
36986         // add some indent caching, this helps performance when rendering a large tree
36987         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36988         var t = n.getOwnerTree();
36989         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36990         if (typeof(n.attributes.html) != 'undefined') {
36991             txt = n.attributes.html;
36992         }
36993         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36994         var cb = typeof a.checked == 'boolean';
36995         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36996         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36997             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36998             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36999             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37000             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37001             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37002              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37003                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37004             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37005             "</li>"];
37006
37007         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37008             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37009                                 n.nextSibling.ui.getEl(), buf.join(""));
37010         }else{
37011             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37012         }
37013
37014         this.elNode = this.wrap.childNodes[0];
37015         this.ctNode = this.wrap.childNodes[1];
37016         var cs = this.elNode.childNodes;
37017         this.indentNode = cs[0];
37018         this.ecNode = cs[1];
37019         this.iconNode = cs[2];
37020         var index = 3;
37021         if(cb){
37022             this.checkbox = cs[3];
37023             index++;
37024         }
37025         this.anchor = cs[index];
37026         this.textNode = cs[index].firstChild;
37027     },
37028
37029     getAnchor : function(){
37030         return this.anchor;
37031     },
37032
37033     getTextEl : function(){
37034         return this.textNode;
37035     },
37036
37037     getIconEl : function(){
37038         return this.iconNode;
37039     },
37040
37041     isChecked : function(){
37042         return this.checkbox ? this.checkbox.checked : false;
37043     },
37044
37045     updateExpandIcon : function(){
37046         if(this.rendered){
37047             var n = this.node, c1, c2;
37048             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37049             var hasChild = n.hasChildNodes();
37050             if(hasChild){
37051                 if(n.expanded){
37052                     cls += "-minus";
37053                     c1 = "x-tree-node-collapsed";
37054                     c2 = "x-tree-node-expanded";
37055                 }else{
37056                     cls += "-plus";
37057                     c1 = "x-tree-node-expanded";
37058                     c2 = "x-tree-node-collapsed";
37059                 }
37060                 if(this.wasLeaf){
37061                     this.removeClass("x-tree-node-leaf");
37062                     this.wasLeaf = false;
37063                 }
37064                 if(this.c1 != c1 || this.c2 != c2){
37065                     Roo.fly(this.elNode).replaceClass(c1, c2);
37066                     this.c1 = c1; this.c2 = c2;
37067                 }
37068             }else{
37069                 // this changes non-leafs into leafs if they have no children.
37070                 // it's not very rational behaviour..
37071                 
37072                 if(!this.wasLeaf && this.node.leaf){
37073                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37074                     delete this.c1;
37075                     delete this.c2;
37076                     this.wasLeaf = true;
37077                 }
37078             }
37079             var ecc = "x-tree-ec-icon "+cls;
37080             if(this.ecc != ecc){
37081                 this.ecNode.className = ecc;
37082                 this.ecc = ecc;
37083             }
37084         }
37085     },
37086
37087     getChildIndent : function(){
37088         if(!this.childIndent){
37089             var buf = [];
37090             var p = this.node;
37091             while(p){
37092                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37093                     if(!p.isLast()) {
37094                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37095                     } else {
37096                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37097                     }
37098                 }
37099                 p = p.parentNode;
37100             }
37101             this.childIndent = buf.join("");
37102         }
37103         return this.childIndent;
37104     },
37105
37106     renderIndent : function(){
37107         if(this.rendered){
37108             var indent = "";
37109             var p = this.node.parentNode;
37110             if(p){
37111                 indent = p.ui.getChildIndent();
37112             }
37113             if(this.indentMarkup != indent){ // don't rerender if not required
37114                 this.indentNode.innerHTML = indent;
37115                 this.indentMarkup = indent;
37116             }
37117             this.updateExpandIcon();
37118         }
37119     }
37120 };
37121
37122 Roo.tree.RootTreeNodeUI = function(){
37123     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37124 };
37125 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37126     render : function(){
37127         if(!this.rendered){
37128             var targetNode = this.node.ownerTree.innerCt.dom;
37129             this.node.expanded = true;
37130             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37131             this.wrap = this.ctNode = targetNode.firstChild;
37132         }
37133     },
37134     collapse : function(){
37135     },
37136     expand : function(){
37137     }
37138 });/*
37139  * Based on:
37140  * Ext JS Library 1.1.1
37141  * Copyright(c) 2006-2007, Ext JS, LLC.
37142  *
37143  * Originally Released Under LGPL - original licence link has changed is not relivant.
37144  *
37145  * Fork - LGPL
37146  * <script type="text/javascript">
37147  */
37148 /**
37149  * @class Roo.tree.TreeLoader
37150  * @extends Roo.util.Observable
37151  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37152  * nodes from a specified URL. The response must be a javascript Array definition
37153  * who's elements are node definition objects. eg:
37154  * <pre><code>
37155 {  success : true,
37156    data :      [
37157    
37158     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37159     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37160     ]
37161 }
37162
37163
37164 </code></pre>
37165  * <br><br>
37166  * The old style respose with just an array is still supported, but not recommended.
37167  * <br><br>
37168  *
37169  * A server request is sent, and child nodes are loaded only when a node is expanded.
37170  * The loading node's id is passed to the server under the parameter name "node" to
37171  * enable the server to produce the correct child nodes.
37172  * <br><br>
37173  * To pass extra parameters, an event handler may be attached to the "beforeload"
37174  * event, and the parameters specified in the TreeLoader's baseParams property:
37175  * <pre><code>
37176     myTreeLoader.on("beforeload", function(treeLoader, node) {
37177         this.baseParams.category = node.attributes.category;
37178     }, this);
37179     
37180 </code></pre>
37181  *
37182  * This would pass an HTTP parameter called "category" to the server containing
37183  * the value of the Node's "category" attribute.
37184  * @constructor
37185  * Creates a new Treeloader.
37186  * @param {Object} config A config object containing config properties.
37187  */
37188 Roo.tree.TreeLoader = function(config){
37189     this.baseParams = {};
37190     this.requestMethod = "POST";
37191     Roo.apply(this, config);
37192
37193     this.addEvents({
37194     
37195         /**
37196          * @event beforeload
37197          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37198          * @param {Object} This TreeLoader object.
37199          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37200          * @param {Object} callback The callback function specified in the {@link #load} call.
37201          */
37202         beforeload : true,
37203         /**
37204          * @event load
37205          * Fires when the node has been successfuly loaded.
37206          * @param {Object} This TreeLoader object.
37207          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37208          * @param {Object} response The response object containing the data from the server.
37209          */
37210         load : true,
37211         /**
37212          * @event loadexception
37213          * Fires if the network request failed.
37214          * @param {Object} This TreeLoader object.
37215          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37216          * @param {Object} response The response object containing the data from the server.
37217          */
37218         loadexception : true,
37219         /**
37220          * @event create
37221          * Fires before a node is created, enabling you to return custom Node types 
37222          * @param {Object} This TreeLoader object.
37223          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37224          */
37225         create : true
37226     });
37227
37228     Roo.tree.TreeLoader.superclass.constructor.call(this);
37229 };
37230
37231 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37232     /**
37233     * @cfg {String} dataUrl The URL from which to request a Json string which
37234     * specifies an array of node definition object representing the child nodes
37235     * to be loaded.
37236     */
37237     /**
37238     * @cfg {String} requestMethod either GET or POST
37239     * defaults to POST (due to BC)
37240     * to be loaded.
37241     */
37242     /**
37243     * @cfg {Object} baseParams (optional) An object containing properties which
37244     * specify HTTP parameters to be passed to each request for child nodes.
37245     */
37246     /**
37247     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37248     * created by this loader. If the attributes sent by the server have an attribute in this object,
37249     * they take priority.
37250     */
37251     /**
37252     * @cfg {Object} uiProviders (optional) An object containing properties which
37253     * 
37254     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37255     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37256     * <i>uiProvider</i> attribute of a returned child node is a string rather
37257     * than a reference to a TreeNodeUI implementation, this that string value
37258     * is used as a property name in the uiProviders object. You can define the provider named
37259     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37260     */
37261     uiProviders : {},
37262
37263     /**
37264     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37265     * child nodes before loading.
37266     */
37267     clearOnLoad : true,
37268
37269     /**
37270     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37271     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37272     * Grid query { data : [ .....] }
37273     */
37274     
37275     root : false,
37276      /**
37277     * @cfg {String} queryParam (optional) 
37278     * Name of the query as it will be passed on the querystring (defaults to 'node')
37279     * eg. the request will be ?node=[id]
37280     */
37281     
37282     
37283     queryParam: false,
37284     
37285     /**
37286      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37287      * This is called automatically when a node is expanded, but may be used to reload
37288      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37289      * @param {Roo.tree.TreeNode} node
37290      * @param {Function} callback
37291      */
37292     load : function(node, callback){
37293         if(this.clearOnLoad){
37294             while(node.firstChild){
37295                 node.removeChild(node.firstChild);
37296             }
37297         }
37298         if(node.attributes.children){ // preloaded json children
37299             var cs = node.attributes.children;
37300             for(var i = 0, len = cs.length; i < len; i++){
37301                 node.appendChild(this.createNode(cs[i]));
37302             }
37303             if(typeof callback == "function"){
37304                 callback();
37305             }
37306         }else if(this.dataUrl){
37307             this.requestData(node, callback);
37308         }
37309     },
37310
37311     getParams: function(node){
37312         var buf = [], bp = this.baseParams;
37313         for(var key in bp){
37314             if(typeof bp[key] != "function"){
37315                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37316             }
37317         }
37318         var n = this.queryParam === false ? 'node' : this.queryParam;
37319         buf.push(n + "=", encodeURIComponent(node.id));
37320         return buf.join("");
37321     },
37322
37323     requestData : function(node, callback){
37324         if(this.fireEvent("beforeload", this, node, callback) !== false){
37325             this.transId = Roo.Ajax.request({
37326                 method:this.requestMethod,
37327                 url: this.dataUrl||this.url,
37328                 success: this.handleResponse,
37329                 failure: this.handleFailure,
37330                 scope: this,
37331                 argument: {callback: callback, node: node},
37332                 params: this.getParams(node)
37333             });
37334         }else{
37335             // if the load is cancelled, make sure we notify
37336             // the node that we are done
37337             if(typeof callback == "function"){
37338                 callback();
37339             }
37340         }
37341     },
37342
37343     isLoading : function(){
37344         return this.transId ? true : false;
37345     },
37346
37347     abort : function(){
37348         if(this.isLoading()){
37349             Roo.Ajax.abort(this.transId);
37350         }
37351     },
37352
37353     // private
37354     createNode : function(attr)
37355     {
37356         // apply baseAttrs, nice idea Corey!
37357         if(this.baseAttrs){
37358             Roo.applyIf(attr, this.baseAttrs);
37359         }
37360         if(this.applyLoader !== false){
37361             attr.loader = this;
37362         }
37363         // uiProvider = depreciated..
37364         
37365         if(typeof(attr.uiProvider) == 'string'){
37366            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37367                 /**  eval:var:attr */ eval(attr.uiProvider);
37368         }
37369         if(typeof(this.uiProviders['default']) != 'undefined') {
37370             attr.uiProvider = this.uiProviders['default'];
37371         }
37372         
37373         this.fireEvent('create', this, attr);
37374         
37375         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37376         return(attr.leaf ?
37377                         new Roo.tree.TreeNode(attr) :
37378                         new Roo.tree.AsyncTreeNode(attr));
37379     },
37380
37381     processResponse : function(response, node, callback)
37382     {
37383         var json = response.responseText;
37384         try {
37385             
37386             var o = Roo.decode(json);
37387             
37388             if (this.root === false && typeof(o.success) != undefined) {
37389                 this.root = 'data'; // the default behaviour for list like data..
37390                 }
37391                 
37392             if (this.root !== false &&  !o.success) {
37393                 // it's a failure condition.
37394                 var a = response.argument;
37395                 this.fireEvent("loadexception", this, a.node, response);
37396                 Roo.log("Load failed - should have a handler really");
37397                 return;
37398             }
37399             
37400             
37401             
37402             if (this.root !== false) {
37403                  o = o[this.root];
37404             }
37405             
37406             for(var i = 0, len = o.length; i < len; i++){
37407                 var n = this.createNode(o[i]);
37408                 if(n){
37409                     node.appendChild(n);
37410                 }
37411             }
37412             if(typeof callback == "function"){
37413                 callback(this, node);
37414             }
37415         }catch(e){
37416             this.handleFailure(response);
37417         }
37418     },
37419
37420     handleResponse : function(response){
37421         this.transId = false;
37422         var a = response.argument;
37423         this.processResponse(response, a.node, a.callback);
37424         this.fireEvent("load", this, a.node, response);
37425     },
37426
37427     handleFailure : function(response)
37428     {
37429         // should handle failure better..
37430         this.transId = false;
37431         var a = response.argument;
37432         this.fireEvent("loadexception", this, a.node, response);
37433         if(typeof a.callback == "function"){
37434             a.callback(this, a.node);
37435         }
37436     }
37437 });/*
37438  * Based on:
37439  * Ext JS Library 1.1.1
37440  * Copyright(c) 2006-2007, Ext JS, LLC.
37441  *
37442  * Originally Released Under LGPL - original licence link has changed is not relivant.
37443  *
37444  * Fork - LGPL
37445  * <script type="text/javascript">
37446  */
37447
37448 /**
37449 * @class Roo.tree.TreeFilter
37450 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37451 * @param {TreePanel} tree
37452 * @param {Object} config (optional)
37453  */
37454 Roo.tree.TreeFilter = function(tree, config){
37455     this.tree = tree;
37456     this.filtered = {};
37457     Roo.apply(this, config);
37458 };
37459
37460 Roo.tree.TreeFilter.prototype = {
37461     clearBlank:false,
37462     reverse:false,
37463     autoClear:false,
37464     remove:false,
37465
37466      /**
37467      * Filter the data by a specific attribute.
37468      * @param {String/RegExp} value Either string that the attribute value
37469      * should start with or a RegExp to test against the attribute
37470      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37471      * @param {TreeNode} startNode (optional) The node to start the filter at.
37472      */
37473     filter : function(value, attr, startNode){
37474         attr = attr || "text";
37475         var f;
37476         if(typeof value == "string"){
37477             var vlen = value.length;
37478             // auto clear empty filter
37479             if(vlen == 0 && this.clearBlank){
37480                 this.clear();
37481                 return;
37482             }
37483             value = value.toLowerCase();
37484             f = function(n){
37485                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37486             };
37487         }else if(value.exec){ // regex?
37488             f = function(n){
37489                 return value.test(n.attributes[attr]);
37490             };
37491         }else{
37492             throw 'Illegal filter type, must be string or regex';
37493         }
37494         this.filterBy(f, null, startNode);
37495         },
37496
37497     /**
37498      * Filter by a function. The passed function will be called with each
37499      * node in the tree (or from the startNode). If the function returns true, the node is kept
37500      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37501      * @param {Function} fn The filter function
37502      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37503      */
37504     filterBy : function(fn, scope, startNode){
37505         startNode = startNode || this.tree.root;
37506         if(this.autoClear){
37507             this.clear();
37508         }
37509         var af = this.filtered, rv = this.reverse;
37510         var f = function(n){
37511             if(n == startNode){
37512                 return true;
37513             }
37514             if(af[n.id]){
37515                 return false;
37516             }
37517             var m = fn.call(scope || n, n);
37518             if(!m || rv){
37519                 af[n.id] = n;
37520                 n.ui.hide();
37521                 return false;
37522             }
37523             return true;
37524         };
37525         startNode.cascade(f);
37526         if(this.remove){
37527            for(var id in af){
37528                if(typeof id != "function"){
37529                    var n = af[id];
37530                    if(n && n.parentNode){
37531                        n.parentNode.removeChild(n);
37532                    }
37533                }
37534            }
37535         }
37536     },
37537
37538     /**
37539      * Clears the current filter. Note: with the "remove" option
37540      * set a filter cannot be cleared.
37541      */
37542     clear : function(){
37543         var t = this.tree;
37544         var af = this.filtered;
37545         for(var id in af){
37546             if(typeof id != "function"){
37547                 var n = af[id];
37548                 if(n){
37549                     n.ui.show();
37550                 }
37551             }
37552         }
37553         this.filtered = {};
37554     }
37555 };
37556 /*
37557  * Based on:
37558  * Ext JS Library 1.1.1
37559  * Copyright(c) 2006-2007, Ext JS, LLC.
37560  *
37561  * Originally Released Under LGPL - original licence link has changed is not relivant.
37562  *
37563  * Fork - LGPL
37564  * <script type="text/javascript">
37565  */
37566  
37567
37568 /**
37569  * @class Roo.tree.TreeSorter
37570  * Provides sorting of nodes in a TreePanel
37571  * 
37572  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37573  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37574  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37575  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37576  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37577  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37578  * @constructor
37579  * @param {TreePanel} tree
37580  * @param {Object} config
37581  */
37582 Roo.tree.TreeSorter = function(tree, config){
37583     Roo.apply(this, config);
37584     tree.on("beforechildrenrendered", this.doSort, this);
37585     tree.on("append", this.updateSort, this);
37586     tree.on("insert", this.updateSort, this);
37587     
37588     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37589     var p = this.property || "text";
37590     var sortType = this.sortType;
37591     var fs = this.folderSort;
37592     var cs = this.caseSensitive === true;
37593     var leafAttr = this.leafAttr || 'leaf';
37594
37595     this.sortFn = function(n1, n2){
37596         if(fs){
37597             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37598                 return 1;
37599             }
37600             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37601                 return -1;
37602             }
37603         }
37604         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37605         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37606         if(v1 < v2){
37607                         return dsc ? +1 : -1;
37608                 }else if(v1 > v2){
37609                         return dsc ? -1 : +1;
37610         }else{
37611                 return 0;
37612         }
37613     };
37614 };
37615
37616 Roo.tree.TreeSorter.prototype = {
37617     doSort : function(node){
37618         node.sort(this.sortFn);
37619     },
37620     
37621     compareNodes : function(n1, n2){
37622         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37623     },
37624     
37625     updateSort : function(tree, node){
37626         if(node.childrenRendered){
37627             this.doSort.defer(1, this, [node]);
37628         }
37629     }
37630 };/*
37631  * Based on:
37632  * Ext JS Library 1.1.1
37633  * Copyright(c) 2006-2007, Ext JS, LLC.
37634  *
37635  * Originally Released Under LGPL - original licence link has changed is not relivant.
37636  *
37637  * Fork - LGPL
37638  * <script type="text/javascript">
37639  */
37640
37641 if(Roo.dd.DropZone){
37642     
37643 Roo.tree.TreeDropZone = function(tree, config){
37644     this.allowParentInsert = false;
37645     this.allowContainerDrop = false;
37646     this.appendOnly = false;
37647     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37648     this.tree = tree;
37649     this.lastInsertClass = "x-tree-no-status";
37650     this.dragOverData = {};
37651 };
37652
37653 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37654     ddGroup : "TreeDD",
37655     scroll:  true,
37656     
37657     expandDelay : 1000,
37658     
37659     expandNode : function(node){
37660         if(node.hasChildNodes() && !node.isExpanded()){
37661             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37662         }
37663     },
37664     
37665     queueExpand : function(node){
37666         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37667     },
37668     
37669     cancelExpand : function(){
37670         if(this.expandProcId){
37671             clearTimeout(this.expandProcId);
37672             this.expandProcId = false;
37673         }
37674     },
37675     
37676     isValidDropPoint : function(n, pt, dd, e, data){
37677         if(!n || !data){ return false; }
37678         var targetNode = n.node;
37679         var dropNode = data.node;
37680         // default drop rules
37681         if(!(targetNode && targetNode.isTarget && pt)){
37682             return false;
37683         }
37684         if(pt == "append" && targetNode.allowChildren === false){
37685             return false;
37686         }
37687         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37688             return false;
37689         }
37690         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37691             return false;
37692         }
37693         // reuse the object
37694         var overEvent = this.dragOverData;
37695         overEvent.tree = this.tree;
37696         overEvent.target = targetNode;
37697         overEvent.data = data;
37698         overEvent.point = pt;
37699         overEvent.source = dd;
37700         overEvent.rawEvent = e;
37701         overEvent.dropNode = dropNode;
37702         overEvent.cancel = false;  
37703         var result = this.tree.fireEvent("nodedragover", overEvent);
37704         return overEvent.cancel === false && result !== false;
37705     },
37706     
37707     getDropPoint : function(e, n, dd)
37708     {
37709         var tn = n.node;
37710         if(tn.isRoot){
37711             return tn.allowChildren !== false ? "append" : false; // always append for root
37712         }
37713         var dragEl = n.ddel;
37714         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37715         var y = Roo.lib.Event.getPageY(e);
37716         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37717         
37718         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37719         var noAppend = tn.allowChildren === false;
37720         if(this.appendOnly || tn.parentNode.allowChildren === false){
37721             return noAppend ? false : "append";
37722         }
37723         var noBelow = false;
37724         if(!this.allowParentInsert){
37725             noBelow = tn.hasChildNodes() && tn.isExpanded();
37726         }
37727         var q = (b - t) / (noAppend ? 2 : 3);
37728         if(y >= t && y < (t + q)){
37729             return "above";
37730         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37731             return "below";
37732         }else{
37733             return "append";
37734         }
37735     },
37736     
37737     onNodeEnter : function(n, dd, e, data)
37738     {
37739         this.cancelExpand();
37740     },
37741     
37742     onNodeOver : function(n, dd, e, data)
37743     {
37744        
37745         var pt = this.getDropPoint(e, n, dd);
37746         var node = n.node;
37747         
37748         // auto node expand check
37749         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37750             this.queueExpand(node);
37751         }else if(pt != "append"){
37752             this.cancelExpand();
37753         }
37754         
37755         // set the insert point style on the target node
37756         var returnCls = this.dropNotAllowed;
37757         if(this.isValidDropPoint(n, pt, dd, e, data)){
37758            if(pt){
37759                var el = n.ddel;
37760                var cls;
37761                if(pt == "above"){
37762                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37763                    cls = "x-tree-drag-insert-above";
37764                }else if(pt == "below"){
37765                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37766                    cls = "x-tree-drag-insert-below";
37767                }else{
37768                    returnCls = "x-tree-drop-ok-append";
37769                    cls = "x-tree-drag-append";
37770                }
37771                if(this.lastInsertClass != cls){
37772                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37773                    this.lastInsertClass = cls;
37774                }
37775            }
37776        }
37777        return returnCls;
37778     },
37779     
37780     onNodeOut : function(n, dd, e, data){
37781         
37782         this.cancelExpand();
37783         this.removeDropIndicators(n);
37784     },
37785     
37786     onNodeDrop : function(n, dd, e, data){
37787         var point = this.getDropPoint(e, n, dd);
37788         var targetNode = n.node;
37789         targetNode.ui.startDrop();
37790         if(!this.isValidDropPoint(n, point, dd, e, data)){
37791             targetNode.ui.endDrop();
37792             return false;
37793         }
37794         // first try to find the drop node
37795         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37796         var dropEvent = {
37797             tree : this.tree,
37798             target: targetNode,
37799             data: data,
37800             point: point,
37801             source: dd,
37802             rawEvent: e,
37803             dropNode: dropNode,
37804             cancel: !dropNode   
37805         };
37806         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37807         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37808             targetNode.ui.endDrop();
37809             return false;
37810         }
37811         // allow target changing
37812         targetNode = dropEvent.target;
37813         if(point == "append" && !targetNode.isExpanded()){
37814             targetNode.expand(false, null, function(){
37815                 this.completeDrop(dropEvent);
37816             }.createDelegate(this));
37817         }else{
37818             this.completeDrop(dropEvent);
37819         }
37820         return true;
37821     },
37822     
37823     completeDrop : function(de){
37824         var ns = de.dropNode, p = de.point, t = de.target;
37825         if(!(ns instanceof Array)){
37826             ns = [ns];
37827         }
37828         var n;
37829         for(var i = 0, len = ns.length; i < len; i++){
37830             n = ns[i];
37831             if(p == "above"){
37832                 t.parentNode.insertBefore(n, t);
37833             }else if(p == "below"){
37834                 t.parentNode.insertBefore(n, t.nextSibling);
37835             }else{
37836                 t.appendChild(n);
37837             }
37838         }
37839         n.ui.focus();
37840         if(this.tree.hlDrop){
37841             n.ui.highlight();
37842         }
37843         t.ui.endDrop();
37844         this.tree.fireEvent("nodedrop", de);
37845     },
37846     
37847     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37848         if(this.tree.hlDrop){
37849             dropNode.ui.focus();
37850             dropNode.ui.highlight();
37851         }
37852         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37853     },
37854     
37855     getTree : function(){
37856         return this.tree;
37857     },
37858     
37859     removeDropIndicators : function(n){
37860         if(n && n.ddel){
37861             var el = n.ddel;
37862             Roo.fly(el).removeClass([
37863                     "x-tree-drag-insert-above",
37864                     "x-tree-drag-insert-below",
37865                     "x-tree-drag-append"]);
37866             this.lastInsertClass = "_noclass";
37867         }
37868     },
37869     
37870     beforeDragDrop : function(target, e, id){
37871         this.cancelExpand();
37872         return true;
37873     },
37874     
37875     afterRepair : function(data){
37876         if(data && Roo.enableFx){
37877             data.node.ui.highlight();
37878         }
37879         this.hideProxy();
37880     } 
37881     
37882 });
37883
37884 }
37885 /*
37886  * Based on:
37887  * Ext JS Library 1.1.1
37888  * Copyright(c) 2006-2007, Ext JS, LLC.
37889  *
37890  * Originally Released Under LGPL - original licence link has changed is not relivant.
37891  *
37892  * Fork - LGPL
37893  * <script type="text/javascript">
37894  */
37895  
37896
37897 if(Roo.dd.DragZone){
37898 Roo.tree.TreeDragZone = function(tree, config){
37899     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37900     this.tree = tree;
37901 };
37902
37903 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37904     ddGroup : "TreeDD",
37905    
37906     onBeforeDrag : function(data, e){
37907         var n = data.node;
37908         return n && n.draggable && !n.disabled;
37909     },
37910      
37911     
37912     onInitDrag : function(e){
37913         var data = this.dragData;
37914         this.tree.getSelectionModel().select(data.node);
37915         this.proxy.update("");
37916         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37917         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37918     },
37919     
37920     getRepairXY : function(e, data){
37921         return data.node.ui.getDDRepairXY();
37922     },
37923     
37924     onEndDrag : function(data, e){
37925         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37926         
37927         
37928     },
37929     
37930     onValidDrop : function(dd, e, id){
37931         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37932         this.hideProxy();
37933     },
37934     
37935     beforeInvalidDrop : function(e, id){
37936         // this scrolls the original position back into view
37937         var sm = this.tree.getSelectionModel();
37938         sm.clearSelections();
37939         sm.select(this.dragData.node);
37940     }
37941 });
37942 }/*
37943  * Based on:
37944  * Ext JS Library 1.1.1
37945  * Copyright(c) 2006-2007, Ext JS, LLC.
37946  *
37947  * Originally Released Under LGPL - original licence link has changed is not relivant.
37948  *
37949  * Fork - LGPL
37950  * <script type="text/javascript">
37951  */
37952 /**
37953  * @class Roo.tree.TreeEditor
37954  * @extends Roo.Editor
37955  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37956  * as the editor field.
37957  * @constructor
37958  * @param {Object} config (used to be the tree panel.)
37959  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37960  * 
37961  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37962  * @cfg {Roo.form.TextField} field [required] The field configuration
37963  *
37964  * 
37965  */
37966 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37967     var tree = config;
37968     var field;
37969     if (oldconfig) { // old style..
37970         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37971     } else {
37972         // new style..
37973         tree = config.tree;
37974         config.field = config.field  || {};
37975         config.field.xtype = 'TextField';
37976         field = Roo.factory(config.field, Roo.form);
37977     }
37978     config = config || {};
37979     
37980     
37981     this.addEvents({
37982         /**
37983          * @event beforenodeedit
37984          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37985          * false from the handler of this event.
37986          * @param {Editor} this
37987          * @param {Roo.tree.Node} node 
37988          */
37989         "beforenodeedit" : true
37990     });
37991     
37992     //Roo.log(config);
37993     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37994
37995     this.tree = tree;
37996
37997     tree.on('beforeclick', this.beforeNodeClick, this);
37998     tree.getTreeEl().on('mousedown', this.hide, this);
37999     this.on('complete', this.updateNode, this);
38000     this.on('beforestartedit', this.fitToTree, this);
38001     this.on('startedit', this.bindScroll, this, {delay:10});
38002     this.on('specialkey', this.onSpecialKey, this);
38003 };
38004
38005 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38006     /**
38007      * @cfg {String} alignment
38008      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38009      */
38010     alignment: "l-l",
38011     // inherit
38012     autoSize: false,
38013     /**
38014      * @cfg {Boolean} hideEl
38015      * True to hide the bound element while the editor is displayed (defaults to false)
38016      */
38017     hideEl : false,
38018     /**
38019      * @cfg {String} cls
38020      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38021      */
38022     cls: "x-small-editor x-tree-editor",
38023     /**
38024      * @cfg {Boolean} shim
38025      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38026      */
38027     shim:false,
38028     // inherit
38029     shadow:"frame",
38030     /**
38031      * @cfg {Number} maxWidth
38032      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38033      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38034      * scroll and client offsets into account prior to each edit.
38035      */
38036     maxWidth: 250,
38037
38038     editDelay : 350,
38039
38040     // private
38041     fitToTree : function(ed, el){
38042         var td = this.tree.getTreeEl().dom, nd = el.dom;
38043         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38044             td.scrollLeft = nd.offsetLeft;
38045         }
38046         var w = Math.min(
38047                 this.maxWidth,
38048                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38049         this.setSize(w, '');
38050         
38051         return this.fireEvent('beforenodeedit', this, this.editNode);
38052         
38053     },
38054
38055     // private
38056     triggerEdit : function(node){
38057         this.completeEdit();
38058         this.editNode = node;
38059         this.startEdit(node.ui.textNode, node.text);
38060     },
38061
38062     // private
38063     bindScroll : function(){
38064         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38065     },
38066
38067     // private
38068     beforeNodeClick : function(node, e){
38069         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38070         this.lastClick = new Date();
38071         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38072             e.stopEvent();
38073             this.triggerEdit(node);
38074             return false;
38075         }
38076         return true;
38077     },
38078
38079     // private
38080     updateNode : function(ed, value){
38081         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38082         this.editNode.setText(value);
38083     },
38084
38085     // private
38086     onHide : function(){
38087         Roo.tree.TreeEditor.superclass.onHide.call(this);
38088         if(this.editNode){
38089             this.editNode.ui.focus();
38090         }
38091     },
38092
38093     // private
38094     onSpecialKey : function(field, e){
38095         var k = e.getKey();
38096         if(k == e.ESC){
38097             e.stopEvent();
38098             this.cancelEdit();
38099         }else if(k == e.ENTER && !e.hasModifier()){
38100             e.stopEvent();
38101             this.completeEdit();
38102         }
38103     }
38104 });//<Script type="text/javascript">
38105 /*
38106  * Based on:
38107  * Ext JS Library 1.1.1
38108  * Copyright(c) 2006-2007, Ext JS, LLC.
38109  *
38110  * Originally Released Under LGPL - original licence link has changed is not relivant.
38111  *
38112  * Fork - LGPL
38113  * <script type="text/javascript">
38114  */
38115  
38116 /**
38117  * Not documented??? - probably should be...
38118  */
38119
38120 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38121     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38122     
38123     renderElements : function(n, a, targetNode, bulkRender){
38124         //consel.log("renderElements?");
38125         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38126
38127         var t = n.getOwnerTree();
38128         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38129         
38130         var cols = t.columns;
38131         var bw = t.borderWidth;
38132         var c = cols[0];
38133         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38134          var cb = typeof a.checked == "boolean";
38135         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38136         var colcls = 'x-t-' + tid + '-c0';
38137         var buf = [
38138             '<li class="x-tree-node">',
38139             
38140                 
38141                 '<div class="x-tree-node-el ', a.cls,'">',
38142                     // extran...
38143                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38144                 
38145                 
38146                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38147                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38148                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38149                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38150                            (a.iconCls ? ' '+a.iconCls : ''),
38151                            '" unselectable="on" />',
38152                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38153                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38154                              
38155                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38156                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38157                             '<span unselectable="on" qtip="' + tx + '">',
38158                              tx,
38159                              '</span></a>' ,
38160                     '</div>',
38161                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38162                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38163                  ];
38164         for(var i = 1, len = cols.length; i < len; i++){
38165             c = cols[i];
38166             colcls = 'x-t-' + tid + '-c' +i;
38167             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38168             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38169                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38170                       "</div>");
38171          }
38172          
38173          buf.push(
38174             '</a>',
38175             '<div class="x-clear"></div></div>',
38176             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38177             "</li>");
38178         
38179         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38180             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38181                                 n.nextSibling.ui.getEl(), buf.join(""));
38182         }else{
38183             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38184         }
38185         var el = this.wrap.firstChild;
38186         this.elRow = el;
38187         this.elNode = el.firstChild;
38188         this.ranchor = el.childNodes[1];
38189         this.ctNode = this.wrap.childNodes[1];
38190         var cs = el.firstChild.childNodes;
38191         this.indentNode = cs[0];
38192         this.ecNode = cs[1];
38193         this.iconNode = cs[2];
38194         var index = 3;
38195         if(cb){
38196             this.checkbox = cs[3];
38197             index++;
38198         }
38199         this.anchor = cs[index];
38200         
38201         this.textNode = cs[index].firstChild;
38202         
38203         //el.on("click", this.onClick, this);
38204         //el.on("dblclick", this.onDblClick, this);
38205         
38206         
38207        // console.log(this);
38208     },
38209     initEvents : function(){
38210         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38211         
38212             
38213         var a = this.ranchor;
38214
38215         var el = Roo.get(a);
38216
38217         if(Roo.isOpera){ // opera render bug ignores the CSS
38218             el.setStyle("text-decoration", "none");
38219         }
38220
38221         el.on("click", this.onClick, this);
38222         el.on("dblclick", this.onDblClick, this);
38223         el.on("contextmenu", this.onContextMenu, this);
38224         
38225     },
38226     
38227     /*onSelectedChange : function(state){
38228         if(state){
38229             this.focus();
38230             this.addClass("x-tree-selected");
38231         }else{
38232             //this.blur();
38233             this.removeClass("x-tree-selected");
38234         }
38235     },*/
38236     addClass : function(cls){
38237         if(this.elRow){
38238             Roo.fly(this.elRow).addClass(cls);
38239         }
38240         
38241     },
38242     
38243     
38244     removeClass : function(cls){
38245         if(this.elRow){
38246             Roo.fly(this.elRow).removeClass(cls);
38247         }
38248     }
38249
38250     
38251     
38252 });//<Script type="text/javascript">
38253
38254 /*
38255  * Based on:
38256  * Ext JS Library 1.1.1
38257  * Copyright(c) 2006-2007, Ext JS, LLC.
38258  *
38259  * Originally Released Under LGPL - original licence link has changed is not relivant.
38260  *
38261  * Fork - LGPL
38262  * <script type="text/javascript">
38263  */
38264  
38265
38266 /**
38267  * @class Roo.tree.ColumnTree
38268  * @extends Roo.tree.TreePanel
38269  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38270  * @cfg {int} borderWidth  compined right/left border allowance
38271  * @constructor
38272  * @param {String/HTMLElement/Element} el The container element
38273  * @param {Object} config
38274  */
38275 Roo.tree.ColumnTree =  function(el, config)
38276 {
38277    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38278    this.addEvents({
38279         /**
38280         * @event resize
38281         * Fire this event on a container when it resizes
38282         * @param {int} w Width
38283         * @param {int} h Height
38284         */
38285        "resize" : true
38286     });
38287     this.on('resize', this.onResize, this);
38288 };
38289
38290 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38291     //lines:false,
38292     
38293     
38294     borderWidth: Roo.isBorderBox ? 0 : 2, 
38295     headEls : false,
38296     
38297     render : function(){
38298         // add the header.....
38299        
38300         Roo.tree.ColumnTree.superclass.render.apply(this);
38301         
38302         this.el.addClass('x-column-tree');
38303         
38304         this.headers = this.el.createChild(
38305             {cls:'x-tree-headers'},this.innerCt.dom);
38306    
38307         var cols = this.columns, c;
38308         var totalWidth = 0;
38309         this.headEls = [];
38310         var  len = cols.length;
38311         for(var i = 0; i < len; i++){
38312              c = cols[i];
38313              totalWidth += c.width;
38314             this.headEls.push(this.headers.createChild({
38315                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38316                  cn: {
38317                      cls:'x-tree-hd-text',
38318                      html: c.header
38319                  },
38320                  style:'width:'+(c.width-this.borderWidth)+'px;'
38321              }));
38322         }
38323         this.headers.createChild({cls:'x-clear'});
38324         // prevent floats from wrapping when clipped
38325         this.headers.setWidth(totalWidth);
38326         //this.innerCt.setWidth(totalWidth);
38327         this.innerCt.setStyle({ overflow: 'auto' });
38328         this.onResize(this.width, this.height);
38329              
38330         
38331     },
38332     onResize : function(w,h)
38333     {
38334         this.height = h;
38335         this.width = w;
38336         // resize cols..
38337         this.innerCt.setWidth(this.width);
38338         this.innerCt.setHeight(this.height-20);
38339         
38340         // headers...
38341         var cols = this.columns, c;
38342         var totalWidth = 0;
38343         var expEl = false;
38344         var len = cols.length;
38345         for(var i = 0; i < len; i++){
38346             c = cols[i];
38347             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38348                 // it's the expander..
38349                 expEl  = this.headEls[i];
38350                 continue;
38351             }
38352             totalWidth += c.width;
38353             
38354         }
38355         if (expEl) {
38356             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38357         }
38358         this.headers.setWidth(w-20);
38359
38360         
38361         
38362         
38363     }
38364 });
38365 /*
38366  * Based on:
38367  * Ext JS Library 1.1.1
38368  * Copyright(c) 2006-2007, Ext JS, LLC.
38369  *
38370  * Originally Released Under LGPL - original licence link has changed is not relivant.
38371  *
38372  * Fork - LGPL
38373  * <script type="text/javascript">
38374  */
38375  
38376 /**
38377  * @class Roo.menu.Menu
38378  * @extends Roo.util.Observable
38379  * @children Roo.menu.BaseItem
38380  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38381  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38382  * @constructor
38383  * Creates a new Menu
38384  * @param {Object} config Configuration options
38385  */
38386 Roo.menu.Menu = function(config){
38387     
38388     Roo.menu.Menu.superclass.constructor.call(this, config);
38389     
38390     this.id = this.id || Roo.id();
38391     this.addEvents({
38392         /**
38393          * @event beforeshow
38394          * Fires before this menu is displayed
38395          * @param {Roo.menu.Menu} this
38396          */
38397         beforeshow : true,
38398         /**
38399          * @event beforehide
38400          * Fires before this menu is hidden
38401          * @param {Roo.menu.Menu} this
38402          */
38403         beforehide : true,
38404         /**
38405          * @event show
38406          * Fires after this menu is displayed
38407          * @param {Roo.menu.Menu} this
38408          */
38409         show : true,
38410         /**
38411          * @event hide
38412          * Fires after this menu is hidden
38413          * @param {Roo.menu.Menu} this
38414          */
38415         hide : true,
38416         /**
38417          * @event click
38418          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38419          * @param {Roo.menu.Menu} this
38420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38421          * @param {Roo.EventObject} e
38422          */
38423         click : true,
38424         /**
38425          * @event mouseover
38426          * Fires when the mouse is hovering over this menu
38427          * @param {Roo.menu.Menu} this
38428          * @param {Roo.EventObject} e
38429          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38430          */
38431         mouseover : true,
38432         /**
38433          * @event mouseout
38434          * Fires when the mouse exits this menu
38435          * @param {Roo.menu.Menu} this
38436          * @param {Roo.EventObject} e
38437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38438          */
38439         mouseout : true,
38440         /**
38441          * @event itemclick
38442          * Fires when a menu item contained in this menu is clicked
38443          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38444          * @param {Roo.EventObject} e
38445          */
38446         itemclick: true
38447     });
38448     if (this.registerMenu) {
38449         Roo.menu.MenuMgr.register(this);
38450     }
38451     
38452     var mis = this.items;
38453     this.items = new Roo.util.MixedCollection();
38454     if(mis){
38455         this.add.apply(this, mis);
38456     }
38457 };
38458
38459 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38460     /**
38461      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38462      */
38463     minWidth : 120,
38464     /**
38465      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38466      * for bottom-right shadow (defaults to "sides")
38467      */
38468     shadow : "sides",
38469     /**
38470      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38471      * this menu (defaults to "tl-tr?")
38472      */
38473     subMenuAlign : "tl-tr?",
38474     /**
38475      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38476      * relative to its element of origin (defaults to "tl-bl?")
38477      */
38478     defaultAlign : "tl-bl?",
38479     /**
38480      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38481      */
38482     allowOtherMenus : false,
38483     /**
38484      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38485      */
38486     registerMenu : true,
38487
38488     hidden:true,
38489
38490     // private
38491     render : function(){
38492         if(this.el){
38493             return;
38494         }
38495         var el = this.el = new Roo.Layer({
38496             cls: "x-menu",
38497             shadow:this.shadow,
38498             constrain: false,
38499             parentEl: this.parentEl || document.body,
38500             zindex:15000
38501         });
38502
38503         this.keyNav = new Roo.menu.MenuNav(this);
38504
38505         if(this.plain){
38506             el.addClass("x-menu-plain");
38507         }
38508         if(this.cls){
38509             el.addClass(this.cls);
38510         }
38511         // generic focus element
38512         this.focusEl = el.createChild({
38513             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38514         });
38515         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38516         //disabling touch- as it's causing issues ..
38517         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38518         ul.on('click'   , this.onClick, this);
38519         
38520         
38521         ul.on("mouseover", this.onMouseOver, this);
38522         ul.on("mouseout", this.onMouseOut, this);
38523         this.items.each(function(item){
38524             if (item.hidden) {
38525                 return;
38526             }
38527             
38528             var li = document.createElement("li");
38529             li.className = "x-menu-list-item";
38530             ul.dom.appendChild(li);
38531             item.render(li, this);
38532         }, this);
38533         this.ul = ul;
38534         this.autoWidth();
38535     },
38536
38537     // private
38538     autoWidth : function(){
38539         var el = this.el, ul = this.ul;
38540         if(!el){
38541             return;
38542         }
38543         var w = this.width;
38544         if(w){
38545             el.setWidth(w);
38546         }else if(Roo.isIE){
38547             el.setWidth(this.minWidth);
38548             var t = el.dom.offsetWidth; // force recalc
38549             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38550         }
38551     },
38552
38553     // private
38554     delayAutoWidth : function(){
38555         if(this.rendered){
38556             if(!this.awTask){
38557                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38558             }
38559             this.awTask.delay(20);
38560         }
38561     },
38562
38563     // private
38564     findTargetItem : function(e){
38565         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38566         if(t && t.menuItemId){
38567             return this.items.get(t.menuItemId);
38568         }
38569     },
38570
38571     // private
38572     onClick : function(e){
38573         Roo.log("menu.onClick");
38574         var t = this.findTargetItem(e);
38575         if(!t){
38576             return;
38577         }
38578         Roo.log(e);
38579         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38580             if(t == this.activeItem && t.shouldDeactivate(e)){
38581                 this.activeItem.deactivate();
38582                 delete this.activeItem;
38583                 return;
38584             }
38585             if(t.canActivate){
38586                 this.setActiveItem(t, true);
38587             }
38588             return;
38589             
38590             
38591         }
38592         
38593         t.onClick(e);
38594         this.fireEvent("click", this, t, e);
38595     },
38596
38597     // private
38598     setActiveItem : function(item, autoExpand){
38599         if(item != this.activeItem){
38600             if(this.activeItem){
38601                 this.activeItem.deactivate();
38602             }
38603             this.activeItem = item;
38604             item.activate(autoExpand);
38605         }else if(autoExpand){
38606             item.expandMenu();
38607         }
38608     },
38609
38610     // private
38611     tryActivate : function(start, step){
38612         var items = this.items;
38613         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38614             var item = items.get(i);
38615             if(!item.disabled && item.canActivate){
38616                 this.setActiveItem(item, false);
38617                 return item;
38618             }
38619         }
38620         return false;
38621     },
38622
38623     // private
38624     onMouseOver : function(e){
38625         var t;
38626         if(t = this.findTargetItem(e)){
38627             if(t.canActivate && !t.disabled){
38628                 this.setActiveItem(t, true);
38629             }
38630         }
38631         this.fireEvent("mouseover", this, e, t);
38632     },
38633
38634     // private
38635     onMouseOut : function(e){
38636         var t;
38637         if(t = this.findTargetItem(e)){
38638             if(t == this.activeItem && t.shouldDeactivate(e)){
38639                 this.activeItem.deactivate();
38640                 delete this.activeItem;
38641             }
38642         }
38643         this.fireEvent("mouseout", this, e, t);
38644     },
38645
38646     /**
38647      * Read-only.  Returns true if the menu is currently displayed, else false.
38648      * @type Boolean
38649      */
38650     isVisible : function(){
38651         return this.el && !this.hidden;
38652     },
38653
38654     /**
38655      * Displays this menu relative to another element
38656      * @param {String/HTMLElement/Roo.Element} element The element to align to
38657      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38658      * the element (defaults to this.defaultAlign)
38659      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38660      */
38661     show : function(el, pos, parentMenu){
38662         this.parentMenu = parentMenu;
38663         if(!this.el){
38664             this.render();
38665         }
38666         this.fireEvent("beforeshow", this);
38667         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38668     },
38669
38670     /**
38671      * Displays this menu at a specific xy position
38672      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38673      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38674      */
38675     showAt : function(xy, parentMenu, /* private: */_e){
38676         this.parentMenu = parentMenu;
38677         if(!this.el){
38678             this.render();
38679         }
38680         if(_e !== false){
38681             this.fireEvent("beforeshow", this);
38682             xy = this.el.adjustForConstraints(xy);
38683         }
38684         this.el.setXY(xy);
38685         this.el.show();
38686         this.hidden = false;
38687         this.focus();
38688         this.fireEvent("show", this);
38689     },
38690
38691     focus : function(){
38692         if(!this.hidden){
38693             this.doFocus.defer(50, this);
38694         }
38695     },
38696
38697     doFocus : function(){
38698         if(!this.hidden){
38699             this.focusEl.focus();
38700         }
38701     },
38702
38703     /**
38704      * Hides this menu and optionally all parent menus
38705      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38706      */
38707     hide : function(deep){
38708         if(this.el && this.isVisible()){
38709             this.fireEvent("beforehide", this);
38710             if(this.activeItem){
38711                 this.activeItem.deactivate();
38712                 this.activeItem = null;
38713             }
38714             this.el.hide();
38715             this.hidden = true;
38716             this.fireEvent("hide", this);
38717         }
38718         if(deep === true && this.parentMenu){
38719             this.parentMenu.hide(true);
38720         }
38721     },
38722
38723     /**
38724      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38725      * Any of the following are valid:
38726      * <ul>
38727      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38728      * <li>An HTMLElement object which will be converted to a menu item</li>
38729      * <li>A menu item config object that will be created as a new menu item</li>
38730      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38731      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38732      * </ul>
38733      * Usage:
38734      * <pre><code>
38735 // Create the menu
38736 var menu = new Roo.menu.Menu();
38737
38738 // Create a menu item to add by reference
38739 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38740
38741 // Add a bunch of items at once using different methods.
38742 // Only the last item added will be returned.
38743 var item = menu.add(
38744     menuItem,                // add existing item by ref
38745     'Dynamic Item',          // new TextItem
38746     '-',                     // new separator
38747     { text: 'Config Item' }  // new item by config
38748 );
38749 </code></pre>
38750      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38751      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38752      */
38753     add : function(){
38754         var a = arguments, l = a.length, item;
38755         for(var i = 0; i < l; i++){
38756             var el = a[i];
38757             if ((typeof(el) == "object") && el.xtype && el.xns) {
38758                 el = Roo.factory(el, Roo.menu);
38759             }
38760             
38761             if(el.render){ // some kind of Item
38762                 item = this.addItem(el);
38763             }else if(typeof el == "string"){ // string
38764                 if(el == "separator" || el == "-"){
38765                     item = this.addSeparator();
38766                 }else{
38767                     item = this.addText(el);
38768                 }
38769             }else if(el.tagName || el.el){ // element
38770                 item = this.addElement(el);
38771             }else if(typeof el == "object"){ // must be menu item config?
38772                 item = this.addMenuItem(el);
38773             }
38774         }
38775         return item;
38776     },
38777
38778     /**
38779      * Returns this menu's underlying {@link Roo.Element} object
38780      * @return {Roo.Element} The element
38781      */
38782     getEl : function(){
38783         if(!this.el){
38784             this.render();
38785         }
38786         return this.el;
38787     },
38788
38789     /**
38790      * Adds a separator bar to the menu
38791      * @return {Roo.menu.Item} The menu item that was added
38792      */
38793     addSeparator : function(){
38794         return this.addItem(new Roo.menu.Separator());
38795     },
38796
38797     /**
38798      * Adds an {@link Roo.Element} object to the menu
38799      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38800      * @return {Roo.menu.Item} The menu item that was added
38801      */
38802     addElement : function(el){
38803         return this.addItem(new Roo.menu.BaseItem(el));
38804     },
38805
38806     /**
38807      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38808      * @param {Roo.menu.Item} item The menu item to add
38809      * @return {Roo.menu.Item} The menu item that was added
38810      */
38811     addItem : function(item){
38812         this.items.add(item);
38813         if(this.ul){
38814             var li = document.createElement("li");
38815             li.className = "x-menu-list-item";
38816             this.ul.dom.appendChild(li);
38817             item.render(li, this);
38818             this.delayAutoWidth();
38819         }
38820         return item;
38821     },
38822
38823     /**
38824      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38825      * @param {Object} config A MenuItem config object
38826      * @return {Roo.menu.Item} The menu item that was added
38827      */
38828     addMenuItem : function(config){
38829         if(!(config instanceof Roo.menu.Item)){
38830             if(typeof config.checked == "boolean"){ // must be check menu item config?
38831                 config = new Roo.menu.CheckItem(config);
38832             }else{
38833                 config = new Roo.menu.Item(config);
38834             }
38835         }
38836         return this.addItem(config);
38837     },
38838
38839     /**
38840      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38841      * @param {String} text The text to display in the menu item
38842      * @return {Roo.menu.Item} The menu item that was added
38843      */
38844     addText : function(text){
38845         return this.addItem(new Roo.menu.TextItem({ text : text }));
38846     },
38847
38848     /**
38849      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38850      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38851      * @param {Roo.menu.Item} item The menu item to add
38852      * @return {Roo.menu.Item} The menu item that was added
38853      */
38854     insert : function(index, item){
38855         this.items.insert(index, item);
38856         if(this.ul){
38857             var li = document.createElement("li");
38858             li.className = "x-menu-list-item";
38859             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38860             item.render(li, this);
38861             this.delayAutoWidth();
38862         }
38863         return item;
38864     },
38865
38866     /**
38867      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38868      * @param {Roo.menu.Item} item The menu item to remove
38869      */
38870     remove : function(item){
38871         this.items.removeKey(item.id);
38872         item.destroy();
38873     },
38874
38875     /**
38876      * Removes and destroys all items in the menu
38877      */
38878     removeAll : function(){
38879         var f;
38880         while(f = this.items.first()){
38881             this.remove(f);
38882         }
38883     }
38884 });
38885
38886 // MenuNav is a private utility class used internally by the Menu
38887 Roo.menu.MenuNav = function(menu){
38888     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38889     this.scope = this.menu = menu;
38890 };
38891
38892 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38893     doRelay : function(e, h){
38894         var k = e.getKey();
38895         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38896             this.menu.tryActivate(0, 1);
38897             return false;
38898         }
38899         return h.call(this.scope || this, e, this.menu);
38900     },
38901
38902     up : function(e, m){
38903         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38904             m.tryActivate(m.items.length-1, -1);
38905         }
38906     },
38907
38908     down : function(e, m){
38909         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38910             m.tryActivate(0, 1);
38911         }
38912     },
38913
38914     right : function(e, m){
38915         if(m.activeItem){
38916             m.activeItem.expandMenu(true);
38917         }
38918     },
38919
38920     left : function(e, m){
38921         m.hide();
38922         if(m.parentMenu && m.parentMenu.activeItem){
38923             m.parentMenu.activeItem.activate();
38924         }
38925     },
38926
38927     enter : function(e, m){
38928         if(m.activeItem){
38929             e.stopPropagation();
38930             m.activeItem.onClick(e);
38931             m.fireEvent("click", this, m.activeItem);
38932             return true;
38933         }
38934     }
38935 });/*
38936  * Based on:
38937  * Ext JS Library 1.1.1
38938  * Copyright(c) 2006-2007, Ext JS, LLC.
38939  *
38940  * Originally Released Under LGPL - original licence link has changed is not relivant.
38941  *
38942  * Fork - LGPL
38943  * <script type="text/javascript">
38944  */
38945  
38946 /**
38947  * @class Roo.menu.MenuMgr
38948  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38949  * @static
38950  */
38951 Roo.menu.MenuMgr = function(){
38952    var menus, active, groups = {}, attached = false, lastShow = new Date();
38953
38954    // private - called when first menu is created
38955    function init(){
38956        menus = {};
38957        active = new Roo.util.MixedCollection();
38958        Roo.get(document).addKeyListener(27, function(){
38959            if(active.length > 0){
38960                hideAll();
38961            }
38962        });
38963    }
38964
38965    // private
38966    function hideAll(){
38967        if(active && active.length > 0){
38968            var c = active.clone();
38969            c.each(function(m){
38970                m.hide();
38971            });
38972        }
38973    }
38974
38975    // private
38976    function onHide(m){
38977        active.remove(m);
38978        if(active.length < 1){
38979            Roo.get(document).un("mousedown", onMouseDown);
38980            attached = false;
38981        }
38982    }
38983
38984    // private
38985    function onShow(m){
38986        var last = active.last();
38987        lastShow = new Date();
38988        active.add(m);
38989        if(!attached){
38990            Roo.get(document).on("mousedown", onMouseDown);
38991            attached = true;
38992        }
38993        if(m.parentMenu){
38994           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38995           m.parentMenu.activeChild = m;
38996        }else if(last && last.isVisible()){
38997           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38998        }
38999    }
39000
39001    // private
39002    function onBeforeHide(m){
39003        if(m.activeChild){
39004            m.activeChild.hide();
39005        }
39006        if(m.autoHideTimer){
39007            clearTimeout(m.autoHideTimer);
39008            delete m.autoHideTimer;
39009        }
39010    }
39011
39012    // private
39013    function onBeforeShow(m){
39014        var pm = m.parentMenu;
39015        if(!pm && !m.allowOtherMenus){
39016            hideAll();
39017        }else if(pm && pm.activeChild && active != m){
39018            pm.activeChild.hide();
39019        }
39020    }
39021
39022    // private
39023    function onMouseDown(e){
39024        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39025            hideAll();
39026        }
39027    }
39028
39029    // private
39030    function onBeforeCheck(mi, state){
39031        if(state){
39032            var g = groups[mi.group];
39033            for(var i = 0, l = g.length; i < l; i++){
39034                if(g[i] != mi){
39035                    g[i].setChecked(false);
39036                }
39037            }
39038        }
39039    }
39040
39041    return {
39042
39043        /**
39044         * Hides all menus that are currently visible
39045         */
39046        hideAll : function(){
39047             hideAll();  
39048        },
39049
39050        // private
39051        register : function(menu){
39052            if(!menus){
39053                init();
39054            }
39055            menus[menu.id] = menu;
39056            menu.on("beforehide", onBeforeHide);
39057            menu.on("hide", onHide);
39058            menu.on("beforeshow", onBeforeShow);
39059            menu.on("show", onShow);
39060            var g = menu.group;
39061            if(g && menu.events["checkchange"]){
39062                if(!groups[g]){
39063                    groups[g] = [];
39064                }
39065                groups[g].push(menu);
39066                menu.on("checkchange", onCheck);
39067            }
39068        },
39069
39070         /**
39071          * Returns a {@link Roo.menu.Menu} object
39072          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39073          * be used to generate and return a new Menu instance.
39074          */
39075        get : function(menu){
39076            if(typeof menu == "string"){ // menu id
39077                return menus[menu];
39078            }else if(menu.events){  // menu instance
39079                return menu;
39080            }else if(typeof menu.length == 'number'){ // array of menu items?
39081                return new Roo.menu.Menu({items:menu});
39082            }else{ // otherwise, must be a config
39083                return new Roo.menu.Menu(menu);
39084            }
39085        },
39086
39087        // private
39088        unregister : function(menu){
39089            delete menus[menu.id];
39090            menu.un("beforehide", onBeforeHide);
39091            menu.un("hide", onHide);
39092            menu.un("beforeshow", onBeforeShow);
39093            menu.un("show", onShow);
39094            var g = menu.group;
39095            if(g && menu.events["checkchange"]){
39096                groups[g].remove(menu);
39097                menu.un("checkchange", onCheck);
39098            }
39099        },
39100
39101        // private
39102        registerCheckable : function(menuItem){
39103            var g = menuItem.group;
39104            if(g){
39105                if(!groups[g]){
39106                    groups[g] = [];
39107                }
39108                groups[g].push(menuItem);
39109                menuItem.on("beforecheckchange", onBeforeCheck);
39110            }
39111        },
39112
39113        // private
39114        unregisterCheckable : function(menuItem){
39115            var g = menuItem.group;
39116            if(g){
39117                groups[g].remove(menuItem);
39118                menuItem.un("beforecheckchange", onBeforeCheck);
39119            }
39120        }
39121    };
39122 }();/*
39123  * Based on:
39124  * Ext JS Library 1.1.1
39125  * Copyright(c) 2006-2007, Ext JS, LLC.
39126  *
39127  * Originally Released Under LGPL - original licence link has changed is not relivant.
39128  *
39129  * Fork - LGPL
39130  * <script type="text/javascript">
39131  */
39132  
39133
39134 /**
39135  * @class Roo.menu.BaseItem
39136  * @extends Roo.Component
39137  * @abstract
39138  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39139  * management and base configuration options shared by all menu components.
39140  * @constructor
39141  * Creates a new BaseItem
39142  * @param {Object} config Configuration options
39143  */
39144 Roo.menu.BaseItem = function(config){
39145     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39146
39147     this.addEvents({
39148         /**
39149          * @event click
39150          * Fires when this item is clicked
39151          * @param {Roo.menu.BaseItem} this
39152          * @param {Roo.EventObject} e
39153          */
39154         click: true,
39155         /**
39156          * @event activate
39157          * Fires when this item is activated
39158          * @param {Roo.menu.BaseItem} this
39159          */
39160         activate : true,
39161         /**
39162          * @event deactivate
39163          * Fires when this item is deactivated
39164          * @param {Roo.menu.BaseItem} this
39165          */
39166         deactivate : true
39167     });
39168
39169     if(this.handler){
39170         this.on("click", this.handler, this.scope, true);
39171     }
39172 };
39173
39174 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39175     /**
39176      * @cfg {Function} handler
39177      * A function that will handle the click event of this menu item (defaults to undefined)
39178      */
39179     /**
39180      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39181      */
39182     canActivate : false,
39183     
39184      /**
39185      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39186      */
39187     hidden: false,
39188     
39189     /**
39190      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39191      */
39192     activeClass : "x-menu-item-active",
39193     /**
39194      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39195      */
39196     hideOnClick : true,
39197     /**
39198      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39199      */
39200     hideDelay : 100,
39201
39202     // private
39203     ctype: "Roo.menu.BaseItem",
39204
39205     // private
39206     actionMode : "container",
39207
39208     // private
39209     render : function(container, parentMenu){
39210         this.parentMenu = parentMenu;
39211         Roo.menu.BaseItem.superclass.render.call(this, container);
39212         this.container.menuItemId = this.id;
39213     },
39214
39215     // private
39216     onRender : function(container, position){
39217         this.el = Roo.get(this.el);
39218         container.dom.appendChild(this.el.dom);
39219     },
39220
39221     // private
39222     onClick : function(e){
39223         if(!this.disabled && this.fireEvent("click", this, e) !== false
39224                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39225             this.handleClick(e);
39226         }else{
39227             e.stopEvent();
39228         }
39229     },
39230
39231     // private
39232     activate : function(){
39233         if(this.disabled){
39234             return false;
39235         }
39236         var li = this.container;
39237         li.addClass(this.activeClass);
39238         this.region = li.getRegion().adjust(2, 2, -2, -2);
39239         this.fireEvent("activate", this);
39240         return true;
39241     },
39242
39243     // private
39244     deactivate : function(){
39245         this.container.removeClass(this.activeClass);
39246         this.fireEvent("deactivate", this);
39247     },
39248
39249     // private
39250     shouldDeactivate : function(e){
39251         return !this.region || !this.region.contains(e.getPoint());
39252     },
39253
39254     // private
39255     handleClick : function(e){
39256         if(this.hideOnClick){
39257             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39258         }
39259     },
39260
39261     // private
39262     expandMenu : function(autoActivate){
39263         // do nothing
39264     },
39265
39266     // private
39267     hideMenu : function(){
39268         // do nothing
39269     }
39270 });/*
39271  * Based on:
39272  * Ext JS Library 1.1.1
39273  * Copyright(c) 2006-2007, Ext JS, LLC.
39274  *
39275  * Originally Released Under LGPL - original licence link has changed is not relivant.
39276  *
39277  * Fork - LGPL
39278  * <script type="text/javascript">
39279  */
39280  
39281 /**
39282  * @class Roo.menu.Adapter
39283  * @extends Roo.menu.BaseItem
39284  * @abstract
39285  * 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.
39286  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39287  * @constructor
39288  * Creates a new Adapter
39289  * @param {Object} config Configuration options
39290  */
39291 Roo.menu.Adapter = function(component, config){
39292     Roo.menu.Adapter.superclass.constructor.call(this, config);
39293     this.component = component;
39294 };
39295 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39296     // private
39297     canActivate : true,
39298
39299     // private
39300     onRender : function(container, position){
39301         this.component.render(container);
39302         this.el = this.component.getEl();
39303     },
39304
39305     // private
39306     activate : function(){
39307         if(this.disabled){
39308             return false;
39309         }
39310         this.component.focus();
39311         this.fireEvent("activate", this);
39312         return true;
39313     },
39314
39315     // private
39316     deactivate : function(){
39317         this.fireEvent("deactivate", this);
39318     },
39319
39320     // private
39321     disable : function(){
39322         this.component.disable();
39323         Roo.menu.Adapter.superclass.disable.call(this);
39324     },
39325
39326     // private
39327     enable : function(){
39328         this.component.enable();
39329         Roo.menu.Adapter.superclass.enable.call(this);
39330     }
39331 });/*
39332  * Based on:
39333  * Ext JS Library 1.1.1
39334  * Copyright(c) 2006-2007, Ext JS, LLC.
39335  *
39336  * Originally Released Under LGPL - original licence link has changed is not relivant.
39337  *
39338  * Fork - LGPL
39339  * <script type="text/javascript">
39340  */
39341
39342 /**
39343  * @class Roo.menu.TextItem
39344  * @extends Roo.menu.BaseItem
39345  * Adds a static text string to a menu, usually used as either a heading or group separator.
39346  * Note: old style constructor with text is still supported.
39347  * 
39348  * @constructor
39349  * Creates a new TextItem
39350  * @param {Object} cfg Configuration
39351  */
39352 Roo.menu.TextItem = function(cfg){
39353     if (typeof(cfg) == 'string') {
39354         this.text = cfg;
39355     } else {
39356         Roo.apply(this,cfg);
39357     }
39358     
39359     Roo.menu.TextItem.superclass.constructor.call(this);
39360 };
39361
39362 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39363     /**
39364      * @cfg {String} text Text to show on item.
39365      */
39366     text : '',
39367     
39368     /**
39369      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39370      */
39371     hideOnClick : false,
39372     /**
39373      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39374      */
39375     itemCls : "x-menu-text",
39376
39377     // private
39378     onRender : function(){
39379         var s = document.createElement("span");
39380         s.className = this.itemCls;
39381         s.innerHTML = this.text;
39382         this.el = s;
39383         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39384     }
39385 });/*
39386  * Based on:
39387  * Ext JS Library 1.1.1
39388  * Copyright(c) 2006-2007, Ext JS, LLC.
39389  *
39390  * Originally Released Under LGPL - original licence link has changed is not relivant.
39391  *
39392  * Fork - LGPL
39393  * <script type="text/javascript">
39394  */
39395
39396 /**
39397  * @class Roo.menu.Separator
39398  * @extends Roo.menu.BaseItem
39399  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39400  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39401  * @constructor
39402  * @param {Object} config Configuration options
39403  */
39404 Roo.menu.Separator = function(config){
39405     Roo.menu.Separator.superclass.constructor.call(this, config);
39406 };
39407
39408 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39409     /**
39410      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39411      */
39412     itemCls : "x-menu-sep",
39413     /**
39414      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39415      */
39416     hideOnClick : false,
39417
39418     // private
39419     onRender : function(li){
39420         var s = document.createElement("span");
39421         s.className = this.itemCls;
39422         s.innerHTML = "&#160;";
39423         this.el = s;
39424         li.addClass("x-menu-sep-li");
39425         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39426     }
39427 });/*
39428  * Based on:
39429  * Ext JS Library 1.1.1
39430  * Copyright(c) 2006-2007, Ext JS, LLC.
39431  *
39432  * Originally Released Under LGPL - original licence link has changed is not relivant.
39433  *
39434  * Fork - LGPL
39435  * <script type="text/javascript">
39436  */
39437 /**
39438  * @class Roo.menu.Item
39439  * @extends Roo.menu.BaseItem
39440  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39441  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39442  * activation and click handling.
39443  * @constructor
39444  * Creates a new Item
39445  * @param {Object} config Configuration options
39446  */
39447 Roo.menu.Item = function(config){
39448     Roo.menu.Item.superclass.constructor.call(this, config);
39449     if(this.menu){
39450         this.menu = Roo.menu.MenuMgr.get(this.menu);
39451     }
39452 };
39453 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39454     /**
39455      * @cfg {Roo.menu.Menu} menu
39456      * A Sub menu
39457      */
39458     /**
39459      * @cfg {String} text
39460      * The text to show on the menu item.
39461      */
39462     text: '',
39463      /**
39464      * @cfg {String} HTML to render in menu
39465      * The text to show on the menu item (HTML version).
39466      */
39467     html: '',
39468     /**
39469      * @cfg {String} icon
39470      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39471      */
39472     icon: undefined,
39473     /**
39474      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39475      */
39476     itemCls : "x-menu-item",
39477     /**
39478      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39479      */
39480     canActivate : true,
39481     /**
39482      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39483      */
39484     showDelay: 200,
39485     // doc'd in BaseItem
39486     hideDelay: 200,
39487
39488     // private
39489     ctype: "Roo.menu.Item",
39490     
39491     // private
39492     onRender : function(container, position){
39493         var el = document.createElement("a");
39494         el.hideFocus = true;
39495         el.unselectable = "on";
39496         el.href = this.href || "#";
39497         if(this.hrefTarget){
39498             el.target = this.hrefTarget;
39499         }
39500         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39501         
39502         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39503         
39504         el.innerHTML = String.format(
39505                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39506                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39507         this.el = el;
39508         Roo.menu.Item.superclass.onRender.call(this, container, position);
39509     },
39510
39511     /**
39512      * Sets the text to display in this menu item
39513      * @param {String} text The text to display
39514      * @param {Boolean} isHTML true to indicate text is pure html.
39515      */
39516     setText : function(text, isHTML){
39517         if (isHTML) {
39518             this.html = text;
39519         } else {
39520             this.text = text;
39521             this.html = '';
39522         }
39523         if(this.rendered){
39524             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39525      
39526             this.el.update(String.format(
39527                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39528                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39529             this.parentMenu.autoWidth();
39530         }
39531     },
39532
39533     // private
39534     handleClick : function(e){
39535         if(!this.href){ // if no link defined, stop the event automatically
39536             e.stopEvent();
39537         }
39538         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39539     },
39540
39541     // private
39542     activate : function(autoExpand){
39543         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39544             this.focus();
39545             if(autoExpand){
39546                 this.expandMenu();
39547             }
39548         }
39549         return true;
39550     },
39551
39552     // private
39553     shouldDeactivate : function(e){
39554         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39555             if(this.menu && this.menu.isVisible()){
39556                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39557             }
39558             return true;
39559         }
39560         return false;
39561     },
39562
39563     // private
39564     deactivate : function(){
39565         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39566         this.hideMenu();
39567     },
39568
39569     // private
39570     expandMenu : function(autoActivate){
39571         if(!this.disabled && this.menu){
39572             clearTimeout(this.hideTimer);
39573             delete this.hideTimer;
39574             if(!this.menu.isVisible() && !this.showTimer){
39575                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39576             }else if (this.menu.isVisible() && autoActivate){
39577                 this.menu.tryActivate(0, 1);
39578             }
39579         }
39580     },
39581
39582     // private
39583     deferExpand : function(autoActivate){
39584         delete this.showTimer;
39585         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39586         if(autoActivate){
39587             this.menu.tryActivate(0, 1);
39588         }
39589     },
39590
39591     // private
39592     hideMenu : function(){
39593         clearTimeout(this.showTimer);
39594         delete this.showTimer;
39595         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39596             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39597         }
39598     },
39599
39600     // private
39601     deferHide : function(){
39602         delete this.hideTimer;
39603         this.menu.hide();
39604     }
39605 });/*
39606  * Based on:
39607  * Ext JS Library 1.1.1
39608  * Copyright(c) 2006-2007, Ext JS, LLC.
39609  *
39610  * Originally Released Under LGPL - original licence link has changed is not relivant.
39611  *
39612  * Fork - LGPL
39613  * <script type="text/javascript">
39614  */
39615  
39616 /**
39617  * @class Roo.menu.CheckItem
39618  * @extends Roo.menu.Item
39619  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39620  * @constructor
39621  * Creates a new CheckItem
39622  * @param {Object} config Configuration options
39623  */
39624 Roo.menu.CheckItem = function(config){
39625     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39626     this.addEvents({
39627         /**
39628          * @event beforecheckchange
39629          * Fires before the checked value is set, providing an opportunity to cancel if needed
39630          * @param {Roo.menu.CheckItem} this
39631          * @param {Boolean} checked The new checked value that will be set
39632          */
39633         "beforecheckchange" : true,
39634         /**
39635          * @event checkchange
39636          * Fires after the checked value has been set
39637          * @param {Roo.menu.CheckItem} this
39638          * @param {Boolean} checked The checked value that was set
39639          */
39640         "checkchange" : true
39641     });
39642     if(this.checkHandler){
39643         this.on('checkchange', this.checkHandler, this.scope);
39644     }
39645 };
39646 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39647     /**
39648      * @cfg {String} group
39649      * All check items with the same group name will automatically be grouped into a single-select
39650      * radio button group (defaults to '')
39651      */
39652     /**
39653      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39654      */
39655     itemCls : "x-menu-item x-menu-check-item",
39656     /**
39657      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39658      */
39659     groupClass : "x-menu-group-item",
39660
39661     /**
39662      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39663      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39664      * initialized with checked = true will be rendered as checked.
39665      */
39666     checked: false,
39667
39668     // private
39669     ctype: "Roo.menu.CheckItem",
39670
39671     // private
39672     onRender : function(c){
39673         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39674         if(this.group){
39675             this.el.addClass(this.groupClass);
39676         }
39677         Roo.menu.MenuMgr.registerCheckable(this);
39678         if(this.checked){
39679             this.checked = false;
39680             this.setChecked(true, true);
39681         }
39682     },
39683
39684     // private
39685     destroy : function(){
39686         if(this.rendered){
39687             Roo.menu.MenuMgr.unregisterCheckable(this);
39688         }
39689         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39690     },
39691
39692     /**
39693      * Set the checked state of this item
39694      * @param {Boolean} checked The new checked value
39695      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39696      */
39697     setChecked : function(state, suppressEvent){
39698         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39699             if(this.container){
39700                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39701             }
39702             this.checked = state;
39703             if(suppressEvent !== true){
39704                 this.fireEvent("checkchange", this, state);
39705             }
39706         }
39707     },
39708
39709     // private
39710     handleClick : function(e){
39711        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39712            this.setChecked(!this.checked);
39713        }
39714        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39715     }
39716 });/*
39717  * Based on:
39718  * Ext JS Library 1.1.1
39719  * Copyright(c) 2006-2007, Ext JS, LLC.
39720  *
39721  * Originally Released Under LGPL - original licence link has changed is not relivant.
39722  *
39723  * Fork - LGPL
39724  * <script type="text/javascript">
39725  */
39726  
39727 /**
39728  * @class Roo.menu.DateItem
39729  * @extends Roo.menu.Adapter
39730  * A menu item that wraps the {@link Roo.DatPicker} component.
39731  * @constructor
39732  * Creates a new DateItem
39733  * @param {Object} config Configuration options
39734  */
39735 Roo.menu.DateItem = function(config){
39736     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39737     /** The Roo.DatePicker object @type Roo.DatePicker */
39738     this.picker = this.component;
39739     this.addEvents({select: true});
39740     
39741     this.picker.on("render", function(picker){
39742         picker.getEl().swallowEvent("click");
39743         picker.container.addClass("x-menu-date-item");
39744     });
39745
39746     this.picker.on("select", this.onSelect, this);
39747 };
39748
39749 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39750     // private
39751     onSelect : function(picker, date){
39752         this.fireEvent("select", this, date, picker);
39753         Roo.menu.DateItem.superclass.handleClick.call(this);
39754     }
39755 });/*
39756  * Based on:
39757  * Ext JS Library 1.1.1
39758  * Copyright(c) 2006-2007, Ext JS, LLC.
39759  *
39760  * Originally Released Under LGPL - original licence link has changed is not relivant.
39761  *
39762  * Fork - LGPL
39763  * <script type="text/javascript">
39764  */
39765  
39766 /**
39767  * @class Roo.menu.ColorItem
39768  * @extends Roo.menu.Adapter
39769  * A menu item that wraps the {@link Roo.ColorPalette} component.
39770  * @constructor
39771  * Creates a new ColorItem
39772  * @param {Object} config Configuration options
39773  */
39774 Roo.menu.ColorItem = function(config){
39775     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39776     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39777     this.palette = this.component;
39778     this.relayEvents(this.palette, ["select"]);
39779     if(this.selectHandler){
39780         this.on('select', this.selectHandler, this.scope);
39781     }
39782 };
39783 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39784  * Based on:
39785  * Ext JS Library 1.1.1
39786  * Copyright(c) 2006-2007, Ext JS, LLC.
39787  *
39788  * Originally Released Under LGPL - original licence link has changed is not relivant.
39789  *
39790  * Fork - LGPL
39791  * <script type="text/javascript">
39792  */
39793  
39794
39795 /**
39796  * @class Roo.menu.DateMenu
39797  * @extends Roo.menu.Menu
39798  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39799  * @constructor
39800  * Creates a new DateMenu
39801  * @param {Object} config Configuration options
39802  */
39803 Roo.menu.DateMenu = function(config){
39804     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39805     this.plain = true;
39806     var di = new Roo.menu.DateItem(config);
39807     this.add(di);
39808     /**
39809      * The {@link Roo.DatePicker} instance for this DateMenu
39810      * @type DatePicker
39811      */
39812     this.picker = di.picker;
39813     /**
39814      * @event select
39815      * @param {DatePicker} picker
39816      * @param {Date} date
39817      */
39818     this.relayEvents(di, ["select"]);
39819     this.on('beforeshow', function(){
39820         if(this.picker){
39821             this.picker.hideMonthPicker(false);
39822         }
39823     }, this);
39824 };
39825 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39826     cls:'x-date-menu'
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837  
39838
39839 /**
39840  * @class Roo.menu.ColorMenu
39841  * @extends Roo.menu.Menu
39842  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39843  * @constructor
39844  * Creates a new ColorMenu
39845  * @param {Object} config Configuration options
39846  */
39847 Roo.menu.ColorMenu = function(config){
39848     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39849     this.plain = true;
39850     var ci = new Roo.menu.ColorItem(config);
39851     this.add(ci);
39852     /**
39853      * The {@link Roo.ColorPalette} instance for this ColorMenu
39854      * @type ColorPalette
39855      */
39856     this.palette = ci.palette;
39857     /**
39858      * @event select
39859      * @param {ColorPalette} palette
39860      * @param {String} color
39861      */
39862     this.relayEvents(ci, ["select"]);
39863 };
39864 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39865  * Based on:
39866  * Ext JS Library 1.1.1
39867  * Copyright(c) 2006-2007, Ext JS, LLC.
39868  *
39869  * Originally Released Under LGPL - original licence link has changed is not relivant.
39870  *
39871  * Fork - LGPL
39872  * <script type="text/javascript">
39873  */
39874  
39875 /**
39876  * @class Roo.form.TextItem
39877  * @extends Roo.BoxComponent
39878  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39879  * @constructor
39880  * Creates a new TextItem
39881  * @param {Object} config Configuration options
39882  */
39883 Roo.form.TextItem = function(config){
39884     Roo.form.TextItem.superclass.constructor.call(this, config);
39885 };
39886
39887 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39888     
39889     /**
39890      * @cfg {String} tag the tag for this item (default div)
39891      */
39892     tag : 'div',
39893     /**
39894      * @cfg {String} html the content for this item
39895      */
39896     html : '',
39897     
39898     getAutoCreate : function()
39899     {
39900         var cfg = {
39901             id: this.id,
39902             tag: this.tag,
39903             html: this.html,
39904             cls: 'x-form-item'
39905         };
39906         
39907         return cfg;
39908         
39909     },
39910     
39911     onRender : function(ct, position)
39912     {
39913         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39914         
39915         if(!this.el){
39916             var cfg = this.getAutoCreate();
39917             if(!cfg.name){
39918                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39919             }
39920             if (!cfg.name.length) {
39921                 delete cfg.name;
39922             }
39923             this.el = ct.createChild(cfg, position);
39924         }
39925     },
39926     /*
39927      * setHTML
39928      * @param {String} html update the Contents of the element.
39929      */
39930     setHTML : function(html)
39931     {
39932         this.fieldEl.dom.innerHTML = html;
39933     }
39934     
39935 });/*
39936  * Based on:
39937  * Ext JS Library 1.1.1
39938  * Copyright(c) 2006-2007, Ext JS, LLC.
39939  *
39940  * Originally Released Under LGPL - original licence link has changed is not relivant.
39941  *
39942  * Fork - LGPL
39943  * <script type="text/javascript">
39944  */
39945  
39946 /**
39947  * @class Roo.form.Field
39948  * @extends Roo.BoxComponent
39949  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39950  * @constructor
39951  * Creates a new Field
39952  * @param {Object} config Configuration options
39953  */
39954 Roo.form.Field = function(config){
39955     Roo.form.Field.superclass.constructor.call(this, config);
39956 };
39957
39958 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39959     /**
39960      * @cfg {String} fieldLabel Label to use when rendering a form.
39961      */
39962        /**
39963      * @cfg {String} qtip Mouse over tip
39964      */
39965      
39966     /**
39967      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39968      */
39969     invalidClass : "x-form-invalid",
39970     /**
39971      * @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")
39972      */
39973     invalidText : "The value in this field is invalid",
39974     /**
39975      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39976      */
39977     focusClass : "x-form-focus",
39978     /**
39979      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39980       automatic validation (defaults to "keyup").
39981      */
39982     validationEvent : "keyup",
39983     /**
39984      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39985      */
39986     validateOnBlur : true,
39987     /**
39988      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39989      */
39990     validationDelay : 250,
39991     /**
39992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39993      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39994      */
39995     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39996     /**
39997      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39998      */
39999     fieldClass : "x-form-field",
40000     /**
40001      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40002      *<pre>
40003 Value         Description
40004 -----------   ----------------------------------------------------------------------
40005 qtip          Display a quick tip when the user hovers over the field
40006 title         Display a default browser title attribute popup
40007 under         Add a block div beneath the field containing the error text
40008 side          Add an error icon to the right of the field with a popup on hover
40009 [element id]  Add the error text directly to the innerHTML of the specified element
40010 </pre>
40011      */
40012     msgTarget : 'qtip',
40013     /**
40014      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40015      */
40016     msgFx : 'normal',
40017
40018     /**
40019      * @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.
40020      */
40021     readOnly : false,
40022
40023     /**
40024      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40025      */
40026     disabled : false,
40027
40028     /**
40029      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40030      */
40031     inputType : undefined,
40032     
40033     /**
40034      * @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).
40035          */
40036         tabIndex : undefined,
40037         
40038     // private
40039     isFormField : true,
40040
40041     // private
40042     hasFocus : false,
40043     /**
40044      * @property {Roo.Element} fieldEl
40045      * Element Containing the rendered Field (with label etc.)
40046      */
40047     /**
40048      * @cfg {Mixed} value A value to initialize this field with.
40049      */
40050     value : undefined,
40051
40052     /**
40053      * @cfg {String} name The field's HTML name attribute.
40054      */
40055     /**
40056      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40057      */
40058     // private
40059     loadedValue : false,
40060      
40061      
40062         // private ??
40063         initComponent : function(){
40064         Roo.form.Field.superclass.initComponent.call(this);
40065         this.addEvents({
40066             /**
40067              * @event focus
40068              * Fires when this field receives input focus.
40069              * @param {Roo.form.Field} this
40070              */
40071             focus : true,
40072             /**
40073              * @event blur
40074              * Fires when this field loses input focus.
40075              * @param {Roo.form.Field} this
40076              */
40077             blur : true,
40078             /**
40079              * @event specialkey
40080              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40081              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40082              * @param {Roo.form.Field} this
40083              * @param {Roo.EventObject} e The event object
40084              */
40085             specialkey : true,
40086             /**
40087              * @event change
40088              * Fires just before the field blurs if the field value has changed.
40089              * @param {Roo.form.Field} this
40090              * @param {Mixed} newValue The new value
40091              * @param {Mixed} oldValue The original value
40092              */
40093             change : true,
40094             /**
40095              * @event invalid
40096              * Fires after the field has been marked as invalid.
40097              * @param {Roo.form.Field} this
40098              * @param {String} msg The validation message
40099              */
40100             invalid : true,
40101             /**
40102              * @event valid
40103              * Fires after the field has been validated with no errors.
40104              * @param {Roo.form.Field} this
40105              */
40106             valid : true,
40107              /**
40108              * @event keyup
40109              * Fires after the key up
40110              * @param {Roo.form.Field} this
40111              * @param {Roo.EventObject}  e The event Object
40112              */
40113             keyup : true
40114         });
40115     },
40116
40117     /**
40118      * Returns the name attribute of the field if available
40119      * @return {String} name The field name
40120      */
40121     getName: function(){
40122          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40123     },
40124
40125     // private
40126     onRender : function(ct, position){
40127         Roo.form.Field.superclass.onRender.call(this, ct, position);
40128         if(!this.el){
40129             var cfg = this.getAutoCreate();
40130             if(!cfg.name){
40131                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40132             }
40133             if (!cfg.name.length) {
40134                 delete cfg.name;
40135             }
40136             if(this.inputType){
40137                 cfg.type = this.inputType;
40138             }
40139             this.el = ct.createChild(cfg, position);
40140         }
40141         var type = this.el.dom.type;
40142         if(type){
40143             if(type == 'password'){
40144                 type = 'text';
40145             }
40146             this.el.addClass('x-form-'+type);
40147         }
40148         if(this.readOnly){
40149             this.el.dom.readOnly = true;
40150         }
40151         if(this.tabIndex !== undefined){
40152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40153         }
40154
40155         this.el.addClass([this.fieldClass, this.cls]);
40156         this.initValue();
40157     },
40158
40159     /**
40160      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40161      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40162      * @return {Roo.form.Field} this
40163      */
40164     applyTo : function(target){
40165         this.allowDomMove = false;
40166         this.el = Roo.get(target);
40167         this.render(this.el.dom.parentNode);
40168         return this;
40169     },
40170
40171     // private
40172     initValue : function(){
40173         if(this.value !== undefined){
40174             this.setValue(this.value);
40175         }else if(this.el.dom.value.length > 0){
40176             this.setValue(this.el.dom.value);
40177         }
40178     },
40179
40180     /**
40181      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40182      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40183      */
40184     isDirty : function() {
40185         if(this.disabled) {
40186             return false;
40187         }
40188         return String(this.getValue()) !== String(this.originalValue);
40189     },
40190
40191     /**
40192      * stores the current value in loadedValue
40193      */
40194     resetHasChanged : function()
40195     {
40196         this.loadedValue = String(this.getValue());
40197     },
40198     /**
40199      * checks the current value against the 'loaded' value.
40200      * Note - will return false if 'resetHasChanged' has not been called first.
40201      */
40202     hasChanged : function()
40203     {
40204         if(this.disabled || this.readOnly) {
40205             return false;
40206         }
40207         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40208     },
40209     
40210     
40211     
40212     // private
40213     afterRender : function(){
40214         Roo.form.Field.superclass.afterRender.call(this);
40215         this.initEvents();
40216     },
40217
40218     // private
40219     fireKey : function(e){
40220         //Roo.log('field ' + e.getKey());
40221         if(e.isNavKeyPress()){
40222             this.fireEvent("specialkey", this, e);
40223         }
40224     },
40225
40226     /**
40227      * Resets the current field value to the originally loaded value and clears any validation messages
40228      */
40229     reset : function(){
40230         this.setValue(this.resetValue);
40231         this.originalValue = this.getValue();
40232         this.clearInvalid();
40233     },
40234
40235     // private
40236     initEvents : function(){
40237         // safari killled keypress - so keydown is now used..
40238         this.el.on("keydown" , this.fireKey,  this);
40239         this.el.on("focus", this.onFocus,  this);
40240         this.el.on("blur", this.onBlur,  this);
40241         this.el.relayEvent('keyup', this);
40242
40243         // reference to original value for reset
40244         this.originalValue = this.getValue();
40245         this.resetValue =  this.getValue();
40246     },
40247
40248     // private
40249     onFocus : function(){
40250         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40251             this.el.addClass(this.focusClass);
40252         }
40253         if(!this.hasFocus){
40254             this.hasFocus = true;
40255             this.startValue = this.getValue();
40256             this.fireEvent("focus", this);
40257         }
40258     },
40259
40260     beforeBlur : Roo.emptyFn,
40261
40262     // private
40263     onBlur : function(){
40264         this.beforeBlur();
40265         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40266             this.el.removeClass(this.focusClass);
40267         }
40268         this.hasFocus = false;
40269         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40270             this.validate();
40271         }
40272         var v = this.getValue();
40273         if(String(v) !== String(this.startValue)){
40274             this.fireEvent('change', this, v, this.startValue);
40275         }
40276         this.fireEvent("blur", this);
40277     },
40278
40279     /**
40280      * Returns whether or not the field value is currently valid
40281      * @param {Boolean} preventMark True to disable marking the field invalid
40282      * @return {Boolean} True if the value is valid, else false
40283      */
40284     isValid : function(preventMark){
40285         if(this.disabled){
40286             return true;
40287         }
40288         var restore = this.preventMark;
40289         this.preventMark = preventMark === true;
40290         var v = this.validateValue(this.processValue(this.getRawValue()));
40291         this.preventMark = restore;
40292         return v;
40293     },
40294
40295     /**
40296      * Validates the field value
40297      * @return {Boolean} True if the value is valid, else false
40298      */
40299     validate : function(){
40300         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40301             this.clearInvalid();
40302             return true;
40303         }
40304         return false;
40305     },
40306
40307     processValue : function(value){
40308         return value;
40309     },
40310
40311     // private
40312     // Subclasses should provide the validation implementation by overriding this
40313     validateValue : function(value){
40314         return true;
40315     },
40316
40317     /**
40318      * Mark this field as invalid
40319      * @param {String} msg The validation message
40320      */
40321     markInvalid : function(msg){
40322         if(!this.rendered || this.preventMark){ // not rendered
40323             return;
40324         }
40325         
40326         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40327         
40328         obj.el.addClass(this.invalidClass);
40329         msg = msg || this.invalidText;
40330         switch(this.msgTarget){
40331             case 'qtip':
40332                 obj.el.dom.qtip = msg;
40333                 obj.el.dom.qclass = 'x-form-invalid-tip';
40334                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40335                     Roo.QuickTips.enable();
40336                 }
40337                 break;
40338             case 'title':
40339                 this.el.dom.title = msg;
40340                 break;
40341             case 'under':
40342                 if(!this.errorEl){
40343                     var elp = this.el.findParent('.x-form-element', 5, true);
40344                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40345                     this.errorEl.setWidth(elp.getWidth(true)-20);
40346                 }
40347                 this.errorEl.update(msg);
40348                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40349                 break;
40350             case 'side':
40351                 if(!this.errorIcon){
40352                     var elp = this.el.findParent('.x-form-element', 5, true);
40353                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40354                 }
40355                 this.alignErrorIcon();
40356                 this.errorIcon.dom.qtip = msg;
40357                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40358                 this.errorIcon.show();
40359                 this.on('resize', this.alignErrorIcon, this);
40360                 break;
40361             default:
40362                 var t = Roo.getDom(this.msgTarget);
40363                 t.innerHTML = msg;
40364                 t.style.display = this.msgDisplay;
40365                 break;
40366         }
40367         this.fireEvent('invalid', this, msg);
40368     },
40369
40370     // private
40371     alignErrorIcon : function(){
40372         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40373     },
40374
40375     /**
40376      * Clear any invalid styles/messages for this field
40377      */
40378     clearInvalid : function(){
40379         if(!this.rendered || this.preventMark){ // not rendered
40380             return;
40381         }
40382         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40383         
40384         obj.el.removeClass(this.invalidClass);
40385         switch(this.msgTarget){
40386             case 'qtip':
40387                 obj.el.dom.qtip = '';
40388                 break;
40389             case 'title':
40390                 this.el.dom.title = '';
40391                 break;
40392             case 'under':
40393                 if(this.errorEl){
40394                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40395                 }
40396                 break;
40397             case 'side':
40398                 if(this.errorIcon){
40399                     this.errorIcon.dom.qtip = '';
40400                     this.errorIcon.hide();
40401                     this.un('resize', this.alignErrorIcon, this);
40402                 }
40403                 break;
40404             default:
40405                 var t = Roo.getDom(this.msgTarget);
40406                 t.innerHTML = '';
40407                 t.style.display = 'none';
40408                 break;
40409         }
40410         this.fireEvent('valid', this);
40411     },
40412
40413     /**
40414      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40415      * @return {Mixed} value The field value
40416      */
40417     getRawValue : function(){
40418         var v = this.el.getValue();
40419         
40420         return v;
40421     },
40422
40423     /**
40424      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40425      * @return {Mixed} value The field value
40426      */
40427     getValue : function(){
40428         var v = this.el.getValue();
40429          
40430         return v;
40431     },
40432
40433     /**
40434      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40435      * @param {Mixed} value The value to set
40436      */
40437     setRawValue : function(v){
40438         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40439     },
40440
40441     /**
40442      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40443      * @param {Mixed} value The value to set
40444      */
40445     setValue : function(v){
40446         this.value = v;
40447         if(this.rendered){
40448             this.el.dom.value = (v === null || v === undefined ? '' : v);
40449              this.validate();
40450         }
40451     },
40452
40453     adjustSize : function(w, h){
40454         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40455         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40456         return s;
40457     },
40458
40459     adjustWidth : function(tag, w){
40460         tag = tag.toLowerCase();
40461         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40462             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40463                 if(tag == 'input'){
40464                     return w + 2;
40465                 }
40466                 if(tag == 'textarea'){
40467                     return w-2;
40468                 }
40469             }else if(Roo.isOpera){
40470                 if(tag == 'input'){
40471                     return w + 2;
40472                 }
40473                 if(tag == 'textarea'){
40474                     return w-2;
40475                 }
40476             }
40477         }
40478         return w;
40479     }
40480 });
40481
40482
40483 // anything other than normal should be considered experimental
40484 Roo.form.Field.msgFx = {
40485     normal : {
40486         show: function(msgEl, f){
40487             msgEl.setDisplayed('block');
40488         },
40489
40490         hide : function(msgEl, f){
40491             msgEl.setDisplayed(false).update('');
40492         }
40493     },
40494
40495     slide : {
40496         show: function(msgEl, f){
40497             msgEl.slideIn('t', {stopFx:true});
40498         },
40499
40500         hide : function(msgEl, f){
40501             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40502         }
40503     },
40504
40505     slideRight : {
40506         show: function(msgEl, f){
40507             msgEl.fixDisplay();
40508             msgEl.alignTo(f.el, 'tl-tr');
40509             msgEl.slideIn('l', {stopFx:true});
40510         },
40511
40512         hide : function(msgEl, f){
40513             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40514         }
40515     }
40516 };/*
40517  * Based on:
40518  * Ext JS Library 1.1.1
40519  * Copyright(c) 2006-2007, Ext JS, LLC.
40520  *
40521  * Originally Released Under LGPL - original licence link has changed is not relivant.
40522  *
40523  * Fork - LGPL
40524  * <script type="text/javascript">
40525  */
40526  
40527
40528 /**
40529  * @class Roo.form.TextField
40530  * @extends Roo.form.Field
40531  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40532  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40533  * @constructor
40534  * Creates a new TextField
40535  * @param {Object} config Configuration options
40536  */
40537 Roo.form.TextField = function(config){
40538     Roo.form.TextField.superclass.constructor.call(this, config);
40539     this.addEvents({
40540         /**
40541          * @event autosize
40542          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40543          * according to the default logic, but this event provides a hook for the developer to apply additional
40544          * logic at runtime to resize the field if needed.
40545              * @param {Roo.form.Field} this This text field
40546              * @param {Number} width The new field width
40547              */
40548         autosize : true
40549     });
40550 };
40551
40552 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40553     /**
40554      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40555      */
40556     grow : false,
40557     /**
40558      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40559      */
40560     growMin : 30,
40561     /**
40562      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40563      */
40564     growMax : 800,
40565     /**
40566      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40567      */
40568     vtype : null,
40569     /**
40570      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40571      */
40572     maskRe : null,
40573     /**
40574      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40575      */
40576     disableKeyFilter : false,
40577     /**
40578      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40579      */
40580     allowBlank : true,
40581     /**
40582      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40583      */
40584     minLength : 0,
40585     /**
40586      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40587      */
40588     maxLength : Number.MAX_VALUE,
40589     /**
40590      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40591      */
40592     minLengthText : "The minimum length for this field is {0}",
40593     /**
40594      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40595      */
40596     maxLengthText : "The maximum length for this field is {0}",
40597     /**
40598      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40599      */
40600     selectOnFocus : false,
40601     /**
40602      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40603      */    
40604     allowLeadingSpace : false,
40605     /**
40606      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40607      */
40608     blankText : "This field is required",
40609     /**
40610      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40611      * If available, this function will be called only after the basic validators all return true, and will be passed the
40612      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40613      */
40614     validator : null,
40615     /**
40616      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40617      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40618      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40619      */
40620     regex : null,
40621     /**
40622      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40623      */
40624     regexText : "",
40625     /**
40626      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40627      */
40628     emptyText : null,
40629    
40630
40631     // private
40632     initEvents : function()
40633     {
40634         if (this.emptyText) {
40635             this.el.attr('placeholder', this.emptyText);
40636         }
40637         
40638         Roo.form.TextField.superclass.initEvents.call(this);
40639         if(this.validationEvent == 'keyup'){
40640             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40641             this.el.on('keyup', this.filterValidation, this);
40642         }
40643         else if(this.validationEvent !== false){
40644             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40645         }
40646         
40647         if(this.selectOnFocus){
40648             this.on("focus", this.preFocus, this);
40649         }
40650         if (!this.allowLeadingSpace) {
40651             this.on('blur', this.cleanLeadingSpace, this);
40652         }
40653         
40654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40655             this.el.on("keypress", this.filterKeys, this);
40656         }
40657         if(this.grow){
40658             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40659             this.el.on("click", this.autoSize,  this);
40660         }
40661         if(this.el.is('input[type=password]') && Roo.isSafari){
40662             this.el.on('keydown', this.SafariOnKeyDown, this);
40663         }
40664     },
40665
40666     processValue : function(value){
40667         if(this.stripCharsRe){
40668             var newValue = value.replace(this.stripCharsRe, '');
40669             if(newValue !== value){
40670                 this.setRawValue(newValue);
40671                 return newValue;
40672             }
40673         }
40674         return value;
40675     },
40676
40677     filterValidation : function(e){
40678         if(!e.isNavKeyPress()){
40679             this.validationTask.delay(this.validationDelay);
40680         }
40681     },
40682
40683     // private
40684     onKeyUp : function(e){
40685         if(!e.isNavKeyPress()){
40686             this.autoSize();
40687         }
40688     },
40689     // private - clean the leading white space
40690     cleanLeadingSpace : function(e)
40691     {
40692         if ( this.inputType == 'file') {
40693             return;
40694         }
40695         
40696         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40697     },
40698     /**
40699      * Resets the current field value to the originally-loaded value and clears any validation messages.
40700      *  
40701      */
40702     reset : function(){
40703         Roo.form.TextField.superclass.reset.call(this);
40704        
40705     }, 
40706     // private
40707     preFocus : function(){
40708         
40709         if(this.selectOnFocus){
40710             this.el.dom.select();
40711         }
40712     },
40713
40714     
40715     // private
40716     filterKeys : function(e){
40717         var k = e.getKey();
40718         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40719             return;
40720         }
40721         var c = e.getCharCode(), cc = String.fromCharCode(c);
40722         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40723             return;
40724         }
40725         if(!this.maskRe.test(cc)){
40726             e.stopEvent();
40727         }
40728     },
40729
40730     setValue : function(v){
40731         
40732         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40733         
40734         this.autoSize();
40735     },
40736
40737     /**
40738      * Validates a value according to the field's validation rules and marks the field as invalid
40739      * if the validation fails
40740      * @param {Mixed} value The value to validate
40741      * @return {Boolean} True if the value is valid, else false
40742      */
40743     validateValue : function(value){
40744         if(value.length < 1)  { // if it's blank
40745              if(this.allowBlank){
40746                 this.clearInvalid();
40747                 return true;
40748              }else{
40749                 this.markInvalid(this.blankText);
40750                 return false;
40751              }
40752         }
40753         if(value.length < this.minLength){
40754             this.markInvalid(String.format(this.minLengthText, this.minLength));
40755             return false;
40756         }
40757         if(value.length > this.maxLength){
40758             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40759             return false;
40760         }
40761         if(this.vtype){
40762             var vt = Roo.form.VTypes;
40763             if(!vt[this.vtype](value, this)){
40764                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40765                 return false;
40766             }
40767         }
40768         if(typeof this.validator == "function"){
40769             var msg = this.validator(value);
40770             if(msg !== true){
40771                 this.markInvalid(msg);
40772                 return false;
40773             }
40774         }
40775         if(this.regex && !this.regex.test(value)){
40776             this.markInvalid(this.regexText);
40777             return false;
40778         }
40779         return true;
40780     },
40781
40782     /**
40783      * Selects text in this field
40784      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40785      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40786      */
40787     selectText : function(start, end){
40788         var v = this.getRawValue();
40789         if(v.length > 0){
40790             start = start === undefined ? 0 : start;
40791             end = end === undefined ? v.length : end;
40792             var d = this.el.dom;
40793             if(d.setSelectionRange){
40794                 d.setSelectionRange(start, end);
40795             }else if(d.createTextRange){
40796                 var range = d.createTextRange();
40797                 range.moveStart("character", start);
40798                 range.moveEnd("character", v.length-end);
40799                 range.select();
40800             }
40801         }
40802     },
40803
40804     /**
40805      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40806      * This only takes effect if grow = true, and fires the autosize event.
40807      */
40808     autoSize : function(){
40809         if(!this.grow || !this.rendered){
40810             return;
40811         }
40812         if(!this.metrics){
40813             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40814         }
40815         var el = this.el;
40816         var v = el.dom.value;
40817         var d = document.createElement('div');
40818         d.appendChild(document.createTextNode(v));
40819         v = d.innerHTML;
40820         d = null;
40821         v += "&#160;";
40822         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40823         this.el.setWidth(w);
40824         this.fireEvent("autosize", this, w);
40825     },
40826     
40827     // private
40828     SafariOnKeyDown : function(event)
40829     {
40830         // this is a workaround for a password hang bug on chrome/ webkit.
40831         
40832         var isSelectAll = false;
40833         
40834         if(this.el.dom.selectionEnd > 0){
40835             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40836         }
40837         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40838             event.preventDefault();
40839             this.setValue('');
40840             return;
40841         }
40842         
40843         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40844             
40845             event.preventDefault();
40846             // this is very hacky as keydown always get's upper case.
40847             
40848             var cc = String.fromCharCode(event.getCharCode());
40849             
40850             
40851             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40852             
40853         }
40854         
40855         
40856     }
40857 });/*
40858  * Based on:
40859  * Ext JS Library 1.1.1
40860  * Copyright(c) 2006-2007, Ext JS, LLC.
40861  *
40862  * Originally Released Under LGPL - original licence link has changed is not relivant.
40863  *
40864  * Fork - LGPL
40865  * <script type="text/javascript">
40866  */
40867  
40868 /**
40869  * @class Roo.form.Hidden
40870  * @extends Roo.form.TextField
40871  * Simple Hidden element used on forms 
40872  * 
40873  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40874  * 
40875  * @constructor
40876  * Creates a new Hidden form element.
40877  * @param {Object} config Configuration options
40878  */
40879
40880
40881
40882 // easy hidden field...
40883 Roo.form.Hidden = function(config){
40884     Roo.form.Hidden.superclass.constructor.call(this, config);
40885 };
40886   
40887 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40888     fieldLabel:      '',
40889     inputType:      'hidden',
40890     width:          50,
40891     allowBlank:     true,
40892     labelSeparator: '',
40893     hidden:         true,
40894     itemCls :       'x-form-item-display-none'
40895
40896
40897 });
40898
40899
40900 /*
40901  * Based on:
40902  * Ext JS Library 1.1.1
40903  * Copyright(c) 2006-2007, Ext JS, LLC.
40904  *
40905  * Originally Released Under LGPL - original licence link has changed is not relivant.
40906  *
40907  * Fork - LGPL
40908  * <script type="text/javascript">
40909  */
40910  
40911 /**
40912  * @class Roo.form.TriggerField
40913  * @extends Roo.form.TextField
40914  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40915  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40916  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40917  * for which you can provide a custom implementation.  For example:
40918  * <pre><code>
40919 var trigger = new Roo.form.TriggerField();
40920 trigger.onTriggerClick = myTriggerFn;
40921 trigger.applyTo('my-field');
40922 </code></pre>
40923  *
40924  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40925  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40926  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40927  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40928  * @constructor
40929  * Create a new TriggerField.
40930  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40931  * to the base TextField)
40932  */
40933 Roo.form.TriggerField = function(config){
40934     this.mimicing = false;
40935     Roo.form.TriggerField.superclass.constructor.call(this, config);
40936 };
40937
40938 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40939     /**
40940      * @cfg {String} triggerClass A CSS class to apply to the trigger
40941      */
40942     /**
40943      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40944      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40945      */
40946     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40947     /**
40948      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40949      */
40950     hideTrigger:false,
40951
40952     /** @cfg {Boolean} grow @hide */
40953     /** @cfg {Number} growMin @hide */
40954     /** @cfg {Number} growMax @hide */
40955
40956     /**
40957      * @hide 
40958      * @method
40959      */
40960     autoSize: Roo.emptyFn,
40961     // private
40962     monitorTab : true,
40963     // private
40964     deferHeight : true,
40965
40966     
40967     actionMode : 'wrap',
40968     // private
40969     onResize : function(w, h){
40970         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40971         if(typeof w == 'number'){
40972             var x = w - this.trigger.getWidth();
40973             this.el.setWidth(this.adjustWidth('input', x));
40974             this.trigger.setStyle('left', x+'px');
40975         }
40976     },
40977
40978     // private
40979     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40980
40981     // private
40982     getResizeEl : function(){
40983         return this.wrap;
40984     },
40985
40986     // private
40987     getPositionEl : function(){
40988         return this.wrap;
40989     },
40990
40991     // private
40992     alignErrorIcon : function(){
40993         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40994     },
40995
40996     // private
40997     onRender : function(ct, position){
40998         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40999         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41000         this.trigger = this.wrap.createChild(this.triggerConfig ||
41001                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41002         if(this.hideTrigger){
41003             this.trigger.setDisplayed(false);
41004         }
41005         this.initTrigger();
41006         if(!this.width){
41007             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41008         }
41009     },
41010
41011     // private
41012     initTrigger : function(){
41013         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41014         this.trigger.addClassOnOver('x-form-trigger-over');
41015         this.trigger.addClassOnClick('x-form-trigger-click');
41016     },
41017
41018     // private
41019     onDestroy : function(){
41020         if(this.trigger){
41021             this.trigger.removeAllListeners();
41022             this.trigger.remove();
41023         }
41024         if(this.wrap){
41025             this.wrap.remove();
41026         }
41027         Roo.form.TriggerField.superclass.onDestroy.call(this);
41028     },
41029
41030     // private
41031     onFocus : function(){
41032         Roo.form.TriggerField.superclass.onFocus.call(this);
41033         if(!this.mimicing){
41034             this.wrap.addClass('x-trigger-wrap-focus');
41035             this.mimicing = true;
41036             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41037             if(this.monitorTab){
41038                 this.el.on("keydown", this.checkTab, this);
41039             }
41040         }
41041     },
41042
41043     // private
41044     checkTab : function(e){
41045         if(e.getKey() == e.TAB){
41046             this.triggerBlur();
41047         }
41048     },
41049
41050     // private
41051     onBlur : function(){
41052         // do nothing
41053     },
41054
41055     // private
41056     mimicBlur : function(e, t){
41057         if(!this.wrap.contains(t) && this.validateBlur()){
41058             this.triggerBlur();
41059         }
41060     },
41061
41062     // private
41063     triggerBlur : function(){
41064         this.mimicing = false;
41065         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41066         if(this.monitorTab){
41067             this.el.un("keydown", this.checkTab, this);
41068         }
41069         this.wrap.removeClass('x-trigger-wrap-focus');
41070         Roo.form.TriggerField.superclass.onBlur.call(this);
41071     },
41072
41073     // private
41074     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41075     validateBlur : function(e, t){
41076         return true;
41077     },
41078
41079     // private
41080     onDisable : function(){
41081         Roo.form.TriggerField.superclass.onDisable.call(this);
41082         if(this.wrap){
41083             this.wrap.addClass('x-item-disabled');
41084         }
41085     },
41086
41087     // private
41088     onEnable : function(){
41089         Roo.form.TriggerField.superclass.onEnable.call(this);
41090         if(this.wrap){
41091             this.wrap.removeClass('x-item-disabled');
41092         }
41093     },
41094
41095     // private
41096     onShow : function(){
41097         var ae = this.getActionEl();
41098         
41099         if(ae){
41100             ae.dom.style.display = '';
41101             ae.dom.style.visibility = 'visible';
41102         }
41103     },
41104
41105     // private
41106     
41107     onHide : function(){
41108         var ae = this.getActionEl();
41109         ae.dom.style.display = 'none';
41110     },
41111
41112     /**
41113      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41114      * by an implementing function.
41115      * @method
41116      * @param {EventObject} e
41117      */
41118     onTriggerClick : Roo.emptyFn
41119 });
41120
41121 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41122 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41123 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41124 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41125     initComponent : function(){
41126         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41127
41128         this.triggerConfig = {
41129             tag:'span', cls:'x-form-twin-triggers', cn:[
41130             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41131             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41132         ]};
41133     },
41134
41135     getTrigger : function(index){
41136         return this.triggers[index];
41137     },
41138
41139     initTrigger : function(){
41140         var ts = this.trigger.select('.x-form-trigger', true);
41141         this.wrap.setStyle('overflow', 'hidden');
41142         var triggerField = this;
41143         ts.each(function(t, all, index){
41144             t.hide = function(){
41145                 var w = triggerField.wrap.getWidth();
41146                 this.dom.style.display = 'none';
41147                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41148             };
41149             t.show = function(){
41150                 var w = triggerField.wrap.getWidth();
41151                 this.dom.style.display = '';
41152                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41153             };
41154             var triggerIndex = 'Trigger'+(index+1);
41155
41156             if(this['hide'+triggerIndex]){
41157                 t.dom.style.display = 'none';
41158             }
41159             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41160             t.addClassOnOver('x-form-trigger-over');
41161             t.addClassOnClick('x-form-trigger-click');
41162         }, this);
41163         this.triggers = ts.elements;
41164     },
41165
41166     onTrigger1Click : Roo.emptyFn,
41167     onTrigger2Click : Roo.emptyFn
41168 });/*
41169  * Based on:
41170  * Ext JS Library 1.1.1
41171  * Copyright(c) 2006-2007, Ext JS, LLC.
41172  *
41173  * Originally Released Under LGPL - original licence link has changed is not relivant.
41174  *
41175  * Fork - LGPL
41176  * <script type="text/javascript">
41177  */
41178  
41179 /**
41180  * @class Roo.form.TextArea
41181  * @extends Roo.form.TextField
41182  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41183  * support for auto-sizing.
41184  * @constructor
41185  * Creates a new TextArea
41186  * @param {Object} config Configuration options
41187  */
41188 Roo.form.TextArea = function(config){
41189     Roo.form.TextArea.superclass.constructor.call(this, config);
41190     // these are provided exchanges for backwards compat
41191     // minHeight/maxHeight were replaced by growMin/growMax to be
41192     // compatible with TextField growing config values
41193     if(this.minHeight !== undefined){
41194         this.growMin = this.minHeight;
41195     }
41196     if(this.maxHeight !== undefined){
41197         this.growMax = this.maxHeight;
41198     }
41199 };
41200
41201 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41202     /**
41203      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41204      */
41205     growMin : 60,
41206     /**
41207      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41208      */
41209     growMax: 1000,
41210     /**
41211      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41212      * in the field (equivalent to setting overflow: hidden, defaults to false)
41213      */
41214     preventScrollbars: false,
41215     /**
41216      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41217      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41218      */
41219
41220     // private
41221     onRender : function(ct, position){
41222         if(!this.el){
41223             this.defaultAutoCreate = {
41224                 tag: "textarea",
41225                 style:"width:300px;height:60px;",
41226                 autocomplete: "new-password"
41227             };
41228         }
41229         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41230         if(this.grow){
41231             this.textSizeEl = Roo.DomHelper.append(document.body, {
41232                 tag: "pre", cls: "x-form-grow-sizer"
41233             });
41234             if(this.preventScrollbars){
41235                 this.el.setStyle("overflow", "hidden");
41236             }
41237             this.el.setHeight(this.growMin);
41238         }
41239     },
41240
41241     onDestroy : function(){
41242         if(this.textSizeEl){
41243             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41244         }
41245         Roo.form.TextArea.superclass.onDestroy.call(this);
41246     },
41247
41248     // private
41249     onKeyUp : function(e){
41250         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41251             this.autoSize();
41252         }
41253     },
41254
41255     /**
41256      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41257      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41258      */
41259     autoSize : function(){
41260         if(!this.grow || !this.textSizeEl){
41261             return;
41262         }
41263         var el = this.el;
41264         var v = el.dom.value;
41265         var ts = this.textSizeEl;
41266
41267         ts.innerHTML = '';
41268         ts.appendChild(document.createTextNode(v));
41269         v = ts.innerHTML;
41270
41271         Roo.fly(ts).setWidth(this.el.getWidth());
41272         if(v.length < 1){
41273             v = "&#160;&#160;";
41274         }else{
41275             if(Roo.isIE){
41276                 v = v.replace(/\n/g, '<p>&#160;</p>');
41277             }
41278             v += "&#160;\n&#160;";
41279         }
41280         ts.innerHTML = v;
41281         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41282         if(h != this.lastHeight){
41283             this.lastHeight = h;
41284             this.el.setHeight(h);
41285             this.fireEvent("autosize", this, h);
41286         }
41287     }
41288 });/*
41289  * Based on:
41290  * Ext JS Library 1.1.1
41291  * Copyright(c) 2006-2007, Ext JS, LLC.
41292  *
41293  * Originally Released Under LGPL - original licence link has changed is not relivant.
41294  *
41295  * Fork - LGPL
41296  * <script type="text/javascript">
41297  */
41298  
41299
41300 /**
41301  * @class Roo.form.NumberField
41302  * @extends Roo.form.TextField
41303  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41304  * @constructor
41305  * Creates a new NumberField
41306  * @param {Object} config Configuration options
41307  */
41308 Roo.form.NumberField = function(config){
41309     Roo.form.NumberField.superclass.constructor.call(this, config);
41310 };
41311
41312 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41313     /**
41314      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41315      */
41316     fieldClass: "x-form-field x-form-num-field",
41317     /**
41318      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41319      */
41320     allowDecimals : true,
41321     /**
41322      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41323      */
41324     decimalSeparator : ".",
41325     /**
41326      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41327      */
41328     decimalPrecision : 2,
41329     /**
41330      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41331      */
41332     allowNegative : true,
41333     /**
41334      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41335      */
41336     minValue : Number.NEGATIVE_INFINITY,
41337     /**
41338      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41339      */
41340     maxValue : Number.MAX_VALUE,
41341     /**
41342      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41343      */
41344     minText : "The minimum value for this field is {0}",
41345     /**
41346      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41347      */
41348     maxText : "The maximum value for this field is {0}",
41349     /**
41350      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41351      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41352      */
41353     nanText : "{0} is not a valid number",
41354
41355     // private
41356     initEvents : function(){
41357         Roo.form.NumberField.superclass.initEvents.call(this);
41358         var allowed = "0123456789";
41359         if(this.allowDecimals){
41360             allowed += this.decimalSeparator;
41361         }
41362         if(this.allowNegative){
41363             allowed += "-";
41364         }
41365         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41366         var keyPress = function(e){
41367             var k = e.getKey();
41368             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41369                 return;
41370             }
41371             var c = e.getCharCode();
41372             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41373                 e.stopEvent();
41374             }
41375         };
41376         this.el.on("keypress", keyPress, this);
41377     },
41378
41379     // private
41380     validateValue : function(value){
41381         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41382             return false;
41383         }
41384         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41385              return true;
41386         }
41387         var num = this.parseValue(value);
41388         if(isNaN(num)){
41389             this.markInvalid(String.format(this.nanText, value));
41390             return false;
41391         }
41392         if(num < this.minValue){
41393             this.markInvalid(String.format(this.minText, this.minValue));
41394             return false;
41395         }
41396         if(num > this.maxValue){
41397             this.markInvalid(String.format(this.maxText, this.maxValue));
41398             return false;
41399         }
41400         return true;
41401     },
41402
41403     getValue : function(){
41404         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41405     },
41406
41407     // private
41408     parseValue : function(value){
41409         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41410         return isNaN(value) ? '' : value;
41411     },
41412
41413     // private
41414     fixPrecision : function(value){
41415         var nan = isNaN(value);
41416         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41417             return nan ? '' : value;
41418         }
41419         return parseFloat(value).toFixed(this.decimalPrecision);
41420     },
41421
41422     setValue : function(v){
41423         v = this.fixPrecision(v);
41424         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41425     },
41426
41427     // private
41428     decimalPrecisionFcn : function(v){
41429         return Math.floor(v);
41430     },
41431
41432     beforeBlur : function(){
41433         var v = this.parseValue(this.getRawValue());
41434         if(v){
41435             this.setValue(v);
41436         }
41437     }
41438 });/*
41439  * Based on:
41440  * Ext JS Library 1.1.1
41441  * Copyright(c) 2006-2007, Ext JS, LLC.
41442  *
41443  * Originally Released Under LGPL - original licence link has changed is not relivant.
41444  *
41445  * Fork - LGPL
41446  * <script type="text/javascript">
41447  */
41448  
41449 /**
41450  * @class Roo.form.DateField
41451  * @extends Roo.form.TriggerField
41452  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41453 * @constructor
41454 * Create a new DateField
41455 * @param {Object} config
41456  */
41457 Roo.form.DateField = function(config)
41458 {
41459     Roo.form.DateField.superclass.constructor.call(this, config);
41460     
41461       this.addEvents({
41462          
41463         /**
41464          * @event select
41465          * Fires when a date is selected
41466              * @param {Roo.form.DateField} combo This combo box
41467              * @param {Date} date The date selected
41468              */
41469         'select' : true
41470          
41471     });
41472     
41473     
41474     if(typeof this.minValue == "string") {
41475         this.minValue = this.parseDate(this.minValue);
41476     }
41477     if(typeof this.maxValue == "string") {
41478         this.maxValue = this.parseDate(this.maxValue);
41479     }
41480     this.ddMatch = null;
41481     if(this.disabledDates){
41482         var dd = this.disabledDates;
41483         var re = "(?:";
41484         for(var i = 0; i < dd.length; i++){
41485             re += dd[i];
41486             if(i != dd.length-1) {
41487                 re += "|";
41488             }
41489         }
41490         this.ddMatch = new RegExp(re + ")");
41491     }
41492 };
41493
41494 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41495     /**
41496      * @cfg {String} format
41497      * The default date format string which can be overriden for localization support.  The format must be
41498      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41499      */
41500     format : "m/d/y",
41501     /**
41502      * @cfg {String} altFormats
41503      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41504      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41505      */
41506     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41507     /**
41508      * @cfg {Array} disabledDays
41509      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41510      */
41511     disabledDays : null,
41512     /**
41513      * @cfg {String} disabledDaysText
41514      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41515      */
41516     disabledDaysText : "Disabled",
41517     /**
41518      * @cfg {Array} disabledDates
41519      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41520      * expression so they are very powerful. Some examples:
41521      * <ul>
41522      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41523      * <li>["03/08", "09/16"] would disable those days for every year</li>
41524      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41525      * <li>["03/../2006"] would disable every day in March 2006</li>
41526      * <li>["^03"] would disable every day in every March</li>
41527      * </ul>
41528      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41529      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41530      */
41531     disabledDates : null,
41532     /**
41533      * @cfg {String} disabledDatesText
41534      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41535      */
41536     disabledDatesText : "Disabled",
41537     /**
41538      * @cfg {Date/String} minValue
41539      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41540      * valid format (defaults to null).
41541      */
41542     minValue : null,
41543     /**
41544      * @cfg {Date/String} maxValue
41545      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41546      * valid format (defaults to null).
41547      */
41548     maxValue : null,
41549     /**
41550      * @cfg {String} minText
41551      * The error text to display when the date in the cell is before minValue (defaults to
41552      * 'The date in this field must be after {minValue}').
41553      */
41554     minText : "The date in this field must be equal to or after {0}",
41555     /**
41556      * @cfg {String} maxText
41557      * The error text to display when the date in the cell is after maxValue (defaults to
41558      * 'The date in this field must be before {maxValue}').
41559      */
41560     maxText : "The date in this field must be equal to or before {0}",
41561     /**
41562      * @cfg {String} invalidText
41563      * The error text to display when the date in the field is invalid (defaults to
41564      * '{value} is not a valid date - it must be in the format {format}').
41565      */
41566     invalidText : "{0} is not a valid date - it must be in the format {1}",
41567     /**
41568      * @cfg {String} triggerClass
41569      * An additional CSS class used to style the trigger button.  The trigger will always get the
41570      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41571      * which displays a calendar icon).
41572      */
41573     triggerClass : 'x-form-date-trigger',
41574     
41575
41576     /**
41577      * @cfg {Boolean} useIso
41578      * if enabled, then the date field will use a hidden field to store the 
41579      * real value as iso formated date. default (false)
41580      */ 
41581     useIso : false,
41582     /**
41583      * @cfg {String/Object} autoCreate
41584      * A DomHelper element spec, or true for a default element spec (defaults to
41585      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41586      */ 
41587     // private
41588     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41589     
41590     // private
41591     hiddenField: false,
41592     
41593     onRender : function(ct, position)
41594     {
41595         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41596         if (this.useIso) {
41597             //this.el.dom.removeAttribute('name'); 
41598             Roo.log("Changing name?");
41599             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41600             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41601                     'before', true);
41602             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41603             // prevent input submission
41604             this.hiddenName = this.name;
41605         }
41606             
41607             
41608     },
41609     
41610     // private
41611     validateValue : function(value)
41612     {
41613         value = this.formatDate(value);
41614         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41615             Roo.log('super failed');
41616             return false;
41617         }
41618         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41619              return true;
41620         }
41621         var svalue = value;
41622         value = this.parseDate(value);
41623         if(!value){
41624             Roo.log('parse date failed' + svalue);
41625             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41626             return false;
41627         }
41628         var time = value.getTime();
41629         if(this.minValue && time < this.minValue.getTime()){
41630             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41631             return false;
41632         }
41633         if(this.maxValue && time > this.maxValue.getTime()){
41634             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41635             return false;
41636         }
41637         if(this.disabledDays){
41638             var day = value.getDay();
41639             for(var i = 0; i < this.disabledDays.length; i++) {
41640                 if(day === this.disabledDays[i]){
41641                     this.markInvalid(this.disabledDaysText);
41642                     return false;
41643                 }
41644             }
41645         }
41646         var fvalue = this.formatDate(value);
41647         if(this.ddMatch && this.ddMatch.test(fvalue)){
41648             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41649             return false;
41650         }
41651         return true;
41652     },
41653
41654     // private
41655     // Provides logic to override the default TriggerField.validateBlur which just returns true
41656     validateBlur : function(){
41657         return !this.menu || !this.menu.isVisible();
41658     },
41659     
41660     getName: function()
41661     {
41662         // returns hidden if it's set..
41663         if (!this.rendered) {return ''};
41664         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41665         
41666     },
41667
41668     /**
41669      * Returns the current date value of the date field.
41670      * @return {Date} The date value
41671      */
41672     getValue : function(){
41673         
41674         return  this.hiddenField ?
41675                 this.hiddenField.value :
41676                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41677     },
41678
41679     /**
41680      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41681      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41682      * (the default format used is "m/d/y").
41683      * <br />Usage:
41684      * <pre><code>
41685 //All of these calls set the same date value (May 4, 2006)
41686
41687 //Pass a date object:
41688 var dt = new Date('5/4/06');
41689 dateField.setValue(dt);
41690
41691 //Pass a date string (default format):
41692 dateField.setValue('5/4/06');
41693
41694 //Pass a date string (custom format):
41695 dateField.format = 'Y-m-d';
41696 dateField.setValue('2006-5-4');
41697 </code></pre>
41698      * @param {String/Date} date The date or valid date string
41699      */
41700     setValue : function(date){
41701         if (this.hiddenField) {
41702             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41703         }
41704         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41705         // make sure the value field is always stored as a date..
41706         this.value = this.parseDate(date);
41707         
41708         
41709     },
41710
41711     // private
41712     parseDate : function(value){
41713         if(!value || value instanceof Date){
41714             return value;
41715         }
41716         var v = Date.parseDate(value, this.format);
41717          if (!v && this.useIso) {
41718             v = Date.parseDate(value, 'Y-m-d');
41719         }
41720         if(!v && this.altFormats){
41721             if(!this.altFormatsArray){
41722                 this.altFormatsArray = this.altFormats.split("|");
41723             }
41724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41725                 v = Date.parseDate(value, this.altFormatsArray[i]);
41726             }
41727         }
41728         return v;
41729     },
41730
41731     // private
41732     formatDate : function(date, fmt){
41733         return (!date || !(date instanceof Date)) ?
41734                date : date.dateFormat(fmt || this.format);
41735     },
41736
41737     // private
41738     menuListeners : {
41739         select: function(m, d){
41740             
41741             this.setValue(d);
41742             this.fireEvent('select', this, d);
41743         },
41744         show : function(){ // retain focus styling
41745             this.onFocus();
41746         },
41747         hide : function(){
41748             this.focus.defer(10, this);
41749             var ml = this.menuListeners;
41750             this.menu.un("select", ml.select,  this);
41751             this.menu.un("show", ml.show,  this);
41752             this.menu.un("hide", ml.hide,  this);
41753         }
41754     },
41755
41756     // private
41757     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41758     onTriggerClick : function(){
41759         if(this.disabled){
41760             return;
41761         }
41762         if(this.menu == null){
41763             this.menu = new Roo.menu.DateMenu();
41764         }
41765         Roo.apply(this.menu.picker,  {
41766             showClear: this.allowBlank,
41767             minDate : this.minValue,
41768             maxDate : this.maxValue,
41769             disabledDatesRE : this.ddMatch,
41770             disabledDatesText : this.disabledDatesText,
41771             disabledDays : this.disabledDays,
41772             disabledDaysText : this.disabledDaysText,
41773             format : this.useIso ? 'Y-m-d' : this.format,
41774             minText : String.format(this.minText, this.formatDate(this.minValue)),
41775             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41776         });
41777         this.menu.on(Roo.apply({}, this.menuListeners, {
41778             scope:this
41779         }));
41780         this.menu.picker.setValue(this.getValue() || new Date());
41781         this.menu.show(this.el, "tl-bl?");
41782     },
41783
41784     beforeBlur : function(){
41785         var v = this.parseDate(this.getRawValue());
41786         if(v){
41787             this.setValue(v);
41788         }
41789     },
41790
41791     /*@
41792      * overide
41793      * 
41794      */
41795     isDirty : function() {
41796         if(this.disabled) {
41797             return false;
41798         }
41799         
41800         if(typeof(this.startValue) === 'undefined'){
41801             return false;
41802         }
41803         
41804         return String(this.getValue()) !== String(this.startValue);
41805         
41806     },
41807     // @overide
41808     cleanLeadingSpace : function(e)
41809     {
41810        return;
41811     }
41812     
41813 });/*
41814  * Based on:
41815  * Ext JS Library 1.1.1
41816  * Copyright(c) 2006-2007, Ext JS, LLC.
41817  *
41818  * Originally Released Under LGPL - original licence link has changed is not relivant.
41819  *
41820  * Fork - LGPL
41821  * <script type="text/javascript">
41822  */
41823  
41824 /**
41825  * @class Roo.form.MonthField
41826  * @extends Roo.form.TriggerField
41827  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41828 * @constructor
41829 * Create a new MonthField
41830 * @param {Object} config
41831  */
41832 Roo.form.MonthField = function(config){
41833     
41834     Roo.form.MonthField.superclass.constructor.call(this, config);
41835     
41836       this.addEvents({
41837          
41838         /**
41839          * @event select
41840          * Fires when a date is selected
41841              * @param {Roo.form.MonthFieeld} combo This combo box
41842              * @param {Date} date The date selected
41843              */
41844         'select' : true
41845          
41846     });
41847     
41848     
41849     if(typeof this.minValue == "string") {
41850         this.minValue = this.parseDate(this.minValue);
41851     }
41852     if(typeof this.maxValue == "string") {
41853         this.maxValue = this.parseDate(this.maxValue);
41854     }
41855     this.ddMatch = null;
41856     if(this.disabledDates){
41857         var dd = this.disabledDates;
41858         var re = "(?:";
41859         for(var i = 0; i < dd.length; i++){
41860             re += dd[i];
41861             if(i != dd.length-1) {
41862                 re += "|";
41863             }
41864         }
41865         this.ddMatch = new RegExp(re + ")");
41866     }
41867 };
41868
41869 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41870     /**
41871      * @cfg {String} format
41872      * The default date format string which can be overriden for localization support.  The format must be
41873      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41874      */
41875     format : "M Y",
41876     /**
41877      * @cfg {String} altFormats
41878      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41879      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41880      */
41881     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41882     /**
41883      * @cfg {Array} disabledDays
41884      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41885      */
41886     disabledDays : [0,1,2,3,4,5,6],
41887     /**
41888      * @cfg {String} disabledDaysText
41889      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41890      */
41891     disabledDaysText : "Disabled",
41892     /**
41893      * @cfg {Array} disabledDates
41894      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41895      * expression so they are very powerful. Some examples:
41896      * <ul>
41897      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41898      * <li>["03/08", "09/16"] would disable those days for every year</li>
41899      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41900      * <li>["03/../2006"] would disable every day in March 2006</li>
41901      * <li>["^03"] would disable every day in every March</li>
41902      * </ul>
41903      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41904      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41905      */
41906     disabledDates : null,
41907     /**
41908      * @cfg {String} disabledDatesText
41909      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41910      */
41911     disabledDatesText : "Disabled",
41912     /**
41913      * @cfg {Date/String} minValue
41914      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41915      * valid format (defaults to null).
41916      */
41917     minValue : null,
41918     /**
41919      * @cfg {Date/String} maxValue
41920      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41921      * valid format (defaults to null).
41922      */
41923     maxValue : null,
41924     /**
41925      * @cfg {String} minText
41926      * The error text to display when the date in the cell is before minValue (defaults to
41927      * 'The date in this field must be after {minValue}').
41928      */
41929     minText : "The date in this field must be equal to or after {0}",
41930     /**
41931      * @cfg {String} maxTextf
41932      * The error text to display when the date in the cell is after maxValue (defaults to
41933      * 'The date in this field must be before {maxValue}').
41934      */
41935     maxText : "The date in this field must be equal to or before {0}",
41936     /**
41937      * @cfg {String} invalidText
41938      * The error text to display when the date in the field is invalid (defaults to
41939      * '{value} is not a valid date - it must be in the format {format}').
41940      */
41941     invalidText : "{0} is not a valid date - it must be in the format {1}",
41942     /**
41943      * @cfg {String} triggerClass
41944      * An additional CSS class used to style the trigger button.  The trigger will always get the
41945      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41946      * which displays a calendar icon).
41947      */
41948     triggerClass : 'x-form-date-trigger',
41949     
41950
41951     /**
41952      * @cfg {Boolean} useIso
41953      * if enabled, then the date field will use a hidden field to store the 
41954      * real value as iso formated date. default (true)
41955      */ 
41956     useIso : true,
41957     /**
41958      * @cfg {String/Object} autoCreate
41959      * A DomHelper element spec, or true for a default element spec (defaults to
41960      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41961      */ 
41962     // private
41963     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41964     
41965     // private
41966     hiddenField: false,
41967     
41968     hideMonthPicker : false,
41969     
41970     onRender : function(ct, position)
41971     {
41972         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41973         if (this.useIso) {
41974             this.el.dom.removeAttribute('name'); 
41975             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41976                     'before', true);
41977             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41978             // prevent input submission
41979             this.hiddenName = this.name;
41980         }
41981             
41982             
41983     },
41984     
41985     // private
41986     validateValue : function(value)
41987     {
41988         value = this.formatDate(value);
41989         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41990             return false;
41991         }
41992         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41993              return true;
41994         }
41995         var svalue = value;
41996         value = this.parseDate(value);
41997         if(!value){
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         */
42020         var fvalue = this.formatDate(value);
42021         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42022             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42023             return false;
42024         }
42025         */
42026         return true;
42027     },
42028
42029     // private
42030     // Provides logic to override the default TriggerField.validateBlur which just returns true
42031     validateBlur : function(){
42032         return !this.menu || !this.menu.isVisible();
42033     },
42034
42035     /**
42036      * Returns the current date value of the date field.
42037      * @return {Date} The date value
42038      */
42039     getValue : function(){
42040         
42041         
42042         
42043         return  this.hiddenField ?
42044                 this.hiddenField.value :
42045                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42046     },
42047
42048     /**
42049      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42050      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42051      * (the default format used is "m/d/y").
42052      * <br />Usage:
42053      * <pre><code>
42054 //All of these calls set the same date value (May 4, 2006)
42055
42056 //Pass a date object:
42057 var dt = new Date('5/4/06');
42058 monthField.setValue(dt);
42059
42060 //Pass a date string (default format):
42061 monthField.setValue('5/4/06');
42062
42063 //Pass a date string (custom format):
42064 monthField.format = 'Y-m-d';
42065 monthField.setValue('2006-5-4');
42066 </code></pre>
42067      * @param {String/Date} date The date or valid date string
42068      */
42069     setValue : function(date){
42070         Roo.log('month setValue' + date);
42071         // can only be first of month..
42072         
42073         var val = this.parseDate(date);
42074         
42075         if (this.hiddenField) {
42076             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42077         }
42078         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42079         this.value = this.parseDate(date);
42080     },
42081
42082     // private
42083     parseDate : function(value){
42084         if(!value || value instanceof Date){
42085             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42086             return value;
42087         }
42088         var v = Date.parseDate(value, this.format);
42089         if (!v && this.useIso) {
42090             v = Date.parseDate(value, 'Y-m-d');
42091         }
42092         if (v) {
42093             // 
42094             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42095         }
42096         
42097         
42098         if(!v && this.altFormats){
42099             if(!this.altFormatsArray){
42100                 this.altFormatsArray = this.altFormats.split("|");
42101             }
42102             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42103                 v = Date.parseDate(value, this.altFormatsArray[i]);
42104             }
42105         }
42106         return v;
42107     },
42108
42109     // private
42110     formatDate : function(date, fmt){
42111         return (!date || !(date instanceof Date)) ?
42112                date : date.dateFormat(fmt || this.format);
42113     },
42114
42115     // private
42116     menuListeners : {
42117         select: function(m, d){
42118             this.setValue(d);
42119             this.fireEvent('select', this, d);
42120         },
42121         show : function(){ // retain focus styling
42122             this.onFocus();
42123         },
42124         hide : function(){
42125             this.focus.defer(10, this);
42126             var ml = this.menuListeners;
42127             this.menu.un("select", ml.select,  this);
42128             this.menu.un("show", ml.show,  this);
42129             this.menu.un("hide", ml.hide,  this);
42130         }
42131     },
42132     // private
42133     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42134     onTriggerClick : function(){
42135         if(this.disabled){
42136             return;
42137         }
42138         if(this.menu == null){
42139             this.menu = new Roo.menu.DateMenu();
42140            
42141         }
42142         
42143         Roo.apply(this.menu.picker,  {
42144             
42145             showClear: this.allowBlank,
42146             minDate : this.minValue,
42147             maxDate : this.maxValue,
42148             disabledDatesRE : this.ddMatch,
42149             disabledDatesText : this.disabledDatesText,
42150             
42151             format : this.useIso ? 'Y-m-d' : this.format,
42152             minText : String.format(this.minText, this.formatDate(this.minValue)),
42153             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42154             
42155         });
42156          this.menu.on(Roo.apply({}, this.menuListeners, {
42157             scope:this
42158         }));
42159        
42160         
42161         var m = this.menu;
42162         var p = m.picker;
42163         
42164         // hide month picker get's called when we called by 'before hide';
42165         
42166         var ignorehide = true;
42167         p.hideMonthPicker  = function(disableAnim){
42168             if (ignorehide) {
42169                 return;
42170             }
42171              if(this.monthPicker){
42172                 Roo.log("hideMonthPicker called");
42173                 if(disableAnim === true){
42174                     this.monthPicker.hide();
42175                 }else{
42176                     this.monthPicker.slideOut('t', {duration:.2});
42177                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42178                     p.fireEvent("select", this, this.value);
42179                     m.hide();
42180                 }
42181             }
42182         }
42183         
42184         Roo.log('picker set value');
42185         Roo.log(this.getValue());
42186         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42187         m.show(this.el, 'tl-bl?');
42188         ignorehide  = false;
42189         // this will trigger hideMonthPicker..
42190         
42191         
42192         // hidden the day picker
42193         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42194         
42195         
42196         
42197       
42198         
42199         p.showMonthPicker.defer(100, p);
42200     
42201         
42202        
42203     },
42204
42205     beforeBlur : function(){
42206         var v = this.parseDate(this.getRawValue());
42207         if(v){
42208             this.setValue(v);
42209         }
42210     }
42211
42212     /** @cfg {Boolean} grow @hide */
42213     /** @cfg {Number} growMin @hide */
42214     /** @cfg {Number} growMax @hide */
42215     /**
42216      * @hide
42217      * @method autoSize
42218      */
42219 });/*
42220  * Based on:
42221  * Ext JS Library 1.1.1
42222  * Copyright(c) 2006-2007, Ext JS, LLC.
42223  *
42224  * Originally Released Under LGPL - original licence link has changed is not relivant.
42225  *
42226  * Fork - LGPL
42227  * <script type="text/javascript">
42228  */
42229  
42230
42231 /**
42232  * @class Roo.form.ComboBox
42233  * @extends Roo.form.TriggerField
42234  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42235  * @constructor
42236  * Create a new ComboBox.
42237  * @param {Object} config Configuration options
42238  */
42239 Roo.form.ComboBox = function(config){
42240     Roo.form.ComboBox.superclass.constructor.call(this, config);
42241     this.addEvents({
42242         /**
42243          * @event expand
42244          * Fires when the dropdown list is expanded
42245              * @param {Roo.form.ComboBox} combo This combo box
42246              */
42247         'expand' : true,
42248         /**
42249          * @event collapse
42250          * Fires when the dropdown list is collapsed
42251              * @param {Roo.form.ComboBox} combo This combo box
42252              */
42253         'collapse' : true,
42254         /**
42255          * @event beforeselect
42256          * Fires before a list item is selected. Return false to cancel the selection.
42257              * @param {Roo.form.ComboBox} combo This combo box
42258              * @param {Roo.data.Record} record The data record returned from the underlying store
42259              * @param {Number} index The index of the selected item in the dropdown list
42260              */
42261         'beforeselect' : true,
42262         /**
42263          * @event select
42264          * Fires when a list item is selected
42265              * @param {Roo.form.ComboBox} combo This combo box
42266              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42267              * @param {Number} index The index of the selected item in the dropdown list
42268              */
42269         'select' : true,
42270         /**
42271          * @event beforequery
42272          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42273          * The event object passed has these properties:
42274              * @param {Roo.form.ComboBox} combo This combo box
42275              * @param {String} query The query
42276              * @param {Boolean} forceAll true to force "all" query
42277              * @param {Boolean} cancel true to cancel the query
42278              * @param {Object} e The query event object
42279              */
42280         'beforequery': true,
42281          /**
42282          * @event add
42283          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42284              * @param {Roo.form.ComboBox} combo This combo box
42285              */
42286         'add' : true,
42287         /**
42288          * @event edit
42289          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42290              * @param {Roo.form.ComboBox} combo This combo box
42291              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42292              */
42293         'edit' : true
42294         
42295         
42296     });
42297     if(this.transform){
42298         this.allowDomMove = false;
42299         var s = Roo.getDom(this.transform);
42300         if(!this.hiddenName){
42301             this.hiddenName = s.name;
42302         }
42303         if(!this.store){
42304             this.mode = 'local';
42305             var d = [], opts = s.options;
42306             for(var i = 0, len = opts.length;i < len; i++){
42307                 var o = opts[i];
42308                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42309                 if(o.selected) {
42310                     this.value = value;
42311                 }
42312                 d.push([value, o.text]);
42313             }
42314             this.store = new Roo.data.SimpleStore({
42315                 'id': 0,
42316                 fields: ['value', 'text'],
42317                 data : d
42318             });
42319             this.valueField = 'value';
42320             this.displayField = 'text';
42321         }
42322         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42323         if(!this.lazyRender){
42324             this.target = true;
42325             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42326             s.parentNode.removeChild(s); // remove it
42327             this.render(this.el.parentNode);
42328         }else{
42329             s.parentNode.removeChild(s); // remove it
42330         }
42331
42332     }
42333     if (this.store) {
42334         this.store = Roo.factory(this.store, Roo.data);
42335     }
42336     
42337     this.selectedIndex = -1;
42338     if(this.mode == 'local'){
42339         if(config.queryDelay === undefined){
42340             this.queryDelay = 10;
42341         }
42342         if(config.minChars === undefined){
42343             this.minChars = 0;
42344         }
42345     }
42346 };
42347
42348 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42349     /**
42350      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42351      */
42352     /**
42353      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42354      * rendering into an Roo.Editor, defaults to false)
42355      */
42356     /**
42357      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42358      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42359      */
42360     /**
42361      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42362      */
42363     /**
42364      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42365      * the dropdown list (defaults to undefined, with no header element)
42366      */
42367
42368      /**
42369      * @cfg {String/Roo.Template} tpl The template to use to render the output
42370      */
42371      
42372     // private
42373     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42374     /**
42375      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42376      */
42377     listWidth: undefined,
42378     /**
42379      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42380      * mode = 'remote' or 'text' if mode = 'local')
42381      */
42382     displayField: undefined,
42383     /**
42384      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42385      * mode = 'remote' or 'value' if mode = 'local'). 
42386      * Note: use of a valueField requires the user make a selection
42387      * in order for a value to be mapped.
42388      */
42389     valueField: undefined,
42390     
42391     
42392     /**
42393      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42394      * field's data value (defaults to the underlying DOM element's name)
42395      */
42396     hiddenName: undefined,
42397     /**
42398      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42399      */
42400     listClass: '',
42401     /**
42402      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42403      */
42404     selectedClass: 'x-combo-selected',
42405     /**
42406      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42407      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42408      * which displays a downward arrow icon).
42409      */
42410     triggerClass : 'x-form-arrow-trigger',
42411     /**
42412      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42413      */
42414     shadow:'sides',
42415     /**
42416      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42417      * anchor positions (defaults to 'tl-bl')
42418      */
42419     listAlign: 'tl-bl?',
42420     /**
42421      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42422      */
42423     maxHeight: 300,
42424     /**
42425      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42426      * query specified by the allQuery config option (defaults to 'query')
42427      */
42428     triggerAction: 'query',
42429     /**
42430      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42431      * (defaults to 4, does not apply if editable = false)
42432      */
42433     minChars : 4,
42434     /**
42435      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42436      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42437      */
42438     typeAhead: false,
42439     /**
42440      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42441      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42442      */
42443     queryDelay: 500,
42444     /**
42445      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42446      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42447      */
42448     pageSize: 0,
42449     /**
42450      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42451      * when editable = true (defaults to false)
42452      */
42453     selectOnFocus:false,
42454     /**
42455      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42456      */
42457     queryParam: 'query',
42458     /**
42459      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42460      * when mode = 'remote' (defaults to 'Loading...')
42461      */
42462     loadingText: 'Loading...',
42463     /**
42464      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42465      */
42466     resizable: false,
42467     /**
42468      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42469      */
42470     handleHeight : 8,
42471     /**
42472      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42473      * traditional select (defaults to true)
42474      */
42475     editable: true,
42476     /**
42477      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42478      */
42479     allQuery: '',
42480     /**
42481      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42482      */
42483     mode: 'remote',
42484     /**
42485      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42486      * listWidth has a higher value)
42487      */
42488     minListWidth : 70,
42489     /**
42490      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42491      * allow the user to set arbitrary text into the field (defaults to false)
42492      */
42493     forceSelection:false,
42494     /**
42495      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42496      * if typeAhead = true (defaults to 250)
42497      */
42498     typeAheadDelay : 250,
42499     /**
42500      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42501      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42502      */
42503     valueNotFoundText : undefined,
42504     /**
42505      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42506      */
42507     blockFocus : false,
42508     
42509     /**
42510      * @cfg {Boolean} disableClear Disable showing of clear button.
42511      */
42512     disableClear : false,
42513     /**
42514      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42515      */
42516     alwaysQuery : false,
42517     
42518     //private
42519     addicon : false,
42520     editicon: false,
42521     
42522     // element that contains real text value.. (when hidden is used..)
42523      
42524     // private
42525     onRender : function(ct, position)
42526     {
42527         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42528         
42529         if(this.hiddenName){
42530             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42531                     'before', true);
42532             this.hiddenField.value =
42533                 this.hiddenValue !== undefined ? this.hiddenValue :
42534                 this.value !== undefined ? this.value : '';
42535
42536             // prevent input submission
42537             this.el.dom.removeAttribute('name');
42538              
42539              
42540         }
42541         
42542         if(Roo.isGecko){
42543             this.el.dom.setAttribute('autocomplete', 'off');
42544         }
42545
42546         var cls = 'x-combo-list';
42547
42548         this.list = new Roo.Layer({
42549             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42550         });
42551
42552         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42553         this.list.setWidth(lw);
42554         this.list.swallowEvent('mousewheel');
42555         this.assetHeight = 0;
42556
42557         if(this.title){
42558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42559             this.assetHeight += this.header.getHeight();
42560         }
42561
42562         this.innerList = this.list.createChild({cls:cls+'-inner'});
42563         this.innerList.on('mouseover', this.onViewOver, this);
42564         this.innerList.on('mousemove', this.onViewMove, this);
42565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42566         
42567         if(this.allowBlank && !this.pageSize && !this.disableClear){
42568             this.footer = this.list.createChild({cls:cls+'-ft'});
42569             this.pageTb = new Roo.Toolbar(this.footer);
42570            
42571         }
42572         if(this.pageSize){
42573             this.footer = this.list.createChild({cls:cls+'-ft'});
42574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42575                     {pageSize: this.pageSize});
42576             
42577         }
42578         
42579         if (this.pageTb && this.allowBlank && !this.disableClear) {
42580             var _this = this;
42581             this.pageTb.add(new Roo.Toolbar.Fill(), {
42582                 cls: 'x-btn-icon x-btn-clear',
42583                 text: '&#160;',
42584                 handler: function()
42585                 {
42586                     _this.collapse();
42587                     _this.clearValue();
42588                     _this.onSelect(false, -1);
42589                 }
42590             });
42591         }
42592         if (this.footer) {
42593             this.assetHeight += this.footer.getHeight();
42594         }
42595         
42596
42597         if(!this.tpl){
42598             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42599         }
42600
42601         this.view = new Roo.View(this.innerList, this.tpl, {
42602             singleSelect:true,
42603             store: this.store,
42604             selectedClass: this.selectedClass
42605         });
42606
42607         this.view.on('click', this.onViewClick, this);
42608
42609         this.store.on('beforeload', this.onBeforeLoad, this);
42610         this.store.on('load', this.onLoad, this);
42611         this.store.on('loadexception', this.onLoadException, this);
42612
42613         if(this.resizable){
42614             this.resizer = new Roo.Resizable(this.list,  {
42615                pinned:true, handles:'se'
42616             });
42617             this.resizer.on('resize', function(r, w, h){
42618                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42619                 this.listWidth = w;
42620                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42621                 this.restrictHeight();
42622             }, this);
42623             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42624         }
42625         if(!this.editable){
42626             this.editable = true;
42627             this.setEditable(false);
42628         }  
42629         
42630         
42631         if (typeof(this.events.add.listeners) != 'undefined') {
42632             
42633             this.addicon = this.wrap.createChild(
42634                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42635        
42636             this.addicon.on('click', function(e) {
42637                 this.fireEvent('add', this);
42638             }, this);
42639         }
42640         if (typeof(this.events.edit.listeners) != 'undefined') {
42641             
42642             this.editicon = this.wrap.createChild(
42643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42644             if (this.addicon) {
42645                 this.editicon.setStyle('margin-left', '40px');
42646             }
42647             this.editicon.on('click', function(e) {
42648                 
42649                 // we fire even  if inothing is selected..
42650                 this.fireEvent('edit', this, this.lastData );
42651                 
42652             }, this);
42653         }
42654         
42655         
42656         
42657     },
42658
42659     // private
42660     initEvents : function(){
42661         Roo.form.ComboBox.superclass.initEvents.call(this);
42662
42663         this.keyNav = new Roo.KeyNav(this.el, {
42664             "up" : function(e){
42665                 this.inKeyMode = true;
42666                 this.selectPrev();
42667             },
42668
42669             "down" : function(e){
42670                 if(!this.isExpanded()){
42671                     this.onTriggerClick();
42672                 }else{
42673                     this.inKeyMode = true;
42674                     this.selectNext();
42675                 }
42676             },
42677
42678             "enter" : function(e){
42679                 this.onViewClick();
42680                 //return true;
42681             },
42682
42683             "esc" : function(e){
42684                 this.collapse();
42685             },
42686
42687             "tab" : function(e){
42688                 this.onViewClick(false);
42689                 this.fireEvent("specialkey", this, e);
42690                 return true;
42691             },
42692
42693             scope : this,
42694
42695             doRelay : function(foo, bar, hname){
42696                 if(hname == 'down' || this.scope.isExpanded()){
42697                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42698                 }
42699                 return true;
42700             },
42701
42702             forceKeyDown: true
42703         });
42704         this.queryDelay = Math.max(this.queryDelay || 10,
42705                 this.mode == 'local' ? 10 : 250);
42706         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42707         if(this.typeAhead){
42708             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42709         }
42710         if(this.editable !== false){
42711             this.el.on("keyup", this.onKeyUp, this);
42712         }
42713         if(this.forceSelection){
42714             this.on('blur', this.doForce, this);
42715         }
42716     },
42717
42718     onDestroy : function(){
42719         if(this.view){
42720             this.view.setStore(null);
42721             this.view.el.removeAllListeners();
42722             this.view.el.remove();
42723             this.view.purgeListeners();
42724         }
42725         if(this.list){
42726             this.list.destroy();
42727         }
42728         if(this.store){
42729             this.store.un('beforeload', this.onBeforeLoad, this);
42730             this.store.un('load', this.onLoad, this);
42731             this.store.un('loadexception', this.onLoadException, this);
42732         }
42733         Roo.form.ComboBox.superclass.onDestroy.call(this);
42734     },
42735
42736     // private
42737     fireKey : function(e){
42738         if(e.isNavKeyPress() && !this.list.isVisible()){
42739             this.fireEvent("specialkey", this, e);
42740         }
42741     },
42742
42743     // private
42744     onResize: function(w, h){
42745         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42746         
42747         if(typeof w != 'number'){
42748             // we do not handle it!?!?
42749             return;
42750         }
42751         var tw = this.trigger.getWidth();
42752         tw += this.addicon ? this.addicon.getWidth() : 0;
42753         tw += this.editicon ? this.editicon.getWidth() : 0;
42754         var x = w - tw;
42755         this.el.setWidth( this.adjustWidth('input', x));
42756             
42757         this.trigger.setStyle('left', x+'px');
42758         
42759         if(this.list && this.listWidth === undefined){
42760             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42761             this.list.setWidth(lw);
42762             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42763         }
42764         
42765     
42766         
42767     },
42768
42769     /**
42770      * Allow or prevent the user from directly editing the field text.  If false is passed,
42771      * the user will only be able to select from the items defined in the dropdown list.  This method
42772      * is the runtime equivalent of setting the 'editable' config option at config time.
42773      * @param {Boolean} value True to allow the user to directly edit the field text
42774      */
42775     setEditable : function(value){
42776         if(value == this.editable){
42777             return;
42778         }
42779         this.editable = value;
42780         if(!value){
42781             this.el.dom.setAttribute('readOnly', true);
42782             this.el.on('mousedown', this.onTriggerClick,  this);
42783             this.el.addClass('x-combo-noedit');
42784         }else{
42785             this.el.dom.setAttribute('readOnly', false);
42786             this.el.un('mousedown', this.onTriggerClick,  this);
42787             this.el.removeClass('x-combo-noedit');
42788         }
42789     },
42790
42791     // private
42792     onBeforeLoad : function(){
42793         if(!this.hasFocus){
42794             return;
42795         }
42796         this.innerList.update(this.loadingText ?
42797                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42798         this.restrictHeight();
42799         this.selectedIndex = -1;
42800     },
42801
42802     // private
42803     onLoad : function(){
42804         if(!this.hasFocus){
42805             return;
42806         }
42807         if(this.store.getCount() > 0){
42808             this.expand();
42809             this.restrictHeight();
42810             if(this.lastQuery == this.allQuery){
42811                 if(this.editable){
42812                     this.el.dom.select();
42813                 }
42814                 if(!this.selectByValue(this.value, true)){
42815                     this.select(0, true);
42816                 }
42817             }else{
42818                 this.selectNext();
42819                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42820                     this.taTask.delay(this.typeAheadDelay);
42821                 }
42822             }
42823         }else{
42824             this.onEmptyResults();
42825         }
42826         //this.el.focus();
42827     },
42828     // private
42829     onLoadException : function()
42830     {
42831         this.collapse();
42832         Roo.log(this.store.reader.jsonData);
42833         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42834             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42835         }
42836         
42837         
42838     },
42839     // private
42840     onTypeAhead : function(){
42841         if(this.store.getCount() > 0){
42842             var r = this.store.getAt(0);
42843             var newValue = r.data[this.displayField];
42844             var len = newValue.length;
42845             var selStart = this.getRawValue().length;
42846             if(selStart != len){
42847                 this.setRawValue(newValue);
42848                 this.selectText(selStart, newValue.length);
42849             }
42850         }
42851     },
42852
42853     // private
42854     onSelect : function(record, index){
42855         if(this.fireEvent('beforeselect', this, record, index) !== false){
42856             this.setFromData(index > -1 ? record.data : false);
42857             this.collapse();
42858             this.fireEvent('select', this, record, index);
42859         }
42860     },
42861
42862     /**
42863      * Returns the currently selected field value or empty string if no value is set.
42864      * @return {String} value The selected value
42865      */
42866     getValue : function(){
42867         if(this.valueField){
42868             return typeof this.value != 'undefined' ? this.value : '';
42869         }
42870         return Roo.form.ComboBox.superclass.getValue.call(this);
42871     },
42872
42873     /**
42874      * Clears any text/value currently set in the field
42875      */
42876     clearValue : function(){
42877         if(this.hiddenField){
42878             this.hiddenField.value = '';
42879         }
42880         this.value = '';
42881         this.setRawValue('');
42882         this.lastSelectionText = '';
42883         
42884     },
42885
42886     /**
42887      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42888      * will be displayed in the field.  If the value does not match the data value of an existing item,
42889      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42890      * Otherwise the field will be blank (although the value will still be set).
42891      * @param {String} value The value to match
42892      */
42893     setValue : function(v){
42894         var text = v;
42895         if(this.valueField){
42896             var r = this.findRecord(this.valueField, v);
42897             if(r){
42898                 text = r.data[this.displayField];
42899             }else if(this.valueNotFoundText !== undefined){
42900                 text = this.valueNotFoundText;
42901             }
42902         }
42903         this.lastSelectionText = text;
42904         if(this.hiddenField){
42905             this.hiddenField.value = v;
42906         }
42907         Roo.form.ComboBox.superclass.setValue.call(this, text);
42908         this.value = v;
42909     },
42910     /**
42911      * @property {Object} the last set data for the element
42912      */
42913     
42914     lastData : false,
42915     /**
42916      * Sets the value of the field based on a object which is related to the record format for the store.
42917      * @param {Object} value the value to set as. or false on reset?
42918      */
42919     setFromData : function(o){
42920         var dv = ''; // display value
42921         var vv = ''; // value value..
42922         this.lastData = o;
42923         if (this.displayField) {
42924             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42925         } else {
42926             // this is an error condition!!!
42927             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42928         }
42929         
42930         if(this.valueField){
42931             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42932         }
42933         if(this.hiddenField){
42934             this.hiddenField.value = vv;
42935             
42936             this.lastSelectionText = dv;
42937             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42938             this.value = vv;
42939             return;
42940         }
42941         // no hidden field.. - we store the value in 'value', but still display
42942         // display field!!!!
42943         this.lastSelectionText = dv;
42944         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42945         this.value = vv;
42946         
42947         
42948     },
42949     // private
42950     reset : function(){
42951         // overridden so that last data is reset..
42952         this.setValue(this.resetValue);
42953         this.originalValue = this.getValue();
42954         this.clearInvalid();
42955         this.lastData = false;
42956         if (this.view) {
42957             this.view.clearSelections();
42958         }
42959     },
42960     // private
42961     findRecord : function(prop, value){
42962         var record;
42963         if(this.store.getCount() > 0){
42964             this.store.each(function(r){
42965                 if(r.data[prop] == value){
42966                     record = r;
42967                     return false;
42968                 }
42969                 return true;
42970             });
42971         }
42972         return record;
42973     },
42974     
42975     getName: function()
42976     {
42977         // returns hidden if it's set..
42978         if (!this.rendered) {return ''};
42979         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42980         
42981     },
42982     // private
42983     onViewMove : function(e, t){
42984         this.inKeyMode = false;
42985     },
42986
42987     // private
42988     onViewOver : function(e, t){
42989         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42990             return;
42991         }
42992         var item = this.view.findItemFromChild(t);
42993         if(item){
42994             var index = this.view.indexOf(item);
42995             this.select(index, false);
42996         }
42997     },
42998
42999     // private
43000     onViewClick : function(doFocus)
43001     {
43002         var index = this.view.getSelectedIndexes()[0];
43003         var r = this.store.getAt(index);
43004         if(r){
43005             this.onSelect(r, index);
43006         }
43007         if(doFocus !== false && !this.blockFocus){
43008             this.el.focus();
43009         }
43010     },
43011
43012     // private
43013     restrictHeight : function(){
43014         this.innerList.dom.style.height = '';
43015         var inner = this.innerList.dom;
43016         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43017         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43018         this.list.beginUpdate();
43019         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43020         this.list.alignTo(this.el, this.listAlign);
43021         this.list.endUpdate();
43022     },
43023
43024     // private
43025     onEmptyResults : function(){
43026         this.collapse();
43027     },
43028
43029     /**
43030      * Returns true if the dropdown list is expanded, else false.
43031      */
43032     isExpanded : function(){
43033         return this.list.isVisible();
43034     },
43035
43036     /**
43037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43039      * @param {String} value The data value of the item to select
43040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43041      * selected item if it is not currently in view (defaults to true)
43042      * @return {Boolean} True if the value matched an item in the list, else false
43043      */
43044     selectByValue : function(v, scrollIntoView){
43045         if(v !== undefined && v !== null){
43046             var r = this.findRecord(this.valueField || this.displayField, v);
43047             if(r){
43048                 this.select(this.store.indexOf(r), scrollIntoView);
43049                 return true;
43050             }
43051         }
43052         return false;
43053     },
43054
43055     /**
43056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43058      * @param {Number} index The zero-based index of the list item to select
43059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43060      * selected item if it is not currently in view (defaults to true)
43061      */
43062     select : function(index, scrollIntoView){
43063         this.selectedIndex = index;
43064         this.view.select(index);
43065         if(scrollIntoView !== false){
43066             var el = this.view.getNode(index);
43067             if(el){
43068                 this.innerList.scrollChildIntoView(el, false);
43069             }
43070         }
43071     },
43072
43073     // private
43074     selectNext : function(){
43075         var ct = this.store.getCount();
43076         if(ct > 0){
43077             if(this.selectedIndex == -1){
43078                 this.select(0);
43079             }else if(this.selectedIndex < ct-1){
43080                 this.select(this.selectedIndex+1);
43081             }
43082         }
43083     },
43084
43085     // private
43086     selectPrev : function(){
43087         var ct = this.store.getCount();
43088         if(ct > 0){
43089             if(this.selectedIndex == -1){
43090                 this.select(0);
43091             }else if(this.selectedIndex != 0){
43092                 this.select(this.selectedIndex-1);
43093             }
43094         }
43095     },
43096
43097     // private
43098     onKeyUp : function(e){
43099         if(this.editable !== false && !e.isSpecialKey()){
43100             this.lastKey = e.getKey();
43101             this.dqTask.delay(this.queryDelay);
43102         }
43103     },
43104
43105     // private
43106     validateBlur : function(){
43107         return !this.list || !this.list.isVisible();   
43108     },
43109
43110     // private
43111     initQuery : function(){
43112         this.doQuery(this.getRawValue());
43113     },
43114
43115     // private
43116     doForce : function(){
43117         if(this.el.dom.value.length > 0){
43118             this.el.dom.value =
43119                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43120              
43121         }
43122     },
43123
43124     /**
43125      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43126      * query allowing the query action to be canceled if needed.
43127      * @param {String} query The SQL query to execute
43128      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43129      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43130      * saved in the current store (defaults to false)
43131      */
43132     doQuery : function(q, forceAll){
43133         if(q === undefined || q === null){
43134             q = '';
43135         }
43136         var qe = {
43137             query: q,
43138             forceAll: forceAll,
43139             combo: this,
43140             cancel:false
43141         };
43142         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43143             return false;
43144         }
43145         q = qe.query;
43146         forceAll = qe.forceAll;
43147         if(forceAll === true || (q.length >= this.minChars)){
43148             if(this.lastQuery != q || this.alwaysQuery){
43149                 this.lastQuery = q;
43150                 if(this.mode == 'local'){
43151                     this.selectedIndex = -1;
43152                     if(forceAll){
43153                         this.store.clearFilter();
43154                     }else{
43155                         this.store.filter(this.displayField, q);
43156                     }
43157                     this.onLoad();
43158                 }else{
43159                     this.store.baseParams[this.queryParam] = q;
43160                     this.store.load({
43161                         params: this.getParams(q)
43162                     });
43163                     this.expand();
43164                 }
43165             }else{
43166                 this.selectedIndex = -1;
43167                 this.onLoad();   
43168             }
43169         }
43170     },
43171
43172     // private
43173     getParams : function(q){
43174         var p = {};
43175         //p[this.queryParam] = q;
43176         if(this.pageSize){
43177             p.start = 0;
43178             p.limit = this.pageSize;
43179         }
43180         return p;
43181     },
43182
43183     /**
43184      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43185      */
43186     collapse : function(){
43187         if(!this.isExpanded()){
43188             return;
43189         }
43190         this.list.hide();
43191         Roo.get(document).un('mousedown', this.collapseIf, this);
43192         Roo.get(document).un('mousewheel', this.collapseIf, this);
43193         if (!this.editable) {
43194             Roo.get(document).un('keydown', this.listKeyPress, this);
43195         }
43196         this.fireEvent('collapse', this);
43197     },
43198
43199     // private
43200     collapseIf : function(e){
43201         if(!e.within(this.wrap) && !e.within(this.list)){
43202             this.collapse();
43203         }
43204     },
43205
43206     /**
43207      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43208      */
43209     expand : function(){
43210         if(this.isExpanded() || !this.hasFocus){
43211             return;
43212         }
43213         this.list.alignTo(this.el, this.listAlign);
43214         this.list.show();
43215         Roo.get(document).on('mousedown', this.collapseIf, this);
43216         Roo.get(document).on('mousewheel', this.collapseIf, this);
43217         if (!this.editable) {
43218             Roo.get(document).on('keydown', this.listKeyPress, this);
43219         }
43220         
43221         this.fireEvent('expand', this);
43222     },
43223
43224     // private
43225     // Implements the default empty TriggerField.onTriggerClick function
43226     onTriggerClick : function(){
43227         if(this.disabled){
43228             return;
43229         }
43230         if(this.isExpanded()){
43231             this.collapse();
43232             if (!this.blockFocus) {
43233                 this.el.focus();
43234             }
43235             
43236         }else {
43237             this.hasFocus = true;
43238             if(this.triggerAction == 'all') {
43239                 this.doQuery(this.allQuery, true);
43240             } else {
43241                 this.doQuery(this.getRawValue());
43242             }
43243             if (!this.blockFocus) {
43244                 this.el.focus();
43245             }
43246         }
43247     },
43248     listKeyPress : function(e)
43249     {
43250         //Roo.log('listkeypress');
43251         // scroll to first matching element based on key pres..
43252         if (e.isSpecialKey()) {
43253             return false;
43254         }
43255         var k = String.fromCharCode(e.getKey()).toUpperCase();
43256         //Roo.log(k);
43257         var match  = false;
43258         var csel = this.view.getSelectedNodes();
43259         var cselitem = false;
43260         if (csel.length) {
43261             var ix = this.view.indexOf(csel[0]);
43262             cselitem  = this.store.getAt(ix);
43263             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43264                 cselitem = false;
43265             }
43266             
43267         }
43268         
43269         this.store.each(function(v) { 
43270             if (cselitem) {
43271                 // start at existing selection.
43272                 if (cselitem.id == v.id) {
43273                     cselitem = false;
43274                 }
43275                 return;
43276             }
43277                 
43278             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43279                 match = this.store.indexOf(v);
43280                 return false;
43281             }
43282         }, this);
43283         
43284         if (match === false) {
43285             return true; // no more action?
43286         }
43287         // scroll to?
43288         this.view.select(match);
43289         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43290         sn.scrollIntoView(sn.dom.parentNode, false);
43291     } 
43292
43293     /** 
43294     * @cfg {Boolean} grow 
43295     * @hide 
43296     */
43297     /** 
43298     * @cfg {Number} growMin 
43299     * @hide 
43300     */
43301     /** 
43302     * @cfg {Number} growMax 
43303     * @hide 
43304     */
43305     /**
43306      * @hide
43307      * @method autoSize
43308      */
43309 });/*
43310  * Copyright(c) 2010-2012, Roo J Solutions Limited
43311  *
43312  * Licence LGPL
43313  *
43314  */
43315
43316 /**
43317  * @class Roo.form.ComboBoxArray
43318  * @extends Roo.form.TextField
43319  * A facebook style adder... for lists of email / people / countries  etc...
43320  * pick multiple items from a combo box, and shows each one.
43321  *
43322  *  Fred [x]  Brian [x]  [Pick another |v]
43323  *
43324  *
43325  *  For this to work: it needs various extra information
43326  *    - normal combo problay has
43327  *      name, hiddenName
43328  *    + displayField, valueField
43329  *
43330  *    For our purpose...
43331  *
43332  *
43333  *   If we change from 'extends' to wrapping...
43334  *   
43335  *  
43336  *
43337  
43338  
43339  * @constructor
43340  * Create a new ComboBoxArray.
43341  * @param {Object} config Configuration options
43342  */
43343  
43344
43345 Roo.form.ComboBoxArray = function(config)
43346 {
43347     this.addEvents({
43348         /**
43349          * @event beforeremove
43350          * Fires before remove the value from the list
43351              * @param {Roo.form.ComboBoxArray} _self This combo box array
43352              * @param {Roo.form.ComboBoxArray.Item} item removed item
43353              */
43354         'beforeremove' : true,
43355         /**
43356          * @event remove
43357          * Fires when remove the value from the list
43358              * @param {Roo.form.ComboBoxArray} _self This combo box array
43359              * @param {Roo.form.ComboBoxArray.Item} item removed item
43360              */
43361         'remove' : true
43362         
43363         
43364     });
43365     
43366     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43367     
43368     this.items = new Roo.util.MixedCollection(false);
43369     
43370     // construct the child combo...
43371     
43372     
43373     
43374     
43375    
43376     
43377 }
43378
43379  
43380 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43381
43382     /**
43383      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43384      */
43385     
43386     lastData : false,
43387     
43388     // behavies liek a hiddne field
43389     inputType:      'hidden',
43390     /**
43391      * @cfg {Number} width The width of the box that displays the selected element
43392      */ 
43393     width:          300,
43394
43395     
43396     
43397     /**
43398      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43399      */
43400     name : false,
43401     /**
43402      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43403      */
43404     hiddenName : false,
43405       /**
43406      * @cfg {String} seperator    The value seperator normally ',' 
43407      */
43408     seperator : ',',
43409     
43410     // private the array of items that are displayed..
43411     items  : false,
43412     // private - the hidden field el.
43413     hiddenEl : false,
43414     // private - the filed el..
43415     el : false,
43416     
43417     //validateValue : function() { return true; }, // all values are ok!
43418     //onAddClick: function() { },
43419     
43420     onRender : function(ct, position) 
43421     {
43422         
43423         // create the standard hidden element
43424         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43425         
43426         
43427         // give fake names to child combo;
43428         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43429         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43430         
43431         this.combo = Roo.factory(this.combo, Roo.form);
43432         this.combo.onRender(ct, position);
43433         if (typeof(this.combo.width) != 'undefined') {
43434             this.combo.onResize(this.combo.width,0);
43435         }
43436         
43437         this.combo.initEvents();
43438         
43439         // assigned so form know we need to do this..
43440         this.store          = this.combo.store;
43441         this.valueField     = this.combo.valueField;
43442         this.displayField   = this.combo.displayField ;
43443         
43444         
43445         this.combo.wrap.addClass('x-cbarray-grp');
43446         
43447         var cbwrap = this.combo.wrap.createChild(
43448             {tag: 'div', cls: 'x-cbarray-cb'},
43449             this.combo.el.dom
43450         );
43451         
43452              
43453         this.hiddenEl = this.combo.wrap.createChild({
43454             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43455         });
43456         this.el = this.combo.wrap.createChild({
43457             tag: 'input',  type:'hidden' , name: this.name, value : ''
43458         });
43459          //   this.el.dom.removeAttribute("name");
43460         
43461         
43462         this.outerWrap = this.combo.wrap;
43463         this.wrap = cbwrap;
43464         
43465         this.outerWrap.setWidth(this.width);
43466         this.outerWrap.dom.removeChild(this.el.dom);
43467         
43468         this.wrap.dom.appendChild(this.el.dom);
43469         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43470         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43471         
43472         this.combo.trigger.setStyle('position','relative');
43473         this.combo.trigger.setStyle('left', '0px');
43474         this.combo.trigger.setStyle('top', '2px');
43475         
43476         this.combo.el.setStyle('vertical-align', 'text-bottom');
43477         
43478         //this.trigger.setStyle('vertical-align', 'top');
43479         
43480         // this should use the code from combo really... on('add' ....)
43481         if (this.adder) {
43482             
43483         
43484             this.adder = this.outerWrap.createChild(
43485                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43486             var _t = this;
43487             this.adder.on('click', function(e) {
43488                 _t.fireEvent('adderclick', this, e);
43489             }, _t);
43490         }
43491         //var _t = this;
43492         //this.adder.on('click', this.onAddClick, _t);
43493         
43494         
43495         this.combo.on('select', function(cb, rec, ix) {
43496             this.addItem(rec.data);
43497             
43498             cb.setValue('');
43499             cb.el.dom.value = '';
43500             //cb.lastData = rec.data;
43501             // add to list
43502             
43503         }, this);
43504         
43505         
43506     },
43507     
43508     
43509     getName: function()
43510     {
43511         // returns hidden if it's set..
43512         if (!this.rendered) {return ''};
43513         return  this.hiddenName ? this.hiddenName : this.name;
43514         
43515     },
43516     
43517     
43518     onResize: function(w, h){
43519         
43520         return;
43521         // not sure if this is needed..
43522         //this.combo.onResize(w,h);
43523         
43524         if(typeof w != 'number'){
43525             // we do not handle it!?!?
43526             return;
43527         }
43528         var tw = this.combo.trigger.getWidth();
43529         tw += this.addicon ? this.addicon.getWidth() : 0;
43530         tw += this.editicon ? this.editicon.getWidth() : 0;
43531         var x = w - tw;
43532         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43533             
43534         this.combo.trigger.setStyle('left', '0px');
43535         
43536         if(this.list && this.listWidth === undefined){
43537             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43538             this.list.setWidth(lw);
43539             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43540         }
43541         
43542     
43543         
43544     },
43545     
43546     addItem: function(rec)
43547     {
43548         var valueField = this.combo.valueField;
43549         var displayField = this.combo.displayField;
43550         
43551         if (this.items.indexOfKey(rec[valueField]) > -1) {
43552             //console.log("GOT " + rec.data.id);
43553             return;
43554         }
43555         
43556         var x = new Roo.form.ComboBoxArray.Item({
43557             //id : rec[this.idField],
43558             data : rec,
43559             displayField : displayField ,
43560             tipField : displayField ,
43561             cb : this
43562         });
43563         // use the 
43564         this.items.add(rec[valueField],x);
43565         // add it before the element..
43566         this.updateHiddenEl();
43567         x.render(this.outerWrap, this.wrap.dom);
43568         // add the image handler..
43569     },
43570     
43571     updateHiddenEl : function()
43572     {
43573         this.validate();
43574         if (!this.hiddenEl) {
43575             return;
43576         }
43577         var ar = [];
43578         var idField = this.combo.valueField;
43579         
43580         this.items.each(function(f) {
43581             ar.push(f.data[idField]);
43582         });
43583         this.hiddenEl.dom.value = ar.join(this.seperator);
43584         this.validate();
43585     },
43586     
43587     reset : function()
43588     {
43589         this.items.clear();
43590         
43591         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43592            el.remove();
43593         });
43594         
43595         this.el.dom.value = '';
43596         if (this.hiddenEl) {
43597             this.hiddenEl.dom.value = '';
43598         }
43599         
43600     },
43601     getValue: function()
43602     {
43603         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43604     },
43605     setValue: function(v) // not a valid action - must use addItems..
43606     {
43607         
43608         this.reset();
43609          
43610         if (this.store.isLocal && (typeof(v) == 'string')) {
43611             // then we can use the store to find the values..
43612             // comma seperated at present.. this needs to allow JSON based encoding..
43613             this.hiddenEl.value  = v;
43614             var v_ar = [];
43615             Roo.each(v.split(this.seperator), function(k) {
43616                 Roo.log("CHECK " + this.valueField + ',' + k);
43617                 var li = this.store.query(this.valueField, k);
43618                 if (!li.length) {
43619                     return;
43620                 }
43621                 var add = {};
43622                 add[this.valueField] = k;
43623                 add[this.displayField] = li.item(0).data[this.displayField];
43624                 
43625                 this.addItem(add);
43626             }, this) 
43627              
43628         }
43629         if (typeof(v) == 'object' ) {
43630             // then let's assume it's an array of objects..
43631             Roo.each(v, function(l) {
43632                 var add = l;
43633                 if (typeof(l) == 'string') {
43634                     add = {};
43635                     add[this.valueField] = l;
43636                     add[this.displayField] = l
43637                 }
43638                 this.addItem(add);
43639             }, this);
43640              
43641         }
43642         
43643         
43644     },
43645     setFromData: function(v)
43646     {
43647         // this recieves an object, if setValues is called.
43648         this.reset();
43649         this.el.dom.value = v[this.displayField];
43650         this.hiddenEl.dom.value = v[this.valueField];
43651         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43652             return;
43653         }
43654         var kv = v[this.valueField];
43655         var dv = v[this.displayField];
43656         kv = typeof(kv) != 'string' ? '' : kv;
43657         dv = typeof(dv) != 'string' ? '' : dv;
43658         
43659         
43660         var keys = kv.split(this.seperator);
43661         var display = dv.split(this.seperator);
43662         for (var i = 0 ; i < keys.length; i++) {
43663             add = {};
43664             add[this.valueField] = keys[i];
43665             add[this.displayField] = display[i];
43666             this.addItem(add);
43667         }
43668       
43669         
43670     },
43671     
43672     /**
43673      * Validates the combox array value
43674      * @return {Boolean} True if the value is valid, else false
43675      */
43676     validate : function(){
43677         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43678             this.clearInvalid();
43679             return true;
43680         }
43681         return false;
43682     },
43683     
43684     validateValue : function(value){
43685         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43686         
43687     },
43688     
43689     /*@
43690      * overide
43691      * 
43692      */
43693     isDirty : function() {
43694         if(this.disabled) {
43695             return false;
43696         }
43697         
43698         try {
43699             var d = Roo.decode(String(this.originalValue));
43700         } catch (e) {
43701             return String(this.getValue()) !== String(this.originalValue);
43702         }
43703         
43704         var originalValue = [];
43705         
43706         for (var i = 0; i < d.length; i++){
43707             originalValue.push(d[i][this.valueField]);
43708         }
43709         
43710         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43711         
43712     }
43713     
43714 });
43715
43716
43717
43718 /**
43719  * @class Roo.form.ComboBoxArray.Item
43720  * @extends Roo.BoxComponent
43721  * A selected item in the list
43722  *  Fred [x]  Brian [x]  [Pick another |v]
43723  * 
43724  * @constructor
43725  * Create a new item.
43726  * @param {Object} config Configuration options
43727  */
43728  
43729 Roo.form.ComboBoxArray.Item = function(config) {
43730     config.id = Roo.id();
43731     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43732 }
43733
43734 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43735     data : {},
43736     cb: false,
43737     displayField : false,
43738     tipField : false,
43739     
43740     
43741     defaultAutoCreate : {
43742         tag: 'div',
43743         cls: 'x-cbarray-item',
43744         cn : [ 
43745             { tag: 'div' },
43746             {
43747                 tag: 'img',
43748                 width:16,
43749                 height : 16,
43750                 src : Roo.BLANK_IMAGE_URL ,
43751                 align: 'center'
43752             }
43753         ]
43754         
43755     },
43756     
43757  
43758     onRender : function(ct, position)
43759     {
43760         Roo.form.Field.superclass.onRender.call(this, ct, position);
43761         
43762         if(!this.el){
43763             var cfg = this.getAutoCreate();
43764             this.el = ct.createChild(cfg, position);
43765         }
43766         
43767         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43768         
43769         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43770             this.cb.renderer(this.data) :
43771             String.format('{0}',this.data[this.displayField]);
43772         
43773             
43774         this.el.child('div').dom.setAttribute('qtip',
43775                         String.format('{0}',this.data[this.tipField])
43776         );
43777         
43778         this.el.child('img').on('click', this.remove, this);
43779         
43780     },
43781    
43782     remove : function()
43783     {
43784         if(this.cb.disabled){
43785             return;
43786         }
43787         
43788         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43789             this.cb.items.remove(this);
43790             this.el.child('img').un('click', this.remove, this);
43791             this.el.remove();
43792             this.cb.updateHiddenEl();
43793
43794             this.cb.fireEvent('remove', this.cb, this);
43795         }
43796         
43797     }
43798 });/*
43799  * RooJS Library 1.1.1
43800  * Copyright(c) 2008-2011  Alan Knowles
43801  *
43802  * License - LGPL
43803  */
43804  
43805
43806 /**
43807  * @class Roo.form.ComboNested
43808  * @extends Roo.form.ComboBox
43809  * A combobox for that allows selection of nested items in a list,
43810  * eg.
43811  *
43812  *  Book
43813  *    -> red
43814  *    -> green
43815  *  Table
43816  *    -> square
43817  *      ->red
43818  *      ->green
43819  *    -> rectangle
43820  *      ->green
43821  *      
43822  * 
43823  * @constructor
43824  * Create a new ComboNested
43825  * @param {Object} config Configuration options
43826  */
43827 Roo.form.ComboNested = function(config){
43828     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43829     // should verify some data...
43830     // like
43831     // hiddenName = required..
43832     // displayField = required
43833     // valudField == required
43834     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43835     var _t = this;
43836     Roo.each(req, function(e) {
43837         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43838             throw "Roo.form.ComboNested : missing value for: " + e;
43839         }
43840     });
43841      
43842     
43843 };
43844
43845 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43846    
43847     /*
43848      * @config {Number} max Number of columns to show
43849      */
43850     
43851     maxColumns : 3,
43852    
43853     list : null, // the outermost div..
43854     innerLists : null, // the
43855     views : null,
43856     stores : null,
43857     // private
43858     loadingChildren : false,
43859     
43860     onRender : function(ct, position)
43861     {
43862         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43863         
43864         if(this.hiddenName){
43865             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43866                     'before', true);
43867             this.hiddenField.value =
43868                 this.hiddenValue !== undefined ? this.hiddenValue :
43869                 this.value !== undefined ? this.value : '';
43870
43871             // prevent input submission
43872             this.el.dom.removeAttribute('name');
43873              
43874              
43875         }
43876         
43877         if(Roo.isGecko){
43878             this.el.dom.setAttribute('autocomplete', 'off');
43879         }
43880
43881         var cls = 'x-combo-list';
43882
43883         this.list = new Roo.Layer({
43884             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43885         });
43886
43887         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43888         this.list.setWidth(lw);
43889         this.list.swallowEvent('mousewheel');
43890         this.assetHeight = 0;
43891
43892         if(this.title){
43893             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43894             this.assetHeight += this.header.getHeight();
43895         }
43896         this.innerLists = [];
43897         this.views = [];
43898         this.stores = [];
43899         for (var i =0 ; i < this.maxColumns; i++) {
43900             this.onRenderList( cls, i);
43901         }
43902         
43903         // always needs footer, as we are going to have an 'OK' button.
43904         this.footer = this.list.createChild({cls:cls+'-ft'});
43905         this.pageTb = new Roo.Toolbar(this.footer);  
43906         var _this = this;
43907         this.pageTb.add(  {
43908             
43909             text: 'Done',
43910             handler: function()
43911             {
43912                 _this.collapse();
43913             }
43914         });
43915         
43916         if ( this.allowBlank && !this.disableClear) {
43917             
43918             this.pageTb.add(new Roo.Toolbar.Fill(), {
43919                 cls: 'x-btn-icon x-btn-clear',
43920                 text: '&#160;',
43921                 handler: function()
43922                 {
43923                     _this.collapse();
43924                     _this.clearValue();
43925                     _this.onSelect(false, -1);
43926                 }
43927             });
43928         }
43929         if (this.footer) {
43930             this.assetHeight += this.footer.getHeight();
43931         }
43932         
43933     },
43934     onRenderList : function (  cls, i)
43935     {
43936         
43937         var lw = Math.floor(
43938                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43939         );
43940         
43941         this.list.setWidth(lw); // default to '1'
43942
43943         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43944         //il.on('mouseover', this.onViewOver, this, { list:  i });
43945         //il.on('mousemove', this.onViewMove, this, { list:  i });
43946         il.setWidth(lw);
43947         il.setStyle({ 'overflow-x' : 'hidden'});
43948
43949         if(!this.tpl){
43950             this.tpl = new Roo.Template({
43951                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43952                 isEmpty: function (value, allValues) {
43953                     //Roo.log(value);
43954                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43955                     return dl ? 'has-children' : 'no-children'
43956                 }
43957             });
43958         }
43959         
43960         var store  = this.store;
43961         if (i > 0) {
43962             store  = new Roo.data.SimpleStore({
43963                 //fields : this.store.reader.meta.fields,
43964                 reader : this.store.reader,
43965                 data : [ ]
43966             });
43967         }
43968         this.stores[i]  = store;
43969                   
43970         var view = this.views[i] = new Roo.View(
43971             il,
43972             this.tpl,
43973             {
43974                 singleSelect:true,
43975                 store: store,
43976                 selectedClass: this.selectedClass
43977             }
43978         );
43979         view.getEl().setWidth(lw);
43980         view.getEl().setStyle({
43981             position: i < 1 ? 'relative' : 'absolute',
43982             top: 0,
43983             left: (i * lw ) + 'px',
43984             display : i > 0 ? 'none' : 'block'
43985         });
43986         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43987         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43988         //view.on('click', this.onViewClick, this, { list : i });
43989
43990         store.on('beforeload', this.onBeforeLoad, this);
43991         store.on('load',  this.onLoad, this, { list  : i});
43992         store.on('loadexception', this.onLoadException, this);
43993
43994         // hide the other vies..
43995         
43996         
43997         
43998     },
43999       
44000     restrictHeight : function()
44001     {
44002         var mh = 0;
44003         Roo.each(this.innerLists, function(il,i) {
44004             var el = this.views[i].getEl();
44005             el.dom.style.height = '';
44006             var inner = el.dom;
44007             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44008             // only adjust heights on other ones..
44009             mh = Math.max(h, mh);
44010             if (i < 1) {
44011                 
44012                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44013                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44014                
44015             }
44016             
44017             
44018         }, this);
44019         
44020         this.list.beginUpdate();
44021         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44022         this.list.alignTo(this.el, this.listAlign);
44023         this.list.endUpdate();
44024         
44025     },
44026      
44027     
44028     // -- store handlers..
44029     // private
44030     onBeforeLoad : function()
44031     {
44032         if(!this.hasFocus){
44033             return;
44034         }
44035         this.innerLists[0].update(this.loadingText ?
44036                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44037         this.restrictHeight();
44038         this.selectedIndex = -1;
44039     },
44040     // private
44041     onLoad : function(a,b,c,d)
44042     {
44043         if (!this.loadingChildren) {
44044             // then we are loading the top level. - hide the children
44045             for (var i = 1;i < this.views.length; i++) {
44046                 this.views[i].getEl().setStyle({ display : 'none' });
44047             }
44048             var lw = Math.floor(
44049                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44050             );
44051         
44052              this.list.setWidth(lw); // default to '1'
44053
44054             
44055         }
44056         if(!this.hasFocus){
44057             return;
44058         }
44059         
44060         if(this.store.getCount() > 0) {
44061             this.expand();
44062             this.restrictHeight();   
44063         } else {
44064             this.onEmptyResults();
44065         }
44066         
44067         if (!this.loadingChildren) {
44068             this.selectActive();
44069         }
44070         /*
44071         this.stores[1].loadData([]);
44072         this.stores[2].loadData([]);
44073         this.views
44074         */    
44075     
44076         //this.el.focus();
44077     },
44078     
44079     
44080     // private
44081     onLoadException : function()
44082     {
44083         this.collapse();
44084         Roo.log(this.store.reader.jsonData);
44085         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44086             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44087         }
44088         
44089         
44090     },
44091     // no cleaning of leading spaces on blur here.
44092     cleanLeadingSpace : function(e) { },
44093     
44094
44095     onSelectChange : function (view, sels, opts )
44096     {
44097         var ix = view.getSelectedIndexes();
44098          
44099         if (opts.list > this.maxColumns - 2) {
44100             if (view.store.getCount()<  1) {
44101                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44102
44103             } else  {
44104                 if (ix.length) {
44105                     // used to clear ?? but if we are loading unselected 
44106                     this.setFromData(view.store.getAt(ix[0]).data);
44107                 }
44108                 
44109             }
44110             
44111             return;
44112         }
44113         
44114         if (!ix.length) {
44115             // this get's fired when trigger opens..
44116            // this.setFromData({});
44117             var str = this.stores[opts.list+1];
44118             str.data.clear(); // removeall wihtout the fire events..
44119             return;
44120         }
44121         
44122         var rec = view.store.getAt(ix[0]);
44123          
44124         this.setFromData(rec.data);
44125         this.fireEvent('select', this, rec, ix[0]);
44126         
44127         var lw = Math.floor(
44128              (
44129                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44130              ) / this.maxColumns
44131         );
44132         this.loadingChildren = true;
44133         this.stores[opts.list+1].loadDataFromChildren( rec );
44134         this.loadingChildren = false;
44135         var dl = this.stores[opts.list+1]. getTotalCount();
44136         
44137         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44138         
44139         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44140         for (var i = opts.list+2; i < this.views.length;i++) {
44141             this.views[i].getEl().setStyle({ display : 'none' });
44142         }
44143         
44144         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44145         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44146         
44147         if (this.isLoading) {
44148            // this.selectActive(opts.list);
44149         }
44150          
44151     },
44152     
44153     
44154     
44155     
44156     onDoubleClick : function()
44157     {
44158         this.collapse(); //??
44159     },
44160     
44161      
44162     
44163     
44164     
44165     // private
44166     recordToStack : function(store, prop, value, stack)
44167     {
44168         var cstore = new Roo.data.SimpleStore({
44169             //fields : this.store.reader.meta.fields, // we need array reader.. for
44170             reader : this.store.reader,
44171             data : [ ]
44172         });
44173         var _this = this;
44174         var record  = false;
44175         var srec = false;
44176         if(store.getCount() < 1){
44177             return false;
44178         }
44179         store.each(function(r){
44180             if(r.data[prop] == value){
44181                 record = r;
44182             srec = r;
44183                 return false;
44184             }
44185             if (r.data.cn && r.data.cn.length) {
44186                 cstore.loadDataFromChildren( r);
44187                 var cret = _this.recordToStack(cstore, prop, value, stack);
44188                 if (cret !== false) {
44189                     record = cret;
44190                     srec = r;
44191                     return false;
44192                 }
44193             }
44194              
44195             return true;
44196         });
44197         if (record == false) {
44198             return false
44199         }
44200         stack.unshift(srec);
44201         return record;
44202     },
44203     
44204     /*
44205      * find the stack of stores that match our value.
44206      *
44207      * 
44208      */
44209     
44210     selectActive : function ()
44211     {
44212         // if store is not loaded, then we will need to wait for that to happen first.
44213         var stack = [];
44214         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44215         for (var i = 0; i < stack.length; i++ ) {
44216             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44217         }
44218         
44219     }
44220         
44221          
44222     
44223     
44224     
44225     
44226 });/*
44227  * Based on:
44228  * Ext JS Library 1.1.1
44229  * Copyright(c) 2006-2007, Ext JS, LLC.
44230  *
44231  * Originally Released Under LGPL - original licence link has changed is not relivant.
44232  *
44233  * Fork - LGPL
44234  * <script type="text/javascript">
44235  */
44236 /**
44237  * @class Roo.form.Checkbox
44238  * @extends Roo.form.Field
44239  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44240  * @constructor
44241  * Creates a new Checkbox
44242  * @param {Object} config Configuration options
44243  */
44244 Roo.form.Checkbox = function(config){
44245     Roo.form.Checkbox.superclass.constructor.call(this, config);
44246     this.addEvents({
44247         /**
44248          * @event check
44249          * Fires when the checkbox is checked or unchecked.
44250              * @param {Roo.form.Checkbox} this This checkbox
44251              * @param {Boolean} checked The new checked value
44252              */
44253         check : true
44254     });
44255 };
44256
44257 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44258     /**
44259      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44260      */
44261     focusClass : undefined,
44262     /**
44263      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44264      */
44265     fieldClass: "x-form-field",
44266     /**
44267      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44268      */
44269     checked: false,
44270     /**
44271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44272      * {tag: "input", type: "checkbox", autocomplete: "off"})
44273      */
44274     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44275     /**
44276      * @cfg {String} boxLabel The text that appears beside the checkbox
44277      */
44278     boxLabel : "",
44279     /**
44280      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44281      */  
44282     inputValue : '1',
44283     /**
44284      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44285      */
44286      valueOff: '0', // value when not checked..
44287
44288     actionMode : 'viewEl', 
44289     //
44290     // private
44291     itemCls : 'x-menu-check-item x-form-item',
44292     groupClass : 'x-menu-group-item',
44293     inputType : 'hidden',
44294     
44295     
44296     inSetChecked: false, // check that we are not calling self...
44297     
44298     inputElement: false, // real input element?
44299     basedOn: false, // ????
44300     
44301     isFormField: true, // not sure where this is needed!!!!
44302
44303     onResize : function(){
44304         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44305         if(!this.boxLabel){
44306             this.el.alignTo(this.wrap, 'c-c');
44307         }
44308     },
44309
44310     initEvents : function(){
44311         Roo.form.Checkbox.superclass.initEvents.call(this);
44312         this.el.on("click", this.onClick,  this);
44313         this.el.on("change", this.onClick,  this);
44314     },
44315
44316
44317     getResizeEl : function(){
44318         return this.wrap;
44319     },
44320
44321     getPositionEl : function(){
44322         return this.wrap;
44323     },
44324
44325     // private
44326     onRender : function(ct, position){
44327         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44328         /*
44329         if(this.inputValue !== undefined){
44330             this.el.dom.value = this.inputValue;
44331         }
44332         */
44333         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44334         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44335         var viewEl = this.wrap.createChild({ 
44336             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44337         this.viewEl = viewEl;   
44338         this.wrap.on('click', this.onClick,  this); 
44339         
44340         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44341         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44342         
44343         
44344         
44345         if(this.boxLabel){
44346             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44347         //    viewEl.on('click', this.onClick,  this); 
44348         }
44349         //if(this.checked){
44350             this.setChecked(this.checked);
44351         //}else{
44352             //this.checked = this.el.dom;
44353         //}
44354
44355     },
44356
44357     // private
44358     initValue : Roo.emptyFn,
44359
44360     /**
44361      * Returns the checked state of the checkbox.
44362      * @return {Boolean} True if checked, else false
44363      */
44364     getValue : function(){
44365         if(this.el){
44366             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44367         }
44368         return this.valueOff;
44369         
44370     },
44371
44372         // private
44373     onClick : function(){ 
44374         if (this.disabled) {
44375             return;
44376         }
44377         this.setChecked(!this.checked);
44378
44379         //if(this.el.dom.checked != this.checked){
44380         //    this.setValue(this.el.dom.checked);
44381        // }
44382     },
44383
44384     /**
44385      * Sets the checked state of the checkbox.
44386      * On is always based on a string comparison between inputValue and the param.
44387      * @param {Boolean/String} value - the value to set 
44388      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44389      */
44390     setValue : function(v,suppressEvent){
44391         
44392         
44393         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44394         //if(this.el && this.el.dom){
44395         //    this.el.dom.checked = this.checked;
44396         //    this.el.dom.defaultChecked = this.checked;
44397         //}
44398         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44399         //this.fireEvent("check", this, this.checked);
44400     },
44401     // private..
44402     setChecked : function(state,suppressEvent)
44403     {
44404         if (this.inSetChecked) {
44405             this.checked = state;
44406             return;
44407         }
44408         
44409     
44410         if(this.wrap){
44411             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44412         }
44413         this.checked = state;
44414         if(suppressEvent !== true){
44415             this.fireEvent('check', this, state);
44416         }
44417         this.inSetChecked = true;
44418         this.el.dom.value = state ? this.inputValue : this.valueOff;
44419         this.inSetChecked = false;
44420         
44421     },
44422     // handle setting of hidden value by some other method!!?!?
44423     setFromHidden: function()
44424     {
44425         if(!this.el){
44426             return;
44427         }
44428         //console.log("SET FROM HIDDEN");
44429         //alert('setFrom hidden');
44430         this.setValue(this.el.dom.value);
44431     },
44432     
44433     onDestroy : function()
44434     {
44435         if(this.viewEl){
44436             Roo.get(this.viewEl).remove();
44437         }
44438          
44439         Roo.form.Checkbox.superclass.onDestroy.call(this);
44440     },
44441     
44442     setBoxLabel : function(str)
44443     {
44444         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44445     }
44446
44447 });/*
44448  * Based on:
44449  * Ext JS Library 1.1.1
44450  * Copyright(c) 2006-2007, Ext JS, LLC.
44451  *
44452  * Originally Released Under LGPL - original licence link has changed is not relivant.
44453  *
44454  * Fork - LGPL
44455  * <script type="text/javascript">
44456  */
44457  
44458 /**
44459  * @class Roo.form.Radio
44460  * @extends Roo.form.Checkbox
44461  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44462  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44463  * @constructor
44464  * Creates a new Radio
44465  * @param {Object} config Configuration options
44466  */
44467 Roo.form.Radio = function(){
44468     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44469 };
44470 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44471     inputType: 'radio',
44472
44473     /**
44474      * If this radio is part of a group, it will return the selected value
44475      * @return {String}
44476      */
44477     getGroupValue : function(){
44478         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44479     },
44480     
44481     
44482     onRender : function(ct, position){
44483         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44484         
44485         if(this.inputValue !== undefined){
44486             this.el.dom.value = this.inputValue;
44487         }
44488          
44489         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44490         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44491         //var viewEl = this.wrap.createChild({ 
44492         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44493         //this.viewEl = viewEl;   
44494         //this.wrap.on('click', this.onClick,  this); 
44495         
44496         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44497         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44498         
44499         
44500         
44501         if(this.boxLabel){
44502             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44503         //    viewEl.on('click', this.onClick,  this); 
44504         }
44505          if(this.checked){
44506             this.el.dom.checked =   'checked' ;
44507         }
44508          
44509     } 
44510     
44511     
44512 });Roo.htmleditor = {}; 
44513 /**
44514  * @class Roo.htmleditor.Filter
44515  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44516  * @cfg {DomElement} node The node to iterate and filter
44517  * @cfg {boolean|String|Array} tag Tags to replace 
44518  * @constructor
44519  * Create a new Filter.
44520  * @param {Object} config Configuration options
44521  */
44522
44523
44524
44525 Roo.htmleditor.Filter = function(cfg) {
44526     Roo.apply(this.cfg);
44527     // this does not actually call walk as it's really just a abstract class
44528 }
44529
44530
44531 Roo.htmleditor.Filter.prototype = {
44532     
44533     node: false,
44534     
44535     tag: false,
44536
44537     // overrride to do replace comments.
44538     replaceComment : false,
44539     
44540     // overrride to do replace or do stuff with tags..
44541     replaceTag : false,
44542     
44543     walk : function(dom)
44544     {
44545         Roo.each( Array.from(dom.childNodes), function( e ) {
44546             switch(true) {
44547                 
44548                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44549                     this.replaceComment(e);
44550                     return;
44551                 
44552                 case e.nodeType != 1: //not a node.
44553                     return;
44554                 
44555                 case this.tag === true: // everything
44556                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
44557                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
44558                     if (this.replaceTag && false === this.replaceTag(e)) {
44559                         return;
44560                     }
44561                     if (e.hasChildNodes()) {
44562                         this.walk(e);
44563                     }
44564                     return;
44565                 
44566                 default:    // tags .. that do not match.
44567                     if (e.hasChildNodes()) {
44568                         this.walk(e);
44569                     }
44570             }
44571             
44572         }, this);
44573         
44574     }
44575 }; 
44576
44577 /**
44578  * @class Roo.htmleditor.FilterAttributes
44579  * clean attributes and  styles including http:// etc.. in attribute
44580  * @constructor
44581 * Run a new Attribute Filter
44582 * @param {Object} config Configuration options
44583  */
44584 Roo.htmleditor.FilterAttributes = function(cfg)
44585 {
44586     Roo.apply(this, cfg);
44587     this.attrib_black = this.attrib_black || [];
44588     this.attrib_white = this.attrib_white || [];
44589
44590     this.attrib_clean = this.attrib_clean || [];
44591     this.style_white = this.style_white || [];
44592     this.style_black = this.style_black || [];
44593     this.walk(cfg.node);
44594 }
44595
44596 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44597 {
44598     tag: true, // all tags
44599     
44600     attrib_black : false, // array
44601     attrib_clean : false,
44602     attrib_white : false,
44603
44604     style_white : false,
44605     style_black : false,
44606      
44607      
44608     replaceTag : function(node)
44609     {
44610         if (!node.attributes || !node.attributes.length) {
44611             return true;
44612         }
44613         
44614         for (var i = node.attributes.length-1; i > -1 ; i--) {
44615             var a = node.attributes[i];
44616             //console.log(a);
44617             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
44618                 node.removeAttribute(a.name);
44619                 continue;
44620             }
44621             
44622             
44623             
44624             if (a.name.toLowerCase().substr(0,2)=='on')  {
44625                 node.removeAttribute(a.name);
44626                 continue;
44627             }
44628             
44629             
44630             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44631                 node.removeAttribute(a.name);
44632                 continue;
44633             }
44634             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44635                 this.cleanAttr(node,a.name,a.value); // fixme..
44636                 continue;
44637             }
44638             if (a.name == 'style') {
44639                 this.cleanStyle(node,a.name,a.value);
44640                 continue;
44641             }
44642             /// clean up MS crap..
44643             // tecnically this should be a list of valid class'es..
44644             
44645             
44646             if (a.name == 'class') {
44647                 if (a.value.match(/^Mso/)) {
44648                     node.removeAttribute('class');
44649                 }
44650                 
44651                 if (a.value.match(/^body$/)) {
44652                     node.removeAttribute('class');
44653                 }
44654                 continue;
44655             }
44656             
44657             
44658             // style cleanup!?
44659             // class cleanup?
44660             
44661         }
44662         return true; // clean children
44663     },
44664         
44665     cleanAttr: function(node, n,v)
44666     {
44667         
44668         if (v.match(/^\./) || v.match(/^\//)) {
44669             return;
44670         }
44671         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44672             return;
44673         }
44674         if (v.match(/^#/)) {
44675             return;
44676         }
44677         if (v.match(/^\{/)) { // allow template editing.
44678             return;
44679         }
44680 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44681         node.removeAttribute(n);
44682         
44683     },
44684     cleanStyle : function(node,  n,v)
44685     {
44686         if (v.match(/expression/)) { //XSS?? should we even bother..
44687             node.removeAttribute(n);
44688             return;
44689         }
44690         
44691         var parts = v.split(/;/);
44692         var clean = [];
44693         
44694         Roo.each(parts, function(p) {
44695             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44696             if (!p.length) {
44697                 return true;
44698             }
44699             var l = p.split(':').shift().replace(/\s+/g,'');
44700             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44701             
44702             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44703                 return true;
44704             }
44705             //Roo.log()
44706             // only allow 'c whitelisted system attributes'
44707             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44708                 return true;
44709             }
44710             
44711             
44712             clean.push(p);
44713             return true;
44714         },this);
44715         if (clean.length) { 
44716             node.setAttribute(n, clean.join(';'));
44717         } else {
44718             node.removeAttribute(n);
44719         }
44720         
44721     }
44722         
44723         
44724         
44725     
44726 });/**
44727  * @class Roo.htmleditor.FilterBlack
44728  * remove blacklisted elements.
44729  * @constructor
44730  * Run a new Blacklisted Filter
44731  * @param {Object} config Configuration options
44732  */
44733
44734 Roo.htmleditor.FilterBlack = function(cfg)
44735 {
44736     Roo.apply(this, cfg);
44737     this.walk(cfg.node);
44738 }
44739
44740 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44741 {
44742     tag : true, // all elements.
44743    
44744     replace : function(n)
44745     {
44746         n.parentNode.removeChild(n);
44747     }
44748 });
44749 /**
44750  * @class Roo.htmleditor.FilterComment
44751  * remove comments.
44752  * @constructor
44753 * Run a new Comments Filter
44754 * @param {Object} config Configuration options
44755  */
44756 Roo.htmleditor.FilterComment = function(cfg)
44757 {
44758     this.walk(cfg.node);
44759 }
44760
44761 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44762 {
44763   
44764     replaceComment : function(n)
44765     {
44766         n.parentNode.removeChild(n);
44767     }
44768 });/**
44769  * @class Roo.htmleditor.FilterKeepChildren
44770  * remove tags but keep children
44771  * @constructor
44772  * Run a new Keep Children Filter
44773  * @param {Object} config Configuration options
44774  */
44775
44776 Roo.htmleditor.FilterKeepChildren = function(cfg)
44777 {
44778     Roo.apply(this, cfg);
44779     if (this.tag === false) {
44780         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
44781     }
44782     this.walk(cfg.node);
44783 }
44784
44785 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44786 {
44787     
44788   
44789     replaceTag : function(node)
44790     {
44791         // walk children...
44792         Roo.log(node);
44793         var ar = Array.from(node.childNodes);
44794         //remove first..
44795         for (var i = 0; i < ar.length; i++) {
44796             if (ar[i].nodeType == 1) {
44797                 if (
44798                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
44799                     || // array and it matches
44800                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
44801                 ) {
44802                     this.replaceTag(ar[i]); // child is blacklisted as well...
44803                     continue;
44804                 }
44805             }
44806         }  
44807         ar = Array.from(node.childNodes);
44808         for (var i = 0; i < ar.length; i++) {
44809          
44810             node.removeChild(ar[i]);
44811             // what if we need to walk these???
44812             node.parentNode.insertBefore(ar[i], node);
44813             if (this.tag !== false) {
44814                 this.walk(ar[i]);
44815                 
44816             }
44817         }
44818         node.parentNode.removeChild(node);
44819         return false; // don't walk children
44820         
44821         
44822     }
44823 });/**
44824  * @class Roo.htmleditor.FilterParagraph
44825  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44826  * like on 'push' to remove the <p> tags and replace them with line breaks.
44827  * @constructor
44828  * Run a new Paragraph Filter
44829  * @param {Object} config Configuration options
44830  */
44831
44832 Roo.htmleditor.FilterParagraph = function(cfg)
44833 {
44834     // no need to apply config.
44835     this.walk(cfg.node);
44836 }
44837
44838 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44839 {
44840     
44841      
44842     tag : 'P',
44843     
44844      
44845     replaceTag : function(node)
44846     {
44847         
44848         if (node.childNodes.length == 1 &&
44849             node.childNodes[0].nodeType == 3 &&
44850             node.childNodes[0].textContent.trim().length < 1
44851             ) {
44852             // remove and replace with '<BR>';
44853             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
44854             return false; // no need to walk..
44855         }
44856         var ar = Array.from(node.childNodes);
44857         for (var i = 0; i < ar.length; i++) {
44858             node.removeChild(ar[i]);
44859             // what if we need to walk these???
44860             node.parentNode.insertBefore(ar[i], node);
44861         }
44862         // now what about this?
44863         // <p> &nbsp; </p>
44864         
44865         // double BR.
44866         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
44867         node.parentNode.removeChild(node);
44868         
44869         return false;
44870
44871     }
44872     
44873 });/**
44874  * @class Roo.htmleditor.FilterSpan
44875  * filter span's with no attributes out..
44876  * @constructor
44877  * Run a new Span Filter
44878  * @param {Object} config Configuration options
44879  */
44880
44881 Roo.htmleditor.FilterSpan = function(cfg)
44882 {
44883     // no need to apply config.
44884     this.walk(cfg.node);
44885 }
44886
44887 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
44888 {
44889      
44890     tag : 'SPAN',
44891      
44892  
44893     replaceTag : function(node)
44894     {
44895         if (node.attributes && node.attributes.length > 0) {
44896             return true; // walk if there are any.
44897         }
44898         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
44899         return false;
44900      
44901     }
44902     
44903 });/**
44904  * @class Roo.htmleditor.FilterTableWidth
44905   try and remove table width data - as that frequently messes up other stuff.
44906  * 
44907  *      was cleanTableWidths.
44908  *
44909  * Quite often pasting from word etc.. results in tables with column and widths.
44910  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44911  *
44912  * @constructor
44913  * Run a new Table Filter
44914  * @param {Object} config Configuration options
44915  */
44916
44917 Roo.htmleditor.FilterTableWidth = function(cfg)
44918 {
44919     // no need to apply config.
44920     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44921     this.walk(cfg.node);
44922 }
44923
44924 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44925 {
44926      
44927      
44928     
44929     replaceTag: function(node) {
44930         
44931         
44932       
44933         if (node.hasAttribute('width')) {
44934             node.removeAttribute('width');
44935         }
44936         
44937          
44938         if (node.hasAttribute("style")) {
44939             // pretty basic...
44940             
44941             var styles = node.getAttribute("style").split(";");
44942             var nstyle = [];
44943             Roo.each(styles, function(s) {
44944                 if (!s.match(/:/)) {
44945                     return;
44946                 }
44947                 var kv = s.split(":");
44948                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44949                     return;
44950                 }
44951                 // what ever is left... we allow.
44952                 nstyle.push(s);
44953             });
44954             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44955             if (!nstyle.length) {
44956                 node.removeAttribute('style');
44957             }
44958         }
44959         
44960         return true; // continue doing children..
44961     }
44962 });/**
44963  * @class Roo.htmleditor.FilterWord
44964  * try and clean up all the mess that Word generates.
44965  * 
44966  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44967  
44968  * @constructor
44969  * Run a new Span Filter
44970  * @param {Object} config Configuration options
44971  */
44972
44973 Roo.htmleditor.FilterWord = function(cfg)
44974 {
44975     // no need to apply config.
44976     this.walk(cfg.node);
44977 }
44978
44979 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44980 {
44981     tag: true,
44982      
44983     
44984     /**
44985      * Clean up MS wordisms...
44986      */
44987     replaceTag : function(node)
44988     {
44989          
44990         // no idea what this does - span with text, replaceds with just text.
44991         if(
44992                 node.nodeName == 'SPAN' &&
44993                 !node.hasAttributes() &&
44994                 node.childNodes.length == 1 &&
44995                 node.firstChild.nodeName == "#text"  
44996         ) {
44997             var textNode = node.firstChild;
44998             node.removeChild(textNode);
44999             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45000                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45001             }
45002             node.parentNode.insertBefore(textNode, node);
45003             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45004                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45005             }
45006             
45007             node.parentNode.removeChild(node);
45008             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45009         }
45010         
45011    
45012         
45013         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45014             node.parentNode.removeChild(node);
45015             return false; // dont do chidlren
45016         }
45017         //Roo.log(node.tagName);
45018         // remove - but keep children..
45019         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45020             //Roo.log('-- removed');
45021             while (node.childNodes.length) {
45022                 var cn = node.childNodes[0];
45023                 node.removeChild(cn);
45024                 node.parentNode.insertBefore(cn, node);
45025                 // move node to parent - and clean it..
45026                 this.replaceTag(cn);
45027             }
45028             node.parentNode.removeChild(node);
45029             /// no need to iterate chidlren = it's got none..
45030             //this.iterateChildren(node, this.cleanWord);
45031             return false; // no need to iterate children.
45032         }
45033         // clean styles
45034         if (node.className.length) {
45035             
45036             var cn = node.className.split(/\W+/);
45037             var cna = [];
45038             Roo.each(cn, function(cls) {
45039                 if (cls.match(/Mso[a-zA-Z]+/)) {
45040                     return;
45041                 }
45042                 cna.push(cls);
45043             });
45044             node.className = cna.length ? cna.join(' ') : '';
45045             if (!cna.length) {
45046                 node.removeAttribute("class");
45047             }
45048         }
45049         
45050         if (node.hasAttribute("lang")) {
45051             node.removeAttribute("lang");
45052         }
45053         
45054         if (node.hasAttribute("style")) {
45055             
45056             var styles = node.getAttribute("style").split(";");
45057             var nstyle = [];
45058             Roo.each(styles, function(s) {
45059                 if (!s.match(/:/)) {
45060                     return;
45061                 }
45062                 var kv = s.split(":");
45063                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45064                     return;
45065                 }
45066                 // what ever is left... we allow.
45067                 nstyle.push(s);
45068             });
45069             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45070             if (!nstyle.length) {
45071                 node.removeAttribute('style');
45072             }
45073         }
45074         return true; // do children
45075         
45076         
45077         
45078     }
45079 });
45080 /**
45081  * @class Roo.htmleditor.FilterStyleToTag
45082  * part of the word stuff... - certain 'styles' should be converted to tags.
45083  * eg.
45084  *   font-weight: bold -> bold
45085  *   ?? super / subscrit etc..
45086  * 
45087  * @constructor
45088 * Run a new style to tag filter.
45089 * @param {Object} config Configuration options
45090  */
45091 Roo.htmleditor.FilterStyleToTag = function(cfg)
45092 {
45093     
45094     this.tags = {
45095         B  : [ 'fontWeight' , 'bold'],
45096         I :  [ 'fontStyle' , 'italic'],
45097         //pre :  [ 'font-style' , 'italic'],
45098         // h1.. h6 ?? font-size?
45099         SUP : [ 'verticalAlign' , 'super' ],
45100         SUB : [ 'verticalAlign' , 'sub' ]
45101         
45102         
45103     };
45104     
45105     Roo.apply(this, cfg);
45106      
45107     
45108     this.walk(cfg.node);
45109     
45110     
45111     
45112 }
45113
45114
45115 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45116 {
45117     tag: true, // all tags
45118     
45119     tags : false,
45120     
45121     
45122     replaceTag : function(node)
45123     {
45124         
45125         
45126         if (node.getAttribute("style") === null) {
45127             return true;
45128         }
45129         var inject = [];
45130         for (var k in this.tags) {
45131             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45132                 inject.push(k);
45133                 node.style.removeProperty(this.tags[k][0]);
45134             }
45135         }
45136         if (!inject.length) {
45137             return true; 
45138         }
45139         var cn = Array.from(node.childNodes);
45140         var nn = node;
45141         Roo.each(inject, function(t) {
45142             var nc = node.ownerDocument.createelement(t);
45143             nn.appendChild(nc);
45144             nn = nc;
45145         });
45146         for(var i = 0;i < cn.length;cn++) {
45147             node.removeChild(cn[i]);
45148             nn.appendChild(cn[i]);
45149         }
45150         return true /// iterate thru
45151     }
45152     
45153 })/**
45154  * @class Roo.htmleditor.FilterLongBr
45155  * BR/BR/BR - keep a maximum of 2...
45156  * @constructor
45157  * Run a new Long BR Filter
45158  * @param {Object} config Configuration options
45159  */
45160
45161 Roo.htmleditor.FilterLongBr = function(cfg)
45162 {
45163     // no need to apply config.
45164     this.walk(cfg.node);
45165 }
45166
45167 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45168 {
45169     
45170      
45171     tag : 'BR',
45172     
45173      
45174     replaceTag : function(node)
45175     {
45176         
45177         var ps = node.nextSibling;
45178         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45179             ps = ps.nextSibling;
45180         }
45181         
45182         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45183             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45184             return false;
45185         }
45186         
45187         if (!ps || ps.nodeType != 1) {
45188             return false;
45189         }
45190         
45191         if (!ps || ps.tagName != 'BR') {
45192            
45193             return false;
45194         }
45195         
45196         
45197         
45198         
45199         
45200         if (!node.previousSibling) {
45201             return false;
45202         }
45203         var ps = node.previousSibling;
45204         
45205         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45206             ps = ps.previousSibling;
45207         }
45208         if (!ps || ps.nodeType != 1) {
45209             return false;
45210         }
45211         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45212         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45213             return false;
45214         }
45215         
45216         node.parentNode.removeChild(node); // remove me...
45217         
45218         return false; // no need to do children
45219
45220     }
45221     
45222 });
45223 /**
45224  * @class Roo.htmleditor.Tidy
45225  * Tidy HTML 
45226  * @cfg {Roo.HtmlEditorCore} core the editor.
45227  * @constructor
45228  * Create a new Filter.
45229  * @param {Object} config Configuration options
45230  */
45231
45232
45233 Roo.htmleditor.Tidy = function(cfg) {
45234     Roo.apply(this, cfg);
45235     
45236     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45237      
45238 }
45239
45240 Roo.htmleditor.Tidy.toString = function(node)
45241 {
45242     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45243 }
45244
45245 Roo.htmleditor.Tidy.prototype = {
45246     
45247     
45248     wrap : function(s) {
45249         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45250     },
45251
45252     
45253     tidy : function(node, indent) {
45254      
45255         if  (node.nodeType == 3) {
45256             // text.
45257             
45258             
45259             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45260                 
45261             
45262         }
45263         
45264         if  (node.nodeType != 1) {
45265             return '';
45266         }
45267         
45268         
45269         
45270         if (node.tagName == 'BODY') {
45271             
45272             return this.cn(node, '');
45273         }
45274              
45275              // Prints the node tagName, such as <A>, <IMG>, etc
45276         var ret = "<" + node.tagName +  this.attr(node) ;
45277         
45278         // elements with no children..
45279         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45280                 return ret + '/>';
45281         }
45282         ret += '>';
45283         
45284         
45285         var cindent = indent === false ? '' : (indent + '  ');
45286         // tags where we will not pad the children.. (inline text tags etc..)
45287         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45288             cindent = false;
45289             
45290             
45291         }
45292         
45293         var cn = this.cn(node, cindent );
45294         
45295         return ret + cn  + '</' + node.tagName + '>';
45296         
45297     },
45298     cn: function(node, indent)
45299     {
45300         var ret = [];
45301         
45302         var ar = Array.from(node.childNodes);
45303         for (var i = 0 ; i < ar.length ; i++) {
45304             
45305             
45306             
45307             if (indent !== false   // indent==false preservies everything
45308                 && i > 0
45309                 && ar[i].nodeType == 3 
45310                 && ar[i].nodeValue.length > 0
45311                 && ar[i].nodeValue.match(/^\s+/)
45312             ) {
45313                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45314                     ret.pop(); // remove line break from last?
45315                 }
45316                 
45317                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45318             }
45319             if (indent !== false
45320                 && ar[i].nodeType == 1 // element - and indent is not set... 
45321             ) {
45322                 ret.push("\n" + indent); 
45323             }
45324             
45325             ret.push(this.tidy(ar[i], indent));
45326             // text + trailing indent 
45327             if (indent !== false
45328                 && ar[i].nodeType == 3
45329                 && ar[i].nodeValue.length > 0
45330                 && ar[i].nodeValue.match(/\s+$/)
45331             ){
45332                 ret.push("\n" + indent); 
45333             }
45334             
45335             
45336             
45337             
45338         }
45339         // what if all text?
45340         
45341         
45342         return ret.join('');
45343     },
45344     
45345          
45346         
45347     attr : function(node)
45348     {
45349         var attr = [];
45350         for(i = 0; i < node.attributes.length;i++) {
45351             
45352             // skip empty values?
45353             if (!node.attributes.item(i).value.length) {
45354                 continue;
45355             }
45356             attr.push(  node.attributes.item(i).name + '="' +
45357                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45358             );
45359         }
45360         return attr.length ? (' ' + attr.join(' ') ) : '';
45361         
45362     }
45363     
45364     
45365     
45366 }
45367 /**
45368  * @class Roo.htmleditor.KeyEnter
45369  * Handle Enter press..
45370  * @cfg {Roo.HtmlEditorCore} core the editor.
45371  * @constructor
45372  * Create a new Filter.
45373  * @param {Object} config Configuration options
45374  */
45375
45376
45377
45378 Roo.htmleditor.KeyEnter = function(cfg) {
45379     Roo.apply(this, cfg);
45380     // this does not actually call walk as it's really just a abstract class
45381  
45382     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45383 }
45384
45385
45386 Roo.htmleditor.KeyEnter.prototype = {
45387     
45388     core : false,
45389     
45390     keypress : function(e) {
45391         if (e.charCode != 13) {
45392             return true;
45393         }
45394         e.preventDefault();
45395         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45396         var doc = this.core.doc;
45397         
45398         var docFragment = doc.createDocumentFragment();
45399     
45400         //add a new line
45401         var newEle = doc.createTextNode('\n');
45402         docFragment.appendChild(newEle);
45403     
45404     
45405         var range = this.core.win.getSelection().getRangeAt(0);
45406         var n = range.commonAncestorContainer ;
45407         while (n && n.nodeType != 1) {
45408             n  = n.parentNode;
45409         }
45410         var li = false;
45411         if (n && n.tagName == 'UL') {
45412             li = doc.createElement('LI');
45413             n.appendChild(li);
45414             
45415         }
45416         if (n && n.tagName == 'LI') {
45417             li = doc.createElement('LI');
45418             if (n.nextSibling) {
45419                 n.parentNode.insertBefore(li, n.firstSibling);
45420                 
45421             } else {
45422                 n.parentNode.appendChild(li);
45423             }
45424         }
45425         if (li) {   
45426             range = doc.createRange();
45427             range.setStartAfter(li);
45428             range.collapse(true);
45429         
45430             //make the cursor there
45431             var sel = this.core.win.getSelection();
45432             sel.removeAllRanges();
45433             sel.addRange(range);
45434             return false;
45435             
45436             
45437         }
45438         //add the br, or p, or something else
45439         newEle = doc.createElement('br');
45440         docFragment.appendChild(newEle);
45441     
45442         //make the br replace selection
45443         
45444         range.deleteContents();
45445         
45446         range.insertNode(docFragment);
45447     
45448         //create a new range
45449         range = doc.createRange();
45450         range.setStartAfter(newEle);
45451         range.collapse(true);
45452     
45453         //make the cursor there
45454         var sel = this.core.win.getSelection();
45455         sel.removeAllRanges();
45456         sel.addRange(range);
45457     
45458         return false;
45459          
45460     }
45461 };
45462      
45463 /**
45464  * @class Roo.htmleditor.Block
45465  * Base class for html editor blocks - do not use it directly .. extend it..
45466  * @cfg {DomElement} node The node to apply stuff to.
45467  * @cfg {String} friendly_name the name that appears in the context bar about this block
45468  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
45469  
45470  * @constructor
45471  * Create a new Filter.
45472  * @param {Object} config Configuration options
45473  */
45474
45475 Roo.htmleditor.Block  = function(cfg)
45476 {
45477     // do nothing .. should not be called really.
45478 }
45479
45480 Roo.htmleditor.Block.factory = function(node)
45481 {
45482     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
45483     if (typeof(cls) == 'undefined') {
45484         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
45485         return false;
45486     }
45487     return new cls({ node: node });  /// should trigger update element
45488 }
45489
45490
45491 Roo.htmleditor.Block.prototype = {
45492     
45493      // used by context menu
45494     friendly_name : 'Image with caption',
45495     
45496     context : false,
45497     /**
45498      * Update a node with values from this object
45499      * @param {DomElement} node
45500      */
45501     updateElement : function(node)
45502     {
45503         Roo.DomHelper.update(node, this.toObject());
45504     },
45505      /**
45506      * convert to plain HTML for calling insertAtCursor..
45507      */
45508     toHTML : function()
45509     {
45510         return Roo.DomHelper.markup(this.toObject());
45511     },
45512     /**
45513      * used by readEleemnt to extract data from a node
45514      * may need improving as it's pretty basic
45515      
45516      * @param {DomElement} node
45517      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
45518      * @param {String} attribute (use html - for contents, or style for using next param as style)
45519      * @param {String} style the style property - eg. text-align
45520      */
45521     getVal : function(node, tag, attr, style)
45522     {
45523         var n = node;
45524         if (n.tagName != tag.toUpperCase()) {
45525             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
45526             // but kiss for now.
45527             n = node.getElementsByTagName(tag).item(0);
45528         }
45529         if (attr == 'html') {
45530             return n.innerHTML;
45531         }
45532         if (attr == 'style') {
45533             return Roo.get(n).getStyle(style);
45534         }
45535         
45536         return Roo.get(n).attr(attr);
45537             
45538     },
45539     /**
45540      * create a DomHelper friendly object - for use with 
45541      * Roo.DomHelper.markup / overwrite / etc..
45542      * (override this)
45543      */
45544     toObject : function()
45545     {
45546         return {};
45547     },
45548       /**
45549      * Read a node that has a 'data-block' property - and extract the values from it.
45550      * @param {DomElement} node - the node
45551      */
45552     readElement : function(node)
45553     {
45554         
45555     } 
45556     
45557     
45558 }
45559
45560  
45561
45562 /**
45563  * @class Roo.htmleditor.BlockFigure
45564  * Block that has an image and a figcaption
45565  * @cfg {String} image_src the url for the image
45566  * @cfg {String} align (left|right) alignment for the block default left
45567  * @cfg {String} text_align (left|right) alignment for the text caption default left.
45568  * @cfg {String} caption the text to appear below  (and in the alt tag)
45569  * @cfg {String|number} image_width the width of the image number or %?
45570  * @cfg {String|number} image_height the height of the image number or %?
45571  * 
45572  * @constructor
45573  * Create a new Filter.
45574  * @param {Object} config Configuration options
45575  */
45576
45577 Roo.htmleditor.BlockFigure = function(cfg)
45578 {
45579     if (cfg.node) {
45580         this.readElement(cfg.node);
45581         this.updateElement(cfg.node);
45582     }
45583     Roo.apply(this, cfg);
45584 }
45585 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
45586  
45587     
45588     // setable values.
45589     image_src: '',
45590     
45591     align: 'left',
45592     caption : '',
45593     text_align: 'left',
45594     
45595     width : '46%',
45596     margin: '2%',
45597     
45598     // used by context menu
45599     friendly_name : 'Image with caption',
45600     
45601     context : { // ?? static really
45602         width : {
45603             title: "Width",
45604             width: 40
45605             // ?? number
45606         },
45607         margin : {
45608             title: "Margin",
45609             width: 40
45610             // ?? number
45611         },
45612         align: {
45613             title: "Align",
45614             opts : [[ "left"],[ "right"]],
45615             width : 80
45616             
45617         },
45618         text_align: {
45619             title: "Caption Align",
45620             opts : [ [ "left"],[ "right"],[ "center"]],
45621             width : 80
45622         },
45623         
45624        
45625         image_src : {
45626             title: "Src",
45627             width: 220
45628         }
45629     },
45630     /**
45631      * create a DomHelper friendly object - for use with
45632      * Roo.DomHelper.markup / overwrite / etc..
45633      */
45634     toObject : function()
45635     {
45636         var d = document.createElement('div');
45637         d.innerHTML = this.caption;
45638         
45639         return {
45640             tag: 'figure',
45641             'data-block' : 'Figure',
45642             contenteditable : 'false',
45643             style : {
45644                 display: 'table',
45645                 float :  this.align ,
45646                 width :  this.width,
45647                 margin:  this.margin
45648             },
45649             cn : [
45650                 {
45651                     tag : 'img',
45652                     src : this.image_src,
45653                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
45654                     style: {
45655                         width: '100%'
45656                     }
45657                 },
45658                 {
45659                     tag: 'figcaption',
45660                     contenteditable : true,
45661                     style : {
45662                         'text-align': this.text_align
45663                     },
45664                     html : this.caption
45665                     
45666                 }
45667             ]
45668         };
45669     },
45670     
45671     readElement : function(node)
45672     {
45673         this.image_src = this.getVal(node, 'img', 'src');
45674         this.align = this.getVal(node, 'figure', 'style', 'float');
45675         this.caption = this.getVal(node, 'figcaption', 'html');
45676         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
45677         this.width = this.getVal(node, 'figure', 'style', 'width');
45678         this.margin = this.getVal(node, 'figure', 'style', 'margin');
45679         
45680     } 
45681     
45682   
45683    
45684      
45685     
45686     
45687     
45688     
45689 })
45690
45691 //<script type="text/javascript">
45692
45693 /*
45694  * Based  Ext JS Library 1.1.1
45695  * Copyright(c) 2006-2007, Ext JS, LLC.
45696  * LGPL
45697  *
45698  */
45699  
45700 /**
45701  * @class Roo.HtmlEditorCore
45702  * @extends Roo.Component
45703  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45704  *
45705  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45706  */
45707
45708 Roo.HtmlEditorCore = function(config){
45709     
45710     
45711     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45712     
45713     
45714     this.addEvents({
45715         /**
45716          * @event initialize
45717          * Fires when the editor is fully initialized (including the iframe)
45718          * @param {Roo.HtmlEditorCore} this
45719          */
45720         initialize: true,
45721         /**
45722          * @event activate
45723          * Fires when the editor is first receives the focus. Any insertion must wait
45724          * until after this event.
45725          * @param {Roo.HtmlEditorCore} this
45726          */
45727         activate: true,
45728          /**
45729          * @event beforesync
45730          * Fires before the textarea is updated with content from the editor iframe. Return false
45731          * to cancel the sync.
45732          * @param {Roo.HtmlEditorCore} this
45733          * @param {String} html
45734          */
45735         beforesync: true,
45736          /**
45737          * @event beforepush
45738          * Fires before the iframe editor is updated with content from the textarea. Return false
45739          * to cancel the push.
45740          * @param {Roo.HtmlEditorCore} this
45741          * @param {String} html
45742          */
45743         beforepush: true,
45744          /**
45745          * @event sync
45746          * Fires when the textarea is updated with content from the editor iframe.
45747          * @param {Roo.HtmlEditorCore} this
45748          * @param {String} html
45749          */
45750         sync: true,
45751          /**
45752          * @event push
45753          * Fires when the iframe editor is updated with content from the textarea.
45754          * @param {Roo.HtmlEditorCore} this
45755          * @param {String} html
45756          */
45757         push: true,
45758         
45759         /**
45760          * @event editorevent
45761          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45762          * @param {Roo.HtmlEditorCore} this
45763          */
45764         editorevent: true
45765         
45766     });
45767     
45768     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45769     
45770     // defaults : white / black...
45771     this.applyBlacklists();
45772     
45773     
45774     
45775 };
45776
45777
45778 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45779
45780
45781      /**
45782      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45783      */
45784     
45785     owner : false,
45786     
45787      /**
45788      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45789      *                        Roo.resizable.
45790      */
45791     resizable : false,
45792      /**
45793      * @cfg {Number} height (in pixels)
45794      */   
45795     height: 300,
45796    /**
45797      * @cfg {Number} width (in pixels)
45798      */   
45799     width: 500,
45800     
45801     /**
45802      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45803      * 
45804      */
45805     stylesheets: false,
45806     
45807     /**
45808      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45809      */
45810     allowComments: false,
45811     // id of frame..
45812     frameId: false,
45813     
45814     // private properties
45815     validationEvent : false,
45816     deferHeight: true,
45817     initialized : false,
45818     activated : false,
45819     sourceEditMode : false,
45820     onFocus : Roo.emptyFn,
45821     iframePad:3,
45822     hideMode:'offsets',
45823     
45824     clearUp: true,
45825     
45826     // blacklist + whitelisted elements..
45827     black: false,
45828     white: false,
45829      
45830     bodyCls : '',
45831
45832     /**
45833      * Protected method that will not generally be called directly. It
45834      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45835      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45836      */
45837     getDocMarkup : function(){
45838         // body styles..
45839         var st = '';
45840         
45841         // inherit styels from page...?? 
45842         if (this.stylesheets === false) {
45843             
45844             Roo.get(document.head).select('style').each(function(node) {
45845                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45846             });
45847             
45848             Roo.get(document.head).select('link').each(function(node) { 
45849                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45850             });
45851             
45852         } else if (!this.stylesheets.length) {
45853                 // simple..
45854                 st = '<style type="text/css">' +
45855                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45856                    '</style>';
45857         } else {
45858             for (var i in this.stylesheets) {
45859                 if (typeof(this.stylesheets[i]) != 'string') {
45860                     continue;
45861                 }
45862                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45863             }
45864             
45865         }
45866         
45867         st +=  '<style type="text/css">' +
45868             'IMG { cursor: pointer } ' +
45869         '</style>';
45870
45871         var cls = 'roo-htmleditor-body';
45872         
45873         if(this.bodyCls.length){
45874             cls += ' ' + this.bodyCls;
45875         }
45876         
45877         return '<html><head>' + st  +
45878             //<style type="text/css">' +
45879             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45880             //'</style>' +
45881             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45882     },
45883
45884     // private
45885     onRender : function(ct, position)
45886     {
45887         var _t = this;
45888         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45889         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45890         
45891         
45892         this.el.dom.style.border = '0 none';
45893         this.el.dom.setAttribute('tabIndex', -1);
45894         this.el.addClass('x-hidden hide');
45895         
45896         
45897         
45898         if(Roo.isIE){ // fix IE 1px bogus margin
45899             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45900         }
45901        
45902         
45903         this.frameId = Roo.id();
45904         
45905          
45906         
45907         var iframe = this.owner.wrap.createChild({
45908             tag: 'iframe',
45909             cls: 'form-control', // bootstrap..
45910             id: this.frameId,
45911             name: this.frameId,
45912             frameBorder : 'no',
45913             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45914         }, this.el
45915         );
45916         
45917         
45918         this.iframe = iframe.dom;
45919
45920         this.assignDocWin();
45921         
45922         this.doc.designMode = 'on';
45923        
45924         this.doc.open();
45925         this.doc.write(this.getDocMarkup());
45926         this.doc.close();
45927
45928         
45929         var task = { // must defer to wait for browser to be ready
45930             run : function(){
45931                 //console.log("run task?" + this.doc.readyState);
45932                 this.assignDocWin();
45933                 if(this.doc.body || this.doc.readyState == 'complete'){
45934                     try {
45935                         this.doc.designMode="on";
45936                     } catch (e) {
45937                         return;
45938                     }
45939                     Roo.TaskMgr.stop(task);
45940                     this.initEditor.defer(10, this);
45941                 }
45942             },
45943             interval : 10,
45944             duration: 10000,
45945             scope: this
45946         };
45947         Roo.TaskMgr.start(task);
45948
45949     },
45950
45951     // private
45952     onResize : function(w, h)
45953     {
45954          Roo.log('resize: ' +w + ',' + h );
45955         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45956         if(!this.iframe){
45957             return;
45958         }
45959         if(typeof w == 'number'){
45960             
45961             this.iframe.style.width = w + 'px';
45962         }
45963         if(typeof h == 'number'){
45964             
45965             this.iframe.style.height = h + 'px';
45966             if(this.doc){
45967                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45968             }
45969         }
45970         
45971     },
45972
45973     /**
45974      * Toggles the editor between standard and source edit mode.
45975      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45976      */
45977     toggleSourceEdit : function(sourceEditMode){
45978         
45979         this.sourceEditMode = sourceEditMode === true;
45980         
45981         if(this.sourceEditMode){
45982  
45983             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
45984             
45985         }else{
45986             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
45987             //this.iframe.className = '';
45988             this.deferFocus();
45989         }
45990         //this.setSize(this.owner.wrap.getSize());
45991         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45992     },
45993
45994     
45995   
45996
45997     /**
45998      * Protected method that will not generally be called directly. If you need/want
45999      * custom HTML cleanup, this is the method you should override.
46000      * @param {String} html The HTML to be cleaned
46001      * return {String} The cleaned HTML
46002      */
46003     cleanHtml : function(html){
46004         html = String(html);
46005         if(html.length > 5){
46006             if(Roo.isSafari){ // strip safari nonsense
46007                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46008             }
46009         }
46010         if(html == '&nbsp;'){
46011             html = '';
46012         }
46013         return html;
46014     },
46015
46016     /**
46017      * HTML Editor -> Textarea
46018      * Protected method that will not generally be called directly. Syncs the contents
46019      * of the editor iframe with the textarea.
46020      */
46021     syncValue : function()
46022     {
46023         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46024         if(this.initialized){
46025             var bd = (this.doc.body || this.doc.documentElement);
46026             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46027             
46028             // not sure if this is really the place for this
46029             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46030             // this has to update attributes that get duped.. like alt and caption..
46031             
46032             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46033                  Roo.htmleditor.Block.factory(e);
46034             },this);
46035             
46036             
46037             var div = document.createElement('div');
46038             div.innerHTML = bd.innerHTML;
46039             // remove content editable. (blocks)
46040             
46041            
46042             
46043             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46044             //?? tidy?
46045             var html = div.innerHTML;
46046             if(Roo.isSafari){
46047                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46048                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46049                 if(m && m[1]){
46050                     html = '<div style="'+m[0]+'">' + html + '</div>';
46051                 }
46052             }
46053             html = this.cleanHtml(html);
46054             // fix up the special chars.. normaly like back quotes in word...
46055             // however we do not want to do this with chinese..
46056             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46057                 
46058                 var cc = match.charCodeAt();
46059
46060                 // Get the character value, handling surrogate pairs
46061                 if (match.length == 2) {
46062                     // It's a surrogate pair, calculate the Unicode code point
46063                     var high = match.charCodeAt(0) - 0xD800;
46064                     var low  = match.charCodeAt(1) - 0xDC00;
46065                     cc = (high * 0x400) + low + 0x10000;
46066                 }  else if (
46067                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46068                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46069                     (cc >= 0xf900 && cc < 0xfb00 )
46070                 ) {
46071                         return match;
46072                 }  
46073          
46074                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46075                 return "&#" + cc + ";";
46076                 
46077                 
46078             });
46079             
46080             
46081              
46082             if(this.owner.fireEvent('beforesync', this, html) !== false){
46083                 this.el.dom.value = html;
46084                 this.owner.fireEvent('sync', this, html);
46085             }
46086         }
46087     },
46088
46089     /**
46090      * TEXTAREA -> EDITABLE
46091      * Protected method that will not generally be called directly. Pushes the value of the textarea
46092      * into the iframe editor.
46093      */
46094     pushValue : function()
46095     {
46096         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46097         if(this.initialized){
46098             var v = this.el.dom.value.trim();
46099             
46100             
46101             if(this.owner.fireEvent('beforepush', this, v) !== false){
46102                 var d = (this.doc.body || this.doc.documentElement);
46103                 d.innerHTML = v;
46104                  
46105                 this.el.dom.value = d.innerHTML;
46106                 this.owner.fireEvent('push', this, v);
46107             }
46108             
46109             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46110                 
46111                 Roo.htmleditor.Block.factory(e);
46112                 
46113             },this);
46114             var lc = this.doc.body.lastChild;
46115             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46116                 // add an extra line at the end.
46117                 this.doc.body.appendChild(this.doc.createElement('br'));
46118             }
46119             
46120             
46121         }
46122     },
46123
46124     // private
46125     deferFocus : function(){
46126         this.focus.defer(10, this);
46127     },
46128
46129     // doc'ed in Field
46130     focus : function(){
46131         if(this.win && !this.sourceEditMode){
46132             this.win.focus();
46133         }else{
46134             this.el.focus();
46135         }
46136     },
46137     
46138     assignDocWin: function()
46139     {
46140         var iframe = this.iframe;
46141         
46142          if(Roo.isIE){
46143             this.doc = iframe.contentWindow.document;
46144             this.win = iframe.contentWindow;
46145         } else {
46146 //            if (!Roo.get(this.frameId)) {
46147 //                return;
46148 //            }
46149 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46150 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46151             
46152             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46153                 return;
46154             }
46155             
46156             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46157             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46158         }
46159     },
46160     
46161     // private
46162     initEditor : function(){
46163         //console.log("INIT EDITOR");
46164         this.assignDocWin();
46165         
46166         
46167         
46168         this.doc.designMode="on";
46169         this.doc.open();
46170         this.doc.write(this.getDocMarkup());
46171         this.doc.close();
46172         
46173         var dbody = (this.doc.body || this.doc.documentElement);
46174         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46175         // this copies styles from the containing element into thsi one..
46176         // not sure why we need all of this..
46177         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46178         
46179         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46180         //ss['background-attachment'] = 'fixed'; // w3c
46181         dbody.bgProperties = 'fixed'; // ie
46182         //Roo.DomHelper.applyStyles(dbody, ss);
46183         Roo.EventManager.on(this.doc, {
46184             //'mousedown': this.onEditorEvent,
46185             'mouseup': this.onEditorEvent,
46186             'dblclick': this.onEditorEvent,
46187             'click': this.onEditorEvent,
46188             'keyup': this.onEditorEvent,
46189             
46190             buffer:100,
46191             scope: this
46192         });
46193         Roo.EventManager.on(this.doc, {
46194             'paste': this.onPasteEvent,
46195             scope : this
46196         });
46197         if(Roo.isGecko){
46198             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46199         }
46200         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46201             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46202         }
46203         this.initialized = true;
46204
46205         
46206         // initialize special key events - enter
46207         new Roo.htmleditor.KeyEnter({core : this});
46208         
46209          
46210         
46211         this.owner.fireEvent('initialize', this);
46212         this.pushValue();
46213     },
46214     
46215     onPasteEvent : function(e,v)
46216     {
46217         // I think we better assume paste is going to be a dirty load of rubish from word..
46218         
46219         // even pasting into a 'email version' of this widget will have to clean up that mess.
46220         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46221         
46222         var html = cd.getData('text/html'); // clipboard event
46223         html = this.cleanWordChars(html);
46224         
46225         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46226          Roo.log(cd.getData('text/rtf'));
46227          Roo.log(cd.getData('text/richtext'));
46228         
46229         Roo.each(cd.items, function(item) {
46230             Roo.log(item);
46231         });
46232         new Roo.htmleditor.FilterStyleToTag({ node : d });
46233         new Roo.htmleditor.FilterAttributes({
46234             node : d,
46235             attrib_white : ['href', 'src', 'name'],
46236             attrib_clean : ['href', 'src', 'name'] 
46237         });
46238         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46239         // should be fonts..
46240         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46241         new Roo.htmleditor.FilterParagraph({ node : d });
46242         new Roo.htmleditor.FilterSpan({ node : d });
46243         new Roo.htmleditor.FilterLongBr({ node : d });
46244         
46245         
46246         
46247         this.insertAtCursor(d.innerHTML);
46248         
46249         e.preventDefault();
46250         return false;
46251         // default behaveiour should be our local cleanup paste? (optional?)
46252         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46253         //this.owner.fireEvent('paste', e, v);
46254     },
46255     // private
46256     onDestroy : function(){
46257         
46258         
46259         
46260         if(this.rendered){
46261             
46262             //for (var i =0; i < this.toolbars.length;i++) {
46263             //    // fixme - ask toolbars for heights?
46264             //    this.toolbars[i].onDestroy();
46265            // }
46266             
46267             //this.wrap.dom.innerHTML = '';
46268             //this.wrap.remove();
46269         }
46270     },
46271
46272     // private
46273     onFirstFocus : function(){
46274         
46275         this.assignDocWin();
46276         
46277         
46278         this.activated = true;
46279          
46280     
46281         if(Roo.isGecko){ // prevent silly gecko errors
46282             this.win.focus();
46283             var s = this.win.getSelection();
46284             if(!s.focusNode || s.focusNode.nodeType != 3){
46285                 var r = s.getRangeAt(0);
46286                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46287                 r.collapse(true);
46288                 this.deferFocus();
46289             }
46290             try{
46291                 this.execCmd('useCSS', true);
46292                 this.execCmd('styleWithCSS', false);
46293             }catch(e){}
46294         }
46295         this.owner.fireEvent('activate', this);
46296     },
46297
46298     // private
46299     adjustFont: function(btn){
46300         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46301         //if(Roo.isSafari){ // safari
46302         //    adjust *= 2;
46303        // }
46304         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46305         if(Roo.isSafari){ // safari
46306             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46307             v =  (v < 10) ? 10 : v;
46308             v =  (v > 48) ? 48 : v;
46309             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46310             
46311         }
46312         
46313         
46314         v = Math.max(1, v+adjust);
46315         
46316         this.execCmd('FontSize', v  );
46317     },
46318
46319     onEditorEvent : function(e)
46320     {
46321         this.owner.fireEvent('editorevent', this, e);
46322       //  this.updateToolbar();
46323         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46324     },
46325
46326     insertTag : function(tg)
46327     {
46328         // could be a bit smarter... -> wrap the current selected tRoo..
46329         if (tg.toLowerCase() == 'span' ||
46330             tg.toLowerCase() == 'code' ||
46331             tg.toLowerCase() == 'sup' ||
46332             tg.toLowerCase() == 'sub' 
46333             ) {
46334             
46335             range = this.createRange(this.getSelection());
46336             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46337             wrappingNode.appendChild(range.extractContents());
46338             range.insertNode(wrappingNode);
46339
46340             return;
46341             
46342             
46343             
46344         }
46345         this.execCmd("formatblock",   tg);
46346         
46347     },
46348     
46349     insertText : function(txt)
46350     {
46351         
46352         
46353         var range = this.createRange();
46354         range.deleteContents();
46355                //alert(Sender.getAttribute('label'));
46356                
46357         range.insertNode(this.doc.createTextNode(txt));
46358     } ,
46359     
46360      
46361
46362     /**
46363      * Executes a Midas editor command on the editor document and performs necessary focus and
46364      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46365      * @param {String} cmd The Midas command
46366      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46367      */
46368     relayCmd : function(cmd, value){
46369         this.win.focus();
46370         this.execCmd(cmd, value);
46371         this.owner.fireEvent('editorevent', this);
46372         //this.updateToolbar();
46373         this.owner.deferFocus();
46374     },
46375
46376     /**
46377      * Executes a Midas editor command directly on the editor document.
46378      * For visual commands, you should use {@link #relayCmd} instead.
46379      * <b>This should only be called after the editor is initialized.</b>
46380      * @param {String} cmd The Midas command
46381      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46382      */
46383     execCmd : function(cmd, value){
46384         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46385         this.syncValue();
46386     },
46387  
46388  
46389    
46390     /**
46391      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46392      * to insert tRoo.
46393      * @param {String} text | dom node.. 
46394      */
46395     insertAtCursor : function(text)
46396     {
46397         
46398         if(!this.activated){
46399             return;
46400         }
46401          
46402         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46403             this.win.focus();
46404             
46405             
46406             // from jquery ui (MIT licenced)
46407             var range, node;
46408             var win = this.win;
46409             
46410             if (win.getSelection && win.getSelection().getRangeAt) {
46411                 
46412                 // delete the existing?
46413                 
46414                 this.createRange(this.getSelection()).deleteContents();
46415                 range = win.getSelection().getRangeAt(0);
46416                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46417                 range.insertNode(node);
46418             } else if (win.document.selection && win.document.selection.createRange) {
46419                 // no firefox support
46420                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46421                 win.document.selection.createRange().pasteHTML(txt);
46422             } else {
46423                 // no firefox support
46424                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46425                 this.execCmd('InsertHTML', txt);
46426             } 
46427             
46428             this.syncValue();
46429             
46430             this.deferFocus();
46431         }
46432     },
46433  // private
46434     mozKeyPress : function(e){
46435         if(e.ctrlKey){
46436             var c = e.getCharCode(), cmd;
46437           
46438             if(c > 0){
46439                 c = String.fromCharCode(c).toLowerCase();
46440                 switch(c){
46441                     case 'b':
46442                         cmd = 'bold';
46443                         break;
46444                     case 'i':
46445                         cmd = 'italic';
46446                         break;
46447                     
46448                     case 'u':
46449                         cmd = 'underline';
46450                         break;
46451                     
46452                     //case 'v':
46453                       //  this.cleanUpPaste.defer(100, this);
46454                       //  return;
46455                         
46456                 }
46457                 if(cmd){
46458                     this.win.focus();
46459                     this.execCmd(cmd);
46460                     this.deferFocus();
46461                     e.preventDefault();
46462                 }
46463                 
46464             }
46465         }
46466     },
46467
46468     // private
46469     fixKeys : function(){ // load time branching for fastest keydown performance
46470         if(Roo.isIE){
46471             return function(e){
46472                 var k = e.getKey(), r;
46473                 if(k == e.TAB){
46474                     e.stopEvent();
46475                     r = this.doc.selection.createRange();
46476                     if(r){
46477                         r.collapse(true);
46478                         r.pasteHTML('&#160;&#160;&#160;&#160;');
46479                         this.deferFocus();
46480                     }
46481                     return;
46482                 }
46483                 
46484                 if(k == e.ENTER){
46485                     r = this.doc.selection.createRange();
46486                     if(r){
46487                         var target = r.parentElement();
46488                         if(!target || target.tagName.toLowerCase() != 'li'){
46489                             e.stopEvent();
46490                             r.pasteHTML('<br/>');
46491                             r.collapse(false);
46492                             r.select();
46493                         }
46494                     }
46495                 }
46496                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46497                 //    this.cleanUpPaste.defer(100, this);
46498                 //    return;
46499                 //}
46500                 
46501                 
46502             };
46503         }else if(Roo.isOpera){
46504             return function(e){
46505                 var k = e.getKey();
46506                 if(k == e.TAB){
46507                     e.stopEvent();
46508                     this.win.focus();
46509                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
46510                     this.deferFocus();
46511                 }
46512                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46513                 //    this.cleanUpPaste.defer(100, this);
46514                  //   return;
46515                 //}
46516                 
46517             };
46518         }else if(Roo.isSafari){
46519             return function(e){
46520                 var k = e.getKey();
46521                 
46522                 if(k == e.TAB){
46523                     e.stopEvent();
46524                     this.execCmd('InsertText','\t');
46525                     this.deferFocus();
46526                     return;
46527                 }
46528                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46529                  //   this.cleanUpPaste.defer(100, this);
46530                  //   return;
46531                // }
46532                 
46533              };
46534         }
46535     }(),
46536     
46537     getAllAncestors: function()
46538     {
46539         var p = this.getSelectedNode();
46540         var a = [];
46541         if (!p) {
46542             a.push(p); // push blank onto stack..
46543             p = this.getParentElement();
46544         }
46545         
46546         
46547         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
46548             a.push(p);
46549             p = p.parentNode;
46550         }
46551         a.push(this.doc.body);
46552         return a;
46553     },
46554     lastSel : false,
46555     lastSelNode : false,
46556     
46557     
46558     getSelection : function() 
46559     {
46560         this.assignDocWin();
46561         return Roo.isIE ? this.doc.selection : this.win.getSelection();
46562     },
46563     /**
46564      * Select a dom node
46565      * @param {DomElement} node the node to select
46566      */
46567     selectNode : function(node)
46568     {
46569         
46570             var nodeRange = node.ownerDocument.createRange();
46571             try {
46572                 nodeRange.selectNode(node);
46573             } catch (e) {
46574                 nodeRange.selectNodeContents(node);
46575             }
46576             //nodeRange.collapse(true);
46577             var s = this.win.getSelection();
46578             s.removeAllRanges();
46579             s.addRange(nodeRange);
46580     },
46581     
46582     getSelectedNode: function() 
46583     {
46584         // this may only work on Gecko!!!
46585         
46586         // should we cache this!!!!
46587         
46588         
46589         
46590          
46591         var range = this.createRange(this.getSelection()).cloneRange();
46592         
46593         if (Roo.isIE) {
46594             var parent = range.parentElement();
46595             while (true) {
46596                 var testRange = range.duplicate();
46597                 testRange.moveToElementText(parent);
46598                 if (testRange.inRange(range)) {
46599                     break;
46600                 }
46601                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
46602                     break;
46603                 }
46604                 parent = parent.parentElement;
46605             }
46606             return parent;
46607         }
46608         
46609         // is ancestor a text element.
46610         var ac =  range.commonAncestorContainer;
46611         if (ac.nodeType == 3) {
46612             ac = ac.parentNode;
46613         }
46614         
46615         var ar = ac.childNodes;
46616          
46617         var nodes = [];
46618         var other_nodes = [];
46619         var has_other_nodes = false;
46620         for (var i=0;i<ar.length;i++) {
46621             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
46622                 continue;
46623             }
46624             // fullly contained node.
46625             
46626             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
46627                 nodes.push(ar[i]);
46628                 continue;
46629             }
46630             
46631             // probably selected..
46632             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
46633                 other_nodes.push(ar[i]);
46634                 continue;
46635             }
46636             // outer..
46637             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
46638                 continue;
46639             }
46640             
46641             
46642             has_other_nodes = true;
46643         }
46644         if (!nodes.length && other_nodes.length) {
46645             nodes= other_nodes;
46646         }
46647         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
46648             return false;
46649         }
46650         
46651         return nodes[0];
46652     },
46653     createRange: function(sel)
46654     {
46655         // this has strange effects when using with 
46656         // top toolbar - not sure if it's a great idea.
46657         //this.editor.contentWindow.focus();
46658         if (typeof sel != "undefined") {
46659             try {
46660                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
46661             } catch(e) {
46662                 return this.doc.createRange();
46663             }
46664         } else {
46665             return this.doc.createRange();
46666         }
46667     },
46668     getParentElement: function()
46669     {
46670         
46671         this.assignDocWin();
46672         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
46673         
46674         var range = this.createRange(sel);
46675          
46676         try {
46677             var p = range.commonAncestorContainer;
46678             while (p.nodeType == 3) { // text node
46679                 p = p.parentNode;
46680             }
46681             return p;
46682         } catch (e) {
46683             return null;
46684         }
46685     
46686     },
46687     /***
46688      *
46689      * Range intersection.. the hard stuff...
46690      *  '-1' = before
46691      *  '0' = hits..
46692      *  '1' = after.
46693      *         [ -- selected range --- ]
46694      *   [fail]                        [fail]
46695      *
46696      *    basically..
46697      *      if end is before start or  hits it. fail.
46698      *      if start is after end or hits it fail.
46699      *
46700      *   if either hits (but other is outside. - then it's not 
46701      *   
46702      *    
46703      **/
46704     
46705     
46706     // @see http://www.thismuchiknow.co.uk/?p=64.
46707     rangeIntersectsNode : function(range, node)
46708     {
46709         var nodeRange = node.ownerDocument.createRange();
46710         try {
46711             nodeRange.selectNode(node);
46712         } catch (e) {
46713             nodeRange.selectNodeContents(node);
46714         }
46715     
46716         var rangeStartRange = range.cloneRange();
46717         rangeStartRange.collapse(true);
46718     
46719         var rangeEndRange = range.cloneRange();
46720         rangeEndRange.collapse(false);
46721     
46722         var nodeStartRange = nodeRange.cloneRange();
46723         nodeStartRange.collapse(true);
46724     
46725         var nodeEndRange = nodeRange.cloneRange();
46726         nodeEndRange.collapse(false);
46727     
46728         return rangeStartRange.compareBoundaryPoints(
46729                  Range.START_TO_START, nodeEndRange) == -1 &&
46730                rangeEndRange.compareBoundaryPoints(
46731                  Range.START_TO_START, nodeStartRange) == 1;
46732         
46733          
46734     },
46735     rangeCompareNode : function(range, node)
46736     {
46737         var nodeRange = node.ownerDocument.createRange();
46738         try {
46739             nodeRange.selectNode(node);
46740         } catch (e) {
46741             nodeRange.selectNodeContents(node);
46742         }
46743         
46744         
46745         range.collapse(true);
46746     
46747         nodeRange.collapse(true);
46748      
46749         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
46750         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
46751          
46752         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
46753         
46754         var nodeIsBefore   =  ss == 1;
46755         var nodeIsAfter    = ee == -1;
46756         
46757         if (nodeIsBefore && nodeIsAfter) {
46758             return 0; // outer
46759         }
46760         if (!nodeIsBefore && nodeIsAfter) {
46761             return 1; //right trailed.
46762         }
46763         
46764         if (nodeIsBefore && !nodeIsAfter) {
46765             return 2;  // left trailed.
46766         }
46767         // fully contined.
46768         return 3;
46769     },
46770  
46771     cleanWordChars : function(input) {// change the chars to hex code
46772         
46773        var swapCodes  = [ 
46774             [    8211, "&#8211;" ], 
46775             [    8212, "&#8212;" ], 
46776             [    8216,  "'" ],  
46777             [    8217, "'" ],  
46778             [    8220, '"' ],  
46779             [    8221, '"' ],  
46780             [    8226, "*" ],  
46781             [    8230, "..." ]
46782         ]; 
46783         var output = input;
46784         Roo.each(swapCodes, function(sw) { 
46785             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46786             
46787             output = output.replace(swapper, sw[1]);
46788         });
46789         
46790         return output;
46791     },
46792     
46793      
46794     
46795         
46796     
46797     cleanUpChild : function (node)
46798     {
46799         
46800         new Roo.htmleditor.FilterComment({node : node});
46801         new Roo.htmleditor.FilterAttributes({
46802                 node : node,
46803                 attrib_black : this.ablack,
46804                 attrib_clean : this.aclean,
46805                 style_white : this.cwhite,
46806                 style_black : this.cblack
46807         });
46808         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
46809         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
46810          
46811         
46812     },
46813     
46814     /**
46815      * Clean up MS wordisms...
46816      * @deprecated - use filter directly
46817      */
46818     cleanWord : function(node)
46819     {
46820         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46821         
46822     },
46823    
46824     
46825     /**
46826
46827      * @deprecated - use filters
46828      */
46829     cleanTableWidths : function(node)
46830     {
46831         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
46832         
46833  
46834     },
46835     
46836      
46837         
46838     applyBlacklists : function()
46839     {
46840         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46841         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46842         
46843         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
46844         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
46845         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
46846         
46847         this.white = [];
46848         this.black = [];
46849         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46850             if (b.indexOf(tag) > -1) {
46851                 return;
46852             }
46853             this.white.push(tag);
46854             
46855         }, this);
46856         
46857         Roo.each(w, function(tag) {
46858             if (b.indexOf(tag) > -1) {
46859                 return;
46860             }
46861             if (this.white.indexOf(tag) > -1) {
46862                 return;
46863             }
46864             this.white.push(tag);
46865             
46866         }, this);
46867         
46868         
46869         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46870             if (w.indexOf(tag) > -1) {
46871                 return;
46872             }
46873             this.black.push(tag);
46874             
46875         }, this);
46876         
46877         Roo.each(b, function(tag) {
46878             if (w.indexOf(tag) > -1) {
46879                 return;
46880             }
46881             if (this.black.indexOf(tag) > -1) {
46882                 return;
46883             }
46884             this.black.push(tag);
46885             
46886         }, this);
46887         
46888         
46889         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46890         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46891         
46892         this.cwhite = [];
46893         this.cblack = [];
46894         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46895             if (b.indexOf(tag) > -1) {
46896                 return;
46897             }
46898             this.cwhite.push(tag);
46899             
46900         }, this);
46901         
46902         Roo.each(w, function(tag) {
46903             if (b.indexOf(tag) > -1) {
46904                 return;
46905             }
46906             if (this.cwhite.indexOf(tag) > -1) {
46907                 return;
46908             }
46909             this.cwhite.push(tag);
46910             
46911         }, this);
46912         
46913         
46914         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46915             if (w.indexOf(tag) > -1) {
46916                 return;
46917             }
46918             this.cblack.push(tag);
46919             
46920         }, this);
46921         
46922         Roo.each(b, function(tag) {
46923             if (w.indexOf(tag) > -1) {
46924                 return;
46925             }
46926             if (this.cblack.indexOf(tag) > -1) {
46927                 return;
46928             }
46929             this.cblack.push(tag);
46930             
46931         }, this);
46932     },
46933     
46934     setStylesheets : function(stylesheets)
46935     {
46936         if(typeof(stylesheets) == 'string'){
46937             Roo.get(this.iframe.contentDocument.head).createChild({
46938                 tag : 'link',
46939                 rel : 'stylesheet',
46940                 type : 'text/css',
46941                 href : stylesheets
46942             });
46943             
46944             return;
46945         }
46946         var _this = this;
46947      
46948         Roo.each(stylesheets, function(s) {
46949             if(!s.length){
46950                 return;
46951             }
46952             
46953             Roo.get(_this.iframe.contentDocument.head).createChild({
46954                 tag : 'link',
46955                 rel : 'stylesheet',
46956                 type : 'text/css',
46957                 href : s
46958             });
46959         });
46960
46961         
46962     },
46963     
46964     removeStylesheets : function()
46965     {
46966         var _this = this;
46967         
46968         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46969             s.remove();
46970         });
46971     },
46972     
46973     setStyle : function(style)
46974     {
46975         Roo.get(this.iframe.contentDocument.head).createChild({
46976             tag : 'style',
46977             type : 'text/css',
46978             html : style
46979         });
46980
46981         return;
46982     }
46983     
46984     // hide stuff that is not compatible
46985     /**
46986      * @event blur
46987      * @hide
46988      */
46989     /**
46990      * @event change
46991      * @hide
46992      */
46993     /**
46994      * @event focus
46995      * @hide
46996      */
46997     /**
46998      * @event specialkey
46999      * @hide
47000      */
47001     /**
47002      * @cfg {String} fieldClass @hide
47003      */
47004     /**
47005      * @cfg {String} focusClass @hide
47006      */
47007     /**
47008      * @cfg {String} autoCreate @hide
47009      */
47010     /**
47011      * @cfg {String} inputType @hide
47012      */
47013     /**
47014      * @cfg {String} invalidClass @hide
47015      */
47016     /**
47017      * @cfg {String} invalidText @hide
47018      */
47019     /**
47020      * @cfg {String} msgFx @hide
47021      */
47022     /**
47023      * @cfg {String} validateOnBlur @hide
47024      */
47025 });
47026
47027 Roo.HtmlEditorCore.white = [
47028         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47029         
47030        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47031        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47032        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47033        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47034        'TABLE',   'UL',         'XMP', 
47035        
47036        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47037       'THEAD',   'TR', 
47038      
47039       'DIR', 'MENU', 'OL', 'UL', 'DL',
47040        
47041       'EMBED',  'OBJECT'
47042 ];
47043
47044
47045 Roo.HtmlEditorCore.black = [
47046     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47047         'APPLET', // 
47048         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47049         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47050         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47051         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47052         //'FONT' // CLEAN LATER..
47053         'COLGROUP', 'COL'  // messy tables.
47054         
47055 ];
47056 Roo.HtmlEditorCore.clean = [ // ?? needed???
47057      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47058 ];
47059 Roo.HtmlEditorCore.tag_remove = [
47060     'FONT', 'TBODY'  
47061 ];
47062 // attributes..
47063
47064 Roo.HtmlEditorCore.ablack = [
47065     'on'
47066 ];
47067     
47068 Roo.HtmlEditorCore.aclean = [ 
47069     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47070 ];
47071
47072 // protocols..
47073 Roo.HtmlEditorCore.pwhite= [
47074         'http',  'https',  'mailto'
47075 ];
47076
47077 // white listed style attributes.
47078 Roo.HtmlEditorCore.cwhite= [
47079       //  'text-align', /// default is to allow most things..
47080       
47081          
47082 //        'font-size'//??
47083 ];
47084
47085 // black listed style attributes.
47086 Roo.HtmlEditorCore.cblack= [
47087       //  'font-size' -- this can be set by the project 
47088 ];
47089
47090
47091
47092
47093     //<script type="text/javascript">
47094
47095 /*
47096  * Ext JS Library 1.1.1
47097  * Copyright(c) 2006-2007, Ext JS, LLC.
47098  * Licence LGPL
47099  * 
47100  */
47101  
47102  
47103 Roo.form.HtmlEditor = function(config){
47104     
47105     
47106     
47107     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47108     
47109     if (!this.toolbars) {
47110         this.toolbars = [];
47111     }
47112     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47113     
47114     
47115 };
47116
47117 /**
47118  * @class Roo.form.HtmlEditor
47119  * @extends Roo.form.Field
47120  * Provides a lightweight HTML Editor component.
47121  *
47122  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47123  * 
47124  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47125  * supported by this editor.</b><br/><br/>
47126  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47127  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47128  */
47129 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47130     /**
47131      * @cfg {Boolean} clearUp
47132      */
47133     clearUp : true,
47134       /**
47135      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47136      */
47137     toolbars : false,
47138    
47139      /**
47140      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47141      *                        Roo.resizable.
47142      */
47143     resizable : false,
47144      /**
47145      * @cfg {Number} height (in pixels)
47146      */   
47147     height: 300,
47148    /**
47149      * @cfg {Number} width (in pixels)
47150      */   
47151     width: 500,
47152     
47153     /**
47154      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47155      * 
47156      */
47157     stylesheets: false,
47158     
47159     
47160      /**
47161      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47162      * 
47163      */
47164     cblack: false,
47165     /**
47166      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47167      * 
47168      */
47169     cwhite: false,
47170     
47171      /**
47172      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47173      * 
47174      */
47175     black: false,
47176     /**
47177      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47178      * 
47179      */
47180     white: false,
47181     /**
47182      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47183      */
47184     allowComments: false,
47185     /**
47186      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47187      */
47188     
47189     
47190      bodyCls : '',
47191     
47192     // id of frame..
47193     frameId: false,
47194     
47195     // private properties
47196     validationEvent : false,
47197     deferHeight: true,
47198     initialized : false,
47199     activated : false,
47200     
47201     onFocus : Roo.emptyFn,
47202     iframePad:3,
47203     hideMode:'offsets',
47204     
47205     actionMode : 'container', // defaults to hiding it...
47206     
47207     defaultAutoCreate : { // modified by initCompnoent..
47208         tag: "textarea",
47209         style:"width:500px;height:300px;",
47210         autocomplete: "new-password"
47211     },
47212
47213     // private
47214     initComponent : function(){
47215         this.addEvents({
47216             /**
47217              * @event initialize
47218              * Fires when the editor is fully initialized (including the iframe)
47219              * @param {HtmlEditor} this
47220              */
47221             initialize: true,
47222             /**
47223              * @event activate
47224              * Fires when the editor is first receives the focus. Any insertion must wait
47225              * until after this event.
47226              * @param {HtmlEditor} this
47227              */
47228             activate: true,
47229              /**
47230              * @event beforesync
47231              * Fires before the textarea is updated with content from the editor iframe. Return false
47232              * to cancel the sync.
47233              * @param {HtmlEditor} this
47234              * @param {String} html
47235              */
47236             beforesync: true,
47237              /**
47238              * @event beforepush
47239              * Fires before the iframe editor is updated with content from the textarea. Return false
47240              * to cancel the push.
47241              * @param {HtmlEditor} this
47242              * @param {String} html
47243              */
47244             beforepush: true,
47245              /**
47246              * @event sync
47247              * Fires when the textarea is updated with content from the editor iframe.
47248              * @param {HtmlEditor} this
47249              * @param {String} html
47250              */
47251             sync: true,
47252              /**
47253              * @event push
47254              * Fires when the iframe editor is updated with content from the textarea.
47255              * @param {HtmlEditor} this
47256              * @param {String} html
47257              */
47258             push: true,
47259              /**
47260              * @event editmodechange
47261              * Fires when the editor switches edit modes
47262              * @param {HtmlEditor} this
47263              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47264              */
47265             editmodechange: true,
47266             /**
47267              * @event editorevent
47268              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47269              * @param {HtmlEditor} this
47270              */
47271             editorevent: true,
47272             /**
47273              * @event firstfocus
47274              * Fires when on first focus - needed by toolbars..
47275              * @param {HtmlEditor} this
47276              */
47277             firstfocus: true,
47278             /**
47279              * @event autosave
47280              * Auto save the htmlEditor value as a file into Events
47281              * @param {HtmlEditor} this
47282              */
47283             autosave: true,
47284             /**
47285              * @event savedpreview
47286              * preview the saved version of htmlEditor
47287              * @param {HtmlEditor} this
47288              */
47289             savedpreview: true,
47290             
47291             /**
47292             * @event stylesheetsclick
47293             * Fires when press the Sytlesheets button
47294             * @param {Roo.HtmlEditorCore} this
47295             */
47296             stylesheetsclick: true,
47297             /**
47298             * @event paste
47299             * Fires when press user pastes into the editor
47300             * @param {Roo.HtmlEditorCore} this
47301             */
47302             paste: true 
47303         });
47304         this.defaultAutoCreate =  {
47305             tag: "textarea",
47306             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47307             autocomplete: "new-password"
47308         };
47309     },
47310
47311     /**
47312      * Protected method that will not generally be called directly. It
47313      * is called when the editor creates its toolbar. Override this method if you need to
47314      * add custom toolbar buttons.
47315      * @param {HtmlEditor} editor
47316      */
47317     createToolbar : function(editor){
47318         Roo.log("create toolbars");
47319         if (!editor.toolbars || !editor.toolbars.length) {
47320             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47321         }
47322         
47323         for (var i =0 ; i < editor.toolbars.length;i++) {
47324             editor.toolbars[i] = Roo.factory(
47325                     typeof(editor.toolbars[i]) == 'string' ?
47326                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47327                 Roo.form.HtmlEditor);
47328             editor.toolbars[i].init(editor);
47329         }
47330          
47331         
47332     },
47333
47334      
47335     // private
47336     onRender : function(ct, position)
47337     {
47338         var _t = this;
47339         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47340         
47341         this.wrap = this.el.wrap({
47342             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47343         });
47344         
47345         this.editorcore.onRender(ct, position);
47346          
47347         if (this.resizable) {
47348             this.resizeEl = new Roo.Resizable(this.wrap, {
47349                 pinned : true,
47350                 wrap: true,
47351                 dynamic : true,
47352                 minHeight : this.height,
47353                 height: this.height,
47354                 handles : this.resizable,
47355                 width: this.width,
47356                 listeners : {
47357                     resize : function(r, w, h) {
47358                         _t.onResize(w,h); // -something
47359                     }
47360                 }
47361             });
47362             
47363         }
47364         this.createToolbar(this);
47365        
47366         
47367         if(!this.width){
47368             this.setSize(this.wrap.getSize());
47369         }
47370         if (this.resizeEl) {
47371             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47372             // should trigger onReize..
47373         }
47374         
47375         this.keyNav = new Roo.KeyNav(this.el, {
47376             
47377             "tab" : function(e){
47378                 e.preventDefault();
47379                 
47380                 var value = this.getValue();
47381                 
47382                 var start = this.el.dom.selectionStart;
47383                 var end = this.el.dom.selectionEnd;
47384                 
47385                 if(!e.shiftKey){
47386                     
47387                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47388                     this.el.dom.setSelectionRange(end + 1, end + 1);
47389                     return;
47390                 }
47391                 
47392                 var f = value.substring(0, start).split("\t");
47393                 
47394                 if(f.pop().length != 0){
47395                     return;
47396                 }
47397                 
47398                 this.setValue(f.join("\t") + value.substring(end));
47399                 this.el.dom.setSelectionRange(start - 1, start - 1);
47400                 
47401             },
47402             
47403             "home" : function(e){
47404                 e.preventDefault();
47405                 
47406                 var curr = this.el.dom.selectionStart;
47407                 var lines = this.getValue().split("\n");
47408                 
47409                 if(!lines.length){
47410                     return;
47411                 }
47412                 
47413                 if(e.ctrlKey){
47414                     this.el.dom.setSelectionRange(0, 0);
47415                     return;
47416                 }
47417                 
47418                 var pos = 0;
47419                 
47420                 for (var i = 0; i < lines.length;i++) {
47421                     pos += lines[i].length;
47422                     
47423                     if(i != 0){
47424                         pos += 1;
47425                     }
47426                     
47427                     if(pos < curr){
47428                         continue;
47429                     }
47430                     
47431                     pos -= lines[i].length;
47432                     
47433                     break;
47434                 }
47435                 
47436                 if(!e.shiftKey){
47437                     this.el.dom.setSelectionRange(pos, pos);
47438                     return;
47439                 }
47440                 
47441                 this.el.dom.selectionStart = pos;
47442                 this.el.dom.selectionEnd = curr;
47443             },
47444             
47445             "end" : function(e){
47446                 e.preventDefault();
47447                 
47448                 var curr = this.el.dom.selectionStart;
47449                 var lines = this.getValue().split("\n");
47450                 
47451                 if(!lines.length){
47452                     return;
47453                 }
47454                 
47455                 if(e.ctrlKey){
47456                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
47457                     return;
47458                 }
47459                 
47460                 var pos = 0;
47461                 
47462                 for (var i = 0; i < lines.length;i++) {
47463                     
47464                     pos += lines[i].length;
47465                     
47466                     if(i != 0){
47467                         pos += 1;
47468                     }
47469                     
47470                     if(pos < curr){
47471                         continue;
47472                     }
47473                     
47474                     break;
47475                 }
47476                 
47477                 if(!e.shiftKey){
47478                     this.el.dom.setSelectionRange(pos, pos);
47479                     return;
47480                 }
47481                 
47482                 this.el.dom.selectionStart = curr;
47483                 this.el.dom.selectionEnd = pos;
47484             },
47485
47486             scope : this,
47487
47488             doRelay : function(foo, bar, hname){
47489                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47490             },
47491
47492             forceKeyDown: true
47493         });
47494         
47495 //        if(this.autosave && this.w){
47496 //            this.autoSaveFn = setInterval(this.autosave, 1000);
47497 //        }
47498     },
47499
47500     // private
47501     onResize : function(w, h)
47502     {
47503         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
47504         var ew = false;
47505         var eh = false;
47506         
47507         if(this.el ){
47508             if(typeof w == 'number'){
47509                 var aw = w - this.wrap.getFrameWidth('lr');
47510                 this.el.setWidth(this.adjustWidth('textarea', aw));
47511                 ew = aw;
47512             }
47513             if(typeof h == 'number'){
47514                 var tbh = 0;
47515                 for (var i =0; i < this.toolbars.length;i++) {
47516                     // fixme - ask toolbars for heights?
47517                     tbh += this.toolbars[i].tb.el.getHeight();
47518                     if (this.toolbars[i].footer) {
47519                         tbh += this.toolbars[i].footer.el.getHeight();
47520                     }
47521                 }
47522                 
47523                 
47524                 
47525                 
47526                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
47527                 ah -= 5; // knock a few pixes off for look..
47528 //                Roo.log(ah);
47529                 this.el.setHeight(this.adjustWidth('textarea', ah));
47530                 var eh = ah;
47531             }
47532         }
47533         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
47534         this.editorcore.onResize(ew,eh);
47535         
47536     },
47537
47538     /**
47539      * Toggles the editor between standard and source edit mode.
47540      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
47541      */
47542     toggleSourceEdit : function(sourceEditMode)
47543     {
47544         this.editorcore.toggleSourceEdit(sourceEditMode);
47545         
47546         if(this.editorcore.sourceEditMode){
47547             Roo.log('editor - showing textarea');
47548             
47549 //            Roo.log('in');
47550 //            Roo.log(this.syncValue());
47551             this.editorcore.syncValue();
47552             this.el.removeClass('x-hidden');
47553             this.el.dom.removeAttribute('tabIndex');
47554             this.el.focus();
47555             this.el.dom.scrollTop = 0;
47556             
47557             
47558             for (var i = 0; i < this.toolbars.length; i++) {
47559                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47560                     this.toolbars[i].tb.hide();
47561                     this.toolbars[i].footer.hide();
47562                 }
47563             }
47564             
47565         }else{
47566             Roo.log('editor - hiding textarea');
47567 //            Roo.log('out')
47568 //            Roo.log(this.pushValue()); 
47569             this.editorcore.pushValue();
47570             
47571             this.el.addClass('x-hidden');
47572             this.el.dom.setAttribute('tabIndex', -1);
47573             
47574             for (var i = 0; i < this.toolbars.length; i++) {
47575                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47576                     this.toolbars[i].tb.show();
47577                     this.toolbars[i].footer.show();
47578                 }
47579             }
47580             
47581             //this.deferFocus();
47582         }
47583         
47584         this.setSize(this.wrap.getSize());
47585         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
47586         
47587         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
47588     },
47589  
47590     // private (for BoxComponent)
47591     adjustSize : Roo.BoxComponent.prototype.adjustSize,
47592
47593     // private (for BoxComponent)
47594     getResizeEl : function(){
47595         return this.wrap;
47596     },
47597
47598     // private (for BoxComponent)
47599     getPositionEl : function(){
47600         return this.wrap;
47601     },
47602
47603     // private
47604     initEvents : function(){
47605         this.originalValue = this.getValue();
47606     },
47607
47608     /**
47609      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47610      * @method
47611      */
47612     markInvalid : Roo.emptyFn,
47613     /**
47614      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47615      * @method
47616      */
47617     clearInvalid : Roo.emptyFn,
47618
47619     setValue : function(v){
47620         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
47621         this.editorcore.pushValue();
47622     },
47623
47624      
47625     // private
47626     deferFocus : function(){
47627         this.focus.defer(10, this);
47628     },
47629
47630     // doc'ed in Field
47631     focus : function(){
47632         this.editorcore.focus();
47633         
47634     },
47635       
47636
47637     // private
47638     onDestroy : function(){
47639         
47640         
47641         
47642         if(this.rendered){
47643             
47644             for (var i =0; i < this.toolbars.length;i++) {
47645                 // fixme - ask toolbars for heights?
47646                 this.toolbars[i].onDestroy();
47647             }
47648             
47649             this.wrap.dom.innerHTML = '';
47650             this.wrap.remove();
47651         }
47652     },
47653
47654     // private
47655     onFirstFocus : function(){
47656         //Roo.log("onFirstFocus");
47657         this.editorcore.onFirstFocus();
47658          for (var i =0; i < this.toolbars.length;i++) {
47659             this.toolbars[i].onFirstFocus();
47660         }
47661         
47662     },
47663     
47664     // private
47665     syncValue : function()
47666     {
47667         this.editorcore.syncValue();
47668     },
47669     
47670     pushValue : function()
47671     {
47672         this.editorcore.pushValue();
47673     },
47674     
47675     setStylesheets : function(stylesheets)
47676     {
47677         this.editorcore.setStylesheets(stylesheets);
47678     },
47679     
47680     removeStylesheets : function()
47681     {
47682         this.editorcore.removeStylesheets();
47683     }
47684      
47685     
47686     // hide stuff that is not compatible
47687     /**
47688      * @event blur
47689      * @hide
47690      */
47691     /**
47692      * @event change
47693      * @hide
47694      */
47695     /**
47696      * @event focus
47697      * @hide
47698      */
47699     /**
47700      * @event specialkey
47701      * @hide
47702      */
47703     /**
47704      * @cfg {String} fieldClass @hide
47705      */
47706     /**
47707      * @cfg {String} focusClass @hide
47708      */
47709     /**
47710      * @cfg {String} autoCreate @hide
47711      */
47712     /**
47713      * @cfg {String} inputType @hide
47714      */
47715     /**
47716      * @cfg {String} invalidClass @hide
47717      */
47718     /**
47719      * @cfg {String} invalidText @hide
47720      */
47721     /**
47722      * @cfg {String} msgFx @hide
47723      */
47724     /**
47725      * @cfg {String} validateOnBlur @hide
47726      */
47727 });
47728  
47729     // <script type="text/javascript">
47730 /*
47731  * Based on
47732  * Ext JS Library 1.1.1
47733  * Copyright(c) 2006-2007, Ext JS, LLC.
47734  *  
47735  
47736  */
47737
47738 /**
47739  * @class Roo.form.HtmlEditorToolbar1
47740  * Basic Toolbar
47741  * 
47742  * Usage:
47743  *
47744  new Roo.form.HtmlEditor({
47745     ....
47746     toolbars : [
47747         new Roo.form.HtmlEditorToolbar1({
47748             disable : { fonts: 1 , format: 1, ..., ... , ...],
47749             btns : [ .... ]
47750         })
47751     }
47752      
47753  * 
47754  * @cfg {Object} disable List of elements to disable..
47755  * @cfg {Array} btns List of additional buttons.
47756  * 
47757  * 
47758  * NEEDS Extra CSS? 
47759  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47760  */
47761  
47762 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47763 {
47764     
47765     Roo.apply(this, config);
47766     
47767     // default disabled, based on 'good practice'..
47768     this.disable = this.disable || {};
47769     Roo.applyIf(this.disable, {
47770         fontSize : true,
47771         colors : true,
47772         specialElements : true
47773     });
47774     
47775     
47776     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47777     // dont call parent... till later.
47778 }
47779
47780 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47781     
47782     tb: false,
47783     
47784     rendered: false,
47785     
47786     editor : false,
47787     editorcore : false,
47788     /**
47789      * @cfg {Object} disable  List of toolbar elements to disable
47790          
47791      */
47792     disable : false,
47793     
47794     
47795      /**
47796      * @cfg {String} createLinkText The default text for the create link prompt
47797      */
47798     createLinkText : 'Please enter the URL for the link:',
47799     /**
47800      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47801      */
47802     defaultLinkValue : 'http:/'+'/',
47803    
47804     
47805       /**
47806      * @cfg {Array} fontFamilies An array of available font families
47807      */
47808     fontFamilies : [
47809         'Arial',
47810         'Courier New',
47811         'Tahoma',
47812         'Times New Roman',
47813         'Verdana'
47814     ],
47815     
47816     specialChars : [
47817            "&#169;",
47818           "&#174;",     
47819           "&#8482;",    
47820           "&#163;" ,    
47821          // "&#8212;",    
47822           "&#8230;",    
47823           "&#247;" ,    
47824         //  "&#225;" ,     ?? a acute?
47825            "&#8364;"    , //Euro
47826        //   "&#8220;"    ,
47827         //  "&#8221;"    ,
47828         //  "&#8226;"    ,
47829           "&#176;"  //   , // degrees
47830
47831          // "&#233;"     , // e ecute
47832          // "&#250;"     , // u ecute?
47833     ],
47834     
47835     specialElements : [
47836         {
47837             text: "Insert Table",
47838             xtype: 'MenuItem',
47839             xns : Roo.Menu,
47840             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47841                 
47842         },
47843         {    
47844             text: "Insert Image",
47845             xtype: 'MenuItem',
47846             xns : Roo.Menu,
47847             ihtml : '<img src="about:blank"/>'
47848             
47849         }
47850         
47851          
47852     ],
47853     
47854     
47855     inputElements : [ 
47856             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47857             "input:submit", "input:button", "select", "textarea", "label" ],
47858     formats : [
47859         ["p"] ,  
47860         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47861         ["pre"],[ "code"], 
47862         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47863         ['div'],['span'],
47864         ['sup'],['sub']
47865     ],
47866     
47867     cleanStyles : [
47868         "font-size"
47869     ],
47870      /**
47871      * @cfg {String} defaultFont default font to use.
47872      */
47873     defaultFont: 'tahoma',
47874    
47875     fontSelect : false,
47876     
47877     
47878     formatCombo : false,
47879     
47880     init : function(editor)
47881     {
47882         this.editor = editor;
47883         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47884         var editorcore = this.editorcore;
47885         
47886         var _t = this;
47887         
47888         var fid = editorcore.frameId;
47889         var etb = this;
47890         function btn(id, toggle, handler){
47891             var xid = fid + '-'+ id ;
47892             return {
47893                 id : xid,
47894                 cmd : id,
47895                 cls : 'x-btn-icon x-edit-'+id,
47896                 enableToggle:toggle !== false,
47897                 scope: _t, // was editor...
47898                 handler:handler||_t.relayBtnCmd,
47899                 clickEvent:'mousedown',
47900                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47901                 tabIndex:-1
47902             };
47903         }
47904         
47905         
47906         
47907         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47908         this.tb = tb;
47909          // stop form submits
47910         tb.el.on('click', function(e){
47911             e.preventDefault(); // what does this do?
47912         });
47913
47914         if(!this.disable.font) { // && !Roo.isSafari){
47915             /* why no safari for fonts 
47916             editor.fontSelect = tb.el.createChild({
47917                 tag:'select',
47918                 tabIndex: -1,
47919                 cls:'x-font-select',
47920                 html: this.createFontOptions()
47921             });
47922             
47923             editor.fontSelect.on('change', function(){
47924                 var font = editor.fontSelect.dom.value;
47925                 editor.relayCmd('fontname', font);
47926                 editor.deferFocus();
47927             }, editor);
47928             
47929             tb.add(
47930                 editor.fontSelect.dom,
47931                 '-'
47932             );
47933             */
47934             
47935         };
47936         if(!this.disable.formats){
47937             this.formatCombo = new Roo.form.ComboBox({
47938                 store: new Roo.data.SimpleStore({
47939                     id : 'tag',
47940                     fields: ['tag'],
47941                     data : this.formats // from states.js
47942                 }),
47943                 blockFocus : true,
47944                 name : '',
47945                 //autoCreate : {tag: "div",  size: "20"},
47946                 displayField:'tag',
47947                 typeAhead: false,
47948                 mode: 'local',
47949                 editable : false,
47950                 triggerAction: 'all',
47951                 emptyText:'Add tag',
47952                 selectOnFocus:true,
47953                 width:135,
47954                 listeners : {
47955                     'select': function(c, r, i) {
47956                         editorcore.insertTag(r.get('tag'));
47957                         editor.focus();
47958                     }
47959                 }
47960
47961             });
47962             tb.addField(this.formatCombo);
47963             
47964         }
47965         
47966         if(!this.disable.format){
47967             tb.add(
47968                 btn('bold'),
47969                 btn('italic'),
47970                 btn('underline'),
47971                 btn('strikethrough')
47972             );
47973         };
47974         if(!this.disable.fontSize){
47975             tb.add(
47976                 '-',
47977                 
47978                 
47979                 btn('increasefontsize', false, editorcore.adjustFont),
47980                 btn('decreasefontsize', false, editorcore.adjustFont)
47981             );
47982         };
47983         
47984         
47985         if(!this.disable.colors){
47986             tb.add(
47987                 '-', {
47988                     id:editorcore.frameId +'-forecolor',
47989                     cls:'x-btn-icon x-edit-forecolor',
47990                     clickEvent:'mousedown',
47991                     tooltip: this.buttonTips['forecolor'] || undefined,
47992                     tabIndex:-1,
47993                     menu : new Roo.menu.ColorMenu({
47994                         allowReselect: true,
47995                         focus: Roo.emptyFn,
47996                         value:'000000',
47997                         plain:true,
47998                         selectHandler: function(cp, color){
47999                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48000                             editor.deferFocus();
48001                         },
48002                         scope: editorcore,
48003                         clickEvent:'mousedown'
48004                     })
48005                 }, {
48006                     id:editorcore.frameId +'backcolor',
48007                     cls:'x-btn-icon x-edit-backcolor',
48008                     clickEvent:'mousedown',
48009                     tooltip: this.buttonTips['backcolor'] || undefined,
48010                     tabIndex:-1,
48011                     menu : new Roo.menu.ColorMenu({
48012                         focus: Roo.emptyFn,
48013                         value:'FFFFFF',
48014                         plain:true,
48015                         allowReselect: true,
48016                         selectHandler: function(cp, color){
48017                             if(Roo.isGecko){
48018                                 editorcore.execCmd('useCSS', false);
48019                                 editorcore.execCmd('hilitecolor', color);
48020                                 editorcore.execCmd('useCSS', true);
48021                                 editor.deferFocus();
48022                             }else{
48023                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48024                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48025                                 editor.deferFocus();
48026                             }
48027                         },
48028                         scope:editorcore,
48029                         clickEvent:'mousedown'
48030                     })
48031                 }
48032             );
48033         };
48034         // now add all the items...
48035         
48036
48037         if(!this.disable.alignments){
48038             tb.add(
48039                 '-',
48040                 btn('justifyleft'),
48041                 btn('justifycenter'),
48042                 btn('justifyright')
48043             );
48044         };
48045
48046         //if(!Roo.isSafari){
48047             if(!this.disable.links){
48048                 tb.add(
48049                     '-',
48050                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48051                 );
48052             };
48053
48054             if(!this.disable.lists){
48055                 tb.add(
48056                     '-',
48057                     btn('insertorderedlist'),
48058                     btn('insertunorderedlist')
48059                 );
48060             }
48061             if(!this.disable.sourceEdit){
48062                 tb.add(
48063                     '-',
48064                     btn('sourceedit', true, function(btn){
48065                         this.toggleSourceEdit(btn.pressed);
48066                     })
48067                 );
48068             }
48069         //}
48070         
48071         var smenu = { };
48072         // special menu.. - needs to be tidied up..
48073         if (!this.disable.special) {
48074             smenu = {
48075                 text: "&#169;",
48076                 cls: 'x-edit-none',
48077                 
48078                 menu : {
48079                     items : []
48080                 }
48081             };
48082             for (var i =0; i < this.specialChars.length; i++) {
48083                 smenu.menu.items.push({
48084                     
48085                     html: this.specialChars[i],
48086                     handler: function(a,b) {
48087                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48088                         //editor.insertAtCursor(a.html);
48089                         
48090                     },
48091                     tabIndex:-1
48092                 });
48093             }
48094             
48095             
48096             tb.add(smenu);
48097             
48098             
48099         }
48100         
48101         var cmenu = { };
48102         if (!this.disable.cleanStyles) {
48103             cmenu = {
48104                 cls: 'x-btn-icon x-btn-clear',
48105                 
48106                 menu : {
48107                     items : []
48108                 }
48109             };
48110             for (var i =0; i < this.cleanStyles.length; i++) {
48111                 cmenu.menu.items.push({
48112                     actiontype : this.cleanStyles[i],
48113                     html: 'Remove ' + this.cleanStyles[i],
48114                     handler: function(a,b) {
48115 //                        Roo.log(a);
48116 //                        Roo.log(b);
48117                         var c = Roo.get(editorcore.doc.body);
48118                         c.select('[style]').each(function(s) {
48119                             s.dom.style.removeProperty(a.actiontype);
48120                         });
48121                         editorcore.syncValue();
48122                     },
48123                     tabIndex:-1
48124                 });
48125             }
48126             cmenu.menu.items.push({
48127                 actiontype : 'tablewidths',
48128                 html: 'Remove Table Widths',
48129                 handler: function(a,b) {
48130                     editorcore.cleanTableWidths();
48131                     editorcore.syncValue();
48132                 },
48133                 tabIndex:-1
48134             });
48135             cmenu.menu.items.push({
48136                 actiontype : 'word',
48137                 html: 'Remove MS Word Formating',
48138                 handler: function(a,b) {
48139                     editorcore.cleanWord();
48140                     editorcore.syncValue();
48141                 },
48142                 tabIndex:-1
48143             });
48144             
48145             cmenu.menu.items.push({
48146                 actiontype : 'all',
48147                 html: 'Remove All Styles',
48148                 handler: function(a,b) {
48149                     
48150                     var c = Roo.get(editorcore.doc.body);
48151                     c.select('[style]').each(function(s) {
48152                         s.dom.removeAttribute('style');
48153                     });
48154                     editorcore.syncValue();
48155                 },
48156                 tabIndex:-1
48157             });
48158             
48159             cmenu.menu.items.push({
48160                 actiontype : 'all',
48161                 html: 'Remove All CSS Classes',
48162                 handler: function(a,b) {
48163                     
48164                     var c = Roo.get(editorcore.doc.body);
48165                     c.select('[class]').each(function(s) {
48166                         s.dom.removeAttribute('class');
48167                     });
48168                     editorcore.cleanWord();
48169                     editorcore.syncValue();
48170                 },
48171                 tabIndex:-1
48172             });
48173             
48174              cmenu.menu.items.push({
48175                 actiontype : 'tidy',
48176                 html: 'Tidy HTML Source',
48177                 handler: function(a,b) {
48178                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48179                     editorcore.syncValue();
48180                 },
48181                 tabIndex:-1
48182             });
48183             
48184             
48185             tb.add(cmenu);
48186         }
48187          
48188         if (!this.disable.specialElements) {
48189             var semenu = {
48190                 text: "Other;",
48191                 cls: 'x-edit-none',
48192                 menu : {
48193                     items : []
48194                 }
48195             };
48196             for (var i =0; i < this.specialElements.length; i++) {
48197                 semenu.menu.items.push(
48198                     Roo.apply({ 
48199                         handler: function(a,b) {
48200                             editor.insertAtCursor(this.ihtml);
48201                         }
48202                     }, this.specialElements[i])
48203                 );
48204                     
48205             }
48206             
48207             tb.add(semenu);
48208             
48209             
48210         }
48211          
48212         
48213         if (this.btns) {
48214             for(var i =0; i< this.btns.length;i++) {
48215                 var b = Roo.factory(this.btns[i],Roo.form);
48216                 b.cls =  'x-edit-none';
48217                 
48218                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48219                     b.cls += ' x-init-enable';
48220                 }
48221                 
48222                 b.scope = editorcore;
48223                 tb.add(b);
48224             }
48225         
48226         }
48227         
48228         
48229         
48230         // disable everything...
48231         
48232         this.tb.items.each(function(item){
48233             
48234            if(
48235                 item.id != editorcore.frameId+ '-sourceedit' && 
48236                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48237             ){
48238                 
48239                 item.disable();
48240             }
48241         });
48242         this.rendered = true;
48243         
48244         // the all the btns;
48245         editor.on('editorevent', this.updateToolbar, this);
48246         // other toolbars need to implement this..
48247         //editor.on('editmodechange', this.updateToolbar, this);
48248     },
48249     
48250     
48251     relayBtnCmd : function(btn) {
48252         this.editorcore.relayCmd(btn.cmd);
48253     },
48254     // private used internally
48255     createLink : function(){
48256         Roo.log("create link?");
48257         var url = prompt(this.createLinkText, this.defaultLinkValue);
48258         if(url && url != 'http:/'+'/'){
48259             this.editorcore.relayCmd('createlink', url);
48260         }
48261     },
48262
48263     
48264     /**
48265      * Protected method that will not generally be called directly. It triggers
48266      * a toolbar update by reading the markup state of the current selection in the editor.
48267      */
48268     updateToolbar: function(){
48269
48270         if(!this.editorcore.activated){
48271             this.editor.onFirstFocus();
48272             return;
48273         }
48274
48275         var btns = this.tb.items.map, 
48276             doc = this.editorcore.doc,
48277             frameId = this.editorcore.frameId;
48278
48279         if(!this.disable.font && !Roo.isSafari){
48280             /*
48281             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48282             if(name != this.fontSelect.dom.value){
48283                 this.fontSelect.dom.value = name;
48284             }
48285             */
48286         }
48287         if(!this.disable.format){
48288             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48289             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48290             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48291             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48292         }
48293         if(!this.disable.alignments){
48294             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48295             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48296             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48297         }
48298         if(!Roo.isSafari && !this.disable.lists){
48299             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48300             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48301         }
48302         
48303         var ans = this.editorcore.getAllAncestors();
48304         if (this.formatCombo) {
48305             
48306             
48307             var store = this.formatCombo.store;
48308             this.formatCombo.setValue("");
48309             for (var i =0; i < ans.length;i++) {
48310                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48311                     // select it..
48312                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48313                     break;
48314                 }
48315             }
48316         }
48317         
48318         
48319         
48320         // hides menus... - so this cant be on a menu...
48321         Roo.menu.MenuMgr.hideAll();
48322
48323         //this.editorsyncValue();
48324     },
48325    
48326     
48327     createFontOptions : function(){
48328         var buf = [], fs = this.fontFamilies, ff, lc;
48329         
48330         
48331         
48332         for(var i = 0, len = fs.length; i< len; i++){
48333             ff = fs[i];
48334             lc = ff.toLowerCase();
48335             buf.push(
48336                 '<option value="',lc,'" style="font-family:',ff,';"',
48337                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48338                     ff,
48339                 '</option>'
48340             );
48341         }
48342         return buf.join('');
48343     },
48344     
48345     toggleSourceEdit : function(sourceEditMode){
48346         
48347         Roo.log("toolbar toogle");
48348         if(sourceEditMode === undefined){
48349             sourceEditMode = !this.sourceEditMode;
48350         }
48351         this.sourceEditMode = sourceEditMode === true;
48352         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48353         // just toggle the button?
48354         if(btn.pressed !== this.sourceEditMode){
48355             btn.toggle(this.sourceEditMode);
48356             return;
48357         }
48358         
48359         if(sourceEditMode){
48360             Roo.log("disabling buttons");
48361             this.tb.items.each(function(item){
48362                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48363                     item.disable();
48364                 }
48365             });
48366           
48367         }else{
48368             Roo.log("enabling buttons");
48369             if(this.editorcore.initialized){
48370                 this.tb.items.each(function(item){
48371                     item.enable();
48372                 });
48373             }
48374             
48375         }
48376         Roo.log("calling toggole on editor");
48377         // tell the editor that it's been pressed..
48378         this.editor.toggleSourceEdit(sourceEditMode);
48379        
48380     },
48381      /**
48382      * Object collection of toolbar tooltips for the buttons in the editor. The key
48383      * is the command id associated with that button and the value is a valid QuickTips object.
48384      * For example:
48385 <pre><code>
48386 {
48387     bold : {
48388         title: 'Bold (Ctrl+B)',
48389         text: 'Make the selected text bold.',
48390         cls: 'x-html-editor-tip'
48391     },
48392     italic : {
48393         title: 'Italic (Ctrl+I)',
48394         text: 'Make the selected text italic.',
48395         cls: 'x-html-editor-tip'
48396     },
48397     ...
48398 </code></pre>
48399     * @type Object
48400      */
48401     buttonTips : {
48402         bold : {
48403             title: 'Bold (Ctrl+B)',
48404             text: 'Make the selected text bold.',
48405             cls: 'x-html-editor-tip'
48406         },
48407         italic : {
48408             title: 'Italic (Ctrl+I)',
48409             text: 'Make the selected text italic.',
48410             cls: 'x-html-editor-tip'
48411         },
48412         underline : {
48413             title: 'Underline (Ctrl+U)',
48414             text: 'Underline the selected text.',
48415             cls: 'x-html-editor-tip'
48416         },
48417         strikethrough : {
48418             title: 'Strikethrough',
48419             text: 'Strikethrough the selected text.',
48420             cls: 'x-html-editor-tip'
48421         },
48422         increasefontsize : {
48423             title: 'Grow Text',
48424             text: 'Increase the font size.',
48425             cls: 'x-html-editor-tip'
48426         },
48427         decreasefontsize : {
48428             title: 'Shrink Text',
48429             text: 'Decrease the font size.',
48430             cls: 'x-html-editor-tip'
48431         },
48432         backcolor : {
48433             title: 'Text Highlight Color',
48434             text: 'Change the background color of the selected text.',
48435             cls: 'x-html-editor-tip'
48436         },
48437         forecolor : {
48438             title: 'Font Color',
48439             text: 'Change the color of the selected text.',
48440             cls: 'x-html-editor-tip'
48441         },
48442         justifyleft : {
48443             title: 'Align Text Left',
48444             text: 'Align text to the left.',
48445             cls: 'x-html-editor-tip'
48446         },
48447         justifycenter : {
48448             title: 'Center Text',
48449             text: 'Center text in the editor.',
48450             cls: 'x-html-editor-tip'
48451         },
48452         justifyright : {
48453             title: 'Align Text Right',
48454             text: 'Align text to the right.',
48455             cls: 'x-html-editor-tip'
48456         },
48457         insertunorderedlist : {
48458             title: 'Bullet List',
48459             text: 'Start a bulleted list.',
48460             cls: 'x-html-editor-tip'
48461         },
48462         insertorderedlist : {
48463             title: 'Numbered List',
48464             text: 'Start a numbered list.',
48465             cls: 'x-html-editor-tip'
48466         },
48467         createlink : {
48468             title: 'Hyperlink',
48469             text: 'Make the selected text a hyperlink.',
48470             cls: 'x-html-editor-tip'
48471         },
48472         sourceedit : {
48473             title: 'Source Edit',
48474             text: 'Switch to source editing mode.',
48475             cls: 'x-html-editor-tip'
48476         }
48477     },
48478     // private
48479     onDestroy : function(){
48480         if(this.rendered){
48481             
48482             this.tb.items.each(function(item){
48483                 if(item.menu){
48484                     item.menu.removeAll();
48485                     if(item.menu.el){
48486                         item.menu.el.destroy();
48487                     }
48488                 }
48489                 item.destroy();
48490             });
48491              
48492         }
48493     },
48494     onFirstFocus: function() {
48495         this.tb.items.each(function(item){
48496            item.enable();
48497         });
48498     }
48499 });
48500
48501
48502
48503
48504 // <script type="text/javascript">
48505 /*
48506  * Based on
48507  * Ext JS Library 1.1.1
48508  * Copyright(c) 2006-2007, Ext JS, LLC.
48509  *  
48510  
48511  */
48512
48513  
48514 /**
48515  * @class Roo.form.HtmlEditor.ToolbarContext
48516  * Context Toolbar
48517  * 
48518  * Usage:
48519  *
48520  new Roo.form.HtmlEditor({
48521     ....
48522     toolbars : [
48523         { xtype: 'ToolbarStandard', styles : {} }
48524         { xtype: 'ToolbarContext', disable : {} }
48525     ]
48526 })
48527
48528      
48529  * 
48530  * @config : {Object} disable List of elements to disable.. (not done yet.)
48531  * @config : {Object} styles  Map of styles available.
48532  * 
48533  */
48534
48535 Roo.form.HtmlEditor.ToolbarContext = function(config)
48536 {
48537     
48538     Roo.apply(this, config);
48539     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48540     // dont call parent... till later.
48541     this.styles = this.styles || {};
48542 }
48543
48544  
48545
48546 Roo.form.HtmlEditor.ToolbarContext.types = {
48547     'IMG' : {
48548         width : {
48549             title: "Width",
48550             width: 40
48551         },
48552         height:  {
48553             title: "Height",
48554             width: 40
48555         },
48556         align: {
48557             title: "Align",
48558             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48559             width : 80
48560             
48561         },
48562         border: {
48563             title: "Border",
48564             width: 40
48565         },
48566         alt: {
48567             title: "Alt",
48568             width: 120
48569         },
48570         src : {
48571             title: "Src",
48572             width: 220
48573         }
48574         
48575     },
48576     
48577     'FIGURE' : {
48578          align: {
48579             title: "Align",
48580             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48581             width : 80  
48582         }
48583     },
48584     'A' : {
48585         name : {
48586             title: "Name",
48587             width: 50
48588         },
48589         target:  {
48590             title: "Target",
48591             width: 120
48592         },
48593         href:  {
48594             title: "Href",
48595             width: 220
48596         } // border?
48597         
48598     },
48599     'TABLE' : {
48600         rows : {
48601             title: "Rows",
48602             width: 20
48603         },
48604         cols : {
48605             title: "Cols",
48606             width: 20
48607         },
48608         width : {
48609             title: "Width",
48610             width: 40
48611         },
48612         height : {
48613             title: "Height",
48614             width: 40
48615         },
48616         border : {
48617             title: "Border",
48618             width: 20
48619         }
48620     },
48621     'TD' : {
48622         width : {
48623             title: "Width",
48624             width: 40
48625         },
48626         height : {
48627             title: "Height",
48628             width: 40
48629         },   
48630         align: {
48631             title: "Align",
48632             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
48633             width: 80
48634         },
48635         valign: {
48636             title: "Valign",
48637             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
48638             width: 80
48639         },
48640         colspan: {
48641             title: "Colspan",
48642             width: 20
48643             
48644         },
48645          'font-family'  : {
48646             title : "Font",
48647             style : 'fontFamily',
48648             displayField: 'display',
48649             optname : 'font-family',
48650             width: 140
48651         }
48652     },
48653     'INPUT' : {
48654         name : {
48655             title: "name",
48656             width: 120
48657         },
48658         value : {
48659             title: "Value",
48660             width: 120
48661         },
48662         width : {
48663             title: "Width",
48664             width: 40
48665         }
48666     },
48667     'LABEL' : {
48668         'for' : {
48669             title: "For",
48670             width: 120
48671         }
48672     },
48673     'TEXTAREA' : {
48674         name : {
48675             title: "name",
48676             width: 120
48677         },
48678         rows : {
48679             title: "Rows",
48680             width: 20
48681         },
48682         cols : {
48683             title: "Cols",
48684             width: 20
48685         }
48686     },
48687     'SELECT' : {
48688         name : {
48689             title: "name",
48690             width: 120
48691         },
48692         selectoptions : {
48693             title: "Options",
48694             width: 200
48695         }
48696     },
48697     
48698     // should we really allow this??
48699     // should this just be 
48700     'BODY' : {
48701         title : {
48702             title: "Title",
48703             width: 200,
48704             disabled : true
48705         }
48706     },
48707     /*
48708     'SPAN' : {
48709         'font-family'  : {
48710             title : "Font",
48711             style : 'fontFamily',
48712             displayField: 'display',
48713             optname : 'font-family',
48714             width: 140
48715         }
48716     },
48717     'DIV' : {
48718         'font-family'  : {
48719             title : "Font",
48720             style : 'fontFamily',
48721             displayField: 'display',
48722             optname : 'font-family',
48723             width: 140
48724         }
48725     },
48726      'P' : {
48727         'font-family'  : {
48728             title : "Font",
48729             style : 'fontFamily',
48730             displayField: 'display',
48731             optname : 'font-family',
48732             width: 140
48733         }
48734     },
48735     */
48736     '*' : {
48737         // empty..
48738     }
48739
48740 };
48741
48742 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48743 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48744
48745 Roo.form.HtmlEditor.ToolbarContext.options = {
48746         'font-family'  : [ 
48747                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48748                 [ 'Courier New', 'Courier New'],
48749                 [ 'Tahoma', 'Tahoma'],
48750                 [ 'Times New Roman,serif', 'Times'],
48751                 [ 'Verdana','Verdana' ]
48752         ]
48753 };
48754
48755 // fixme - these need to be configurable..
48756  
48757
48758 //Roo.form.HtmlEditor.ToolbarContext.types
48759
48760
48761 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48762     
48763     tb: false,
48764     
48765     rendered: false,
48766     
48767     editor : false,
48768     editorcore : false,
48769     /**
48770      * @cfg {Object} disable  List of toolbar elements to disable
48771          
48772      */
48773     disable : false,
48774     /**
48775      * @cfg {Object} styles List of styles 
48776      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48777      *
48778      * These must be defined in the page, so they get rendered correctly..
48779      * .headline { }
48780      * TD.underline { }
48781      * 
48782      */
48783     styles : false,
48784     
48785     options: false,
48786     
48787     toolbars : false,
48788     
48789     init : function(editor)
48790     {
48791         this.editor = editor;
48792         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48793         var editorcore = this.editorcore;
48794         
48795         var fid = editorcore.frameId;
48796         var etb = this;
48797         function btn(id, toggle, handler){
48798             var xid = fid + '-'+ id ;
48799             return {
48800                 id : xid,
48801                 cmd : id,
48802                 cls : 'x-btn-icon x-edit-'+id,
48803                 enableToggle:toggle !== false,
48804                 scope: editorcore, // was editor...
48805                 handler:handler||editorcore.relayBtnCmd,
48806                 clickEvent:'mousedown',
48807                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48808                 tabIndex:-1
48809             };
48810         }
48811         // create a new element.
48812         var wdiv = editor.wrap.createChild({
48813                 tag: 'div'
48814             }, editor.wrap.dom.firstChild.nextSibling, true);
48815         
48816         // can we do this more than once??
48817         
48818          // stop form submits
48819       
48820  
48821         // disable everything...
48822         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48823         this.toolbars = {};
48824            
48825         for (var i in  ty) {
48826           
48827             this.toolbars[i] = this.buildToolbar(ty[i],i);
48828         }
48829         this.tb = this.toolbars.BODY;
48830         this.tb.el.show();
48831         this.buildFooter();
48832         this.footer.show();
48833         editor.on('hide', function( ) { this.footer.hide() }, this);
48834         editor.on('show', function( ) { this.footer.show() }, this);
48835         
48836          
48837         this.rendered = true;
48838         
48839         // the all the btns;
48840         editor.on('editorevent', this.updateToolbar, this);
48841         // other toolbars need to implement this..
48842         //editor.on('editmodechange', this.updateToolbar, this);
48843     },
48844     
48845     
48846     
48847     /**
48848      * Protected method that will not generally be called directly. It triggers
48849      * a toolbar update by reading the markup state of the current selection in the editor.
48850      *
48851      * Note you can force an update by calling on('editorevent', scope, false)
48852      */
48853     updateToolbar: function(editor ,ev, sel){
48854
48855         //Roo.log(ev);
48856         // capture mouse up - this is handy for selecting images..
48857         // perhaps should go somewhere else...
48858         if(!this.editorcore.activated){
48859              this.editor.onFirstFocus();
48860             return;
48861         }
48862         
48863         
48864         
48865         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48866         // selectNode - might want to handle IE?
48867         
48868         
48869         
48870         if (ev &&
48871             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48872             ev.target && ev.target.tagName == 'IMG') {
48873             // they have click on an image...
48874             // let's see if we can change the selection...
48875             sel = ev.target;
48876             
48877             
48878             this.editorcore.selectNode(sel);
48879              
48880         }  
48881         
48882       
48883         //var updateFooter = sel ? false : true; 
48884         
48885         
48886         var ans = this.editorcore.getAllAncestors();
48887         
48888         // pick
48889         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
48890         
48891         if (!sel) { 
48892             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48893             sel = sel ? sel : this.editorcore.doc.body;
48894             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48895             
48896         }
48897         
48898         var tn = sel.tagName.toUpperCase();
48899         var lastSel = this.tb.selectedNode;
48900         this.tb.selectedNode = sel;
48901         var left_label = tn;
48902         
48903         // ok see if we are editing a block?
48904         
48905         var db = Roo.get(sel).findParent('[data-block]');
48906         var cepar = Roo.get(sel).findParent('[contenteditable=true]');
48907         if (db && cepar && cepar.tagName != 'BODY') {
48908             db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
48909         }
48910         var block = false;
48911         if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
48912             block = Roo.htmleditor.Block.factory(db);
48913             if (block) {
48914                 tn = 'BLOCK.' + db.getAttribute('data-block');
48915                 this.tb.selectedNode = db;
48916                 this.editorcore.selectNode(db);
48917                 if (typeof(this.toolbars[tn]) == 'undefined') {
48918                    this.toolbars[tn] = this.buildToolbar( block.context,tn ,block.friendly_name);
48919                 }
48920                 left_label = block.friendly_name;
48921                 ans = this.editorcore.getAllAncestors();
48922             }
48923             
48924                 
48925             
48926         }
48927         
48928         
48929         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
48930             return; // no change?
48931         }
48932         
48933         
48934           
48935         this.tb.el.hide();
48936         ///console.log("show: " + tn);
48937         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48938         
48939         this.tb.el.show();
48940         // update name
48941         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
48942         
48943         
48944         // update attributes
48945         if (block) {
48946              
48947             this.tb.fields.each(function(e) {
48948                 e.setValue(block[e.attrname]);
48949             });
48950             
48951             
48952         } else  if (this.tb.fields && this.tb.selectedNode) {
48953             this.tb.fields.each( function(e) {
48954                 if (e.stylename) {
48955                     e.setValue(this.tb.selectedNode.style[e.stylename]);
48956                     return;
48957                 } 
48958                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
48959             }, this);
48960             this.updateToolbarStyles(this.tb.selectedNode);  
48961         }
48962         
48963         
48964        
48965         Roo.menu.MenuMgr.hideAll();
48966
48967         
48968         
48969     
48970         // update the footer
48971         //
48972         this.updateFooter(ans);
48973              
48974     },
48975     
48976     updateToolbarStyles : function(sel)
48977     {
48978          var hasStyles = false;
48979         for(var i in this.styles) {
48980             hasStyles = true;
48981             break;
48982         }
48983         
48984         // update styles
48985         if (hasStyles) { 
48986             var st = this.tb.fields.item(0);
48987             
48988             st.store.removeAll();
48989             var cn = sel.className.split(/\s+/);
48990             
48991             var avs = [];
48992             if (this.styles['*']) {
48993                 
48994                 Roo.each(this.styles['*'], function(v) {
48995                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48996                 });
48997             }
48998             if (this.styles[tn]) { 
48999                 Roo.each(this.styles[tn], function(v) {
49000                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49001                 });
49002             }
49003             
49004             st.store.loadData(avs);
49005             st.collapse();
49006             st.setValue(cn);
49007         }
49008     },
49009     
49010      
49011     updateFooter : function(ans)
49012     {
49013         var html = '';
49014         if (ans === false) {
49015             this.footDisp.dom.innerHTML = '';
49016             return;
49017         }
49018         
49019         this.footerEls = ans.reverse();
49020         Roo.each(this.footerEls, function(a,i) {
49021             if (!a) { return; }
49022             html += html.length ? ' &gt; '  :  '';
49023             
49024             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49025             
49026         });
49027        
49028         // 
49029         var sz = this.footDisp.up('td').getSize();
49030         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49031         this.footDisp.dom.style.marginLeft = '5px';
49032         
49033         this.footDisp.dom.style.overflow = 'hidden';
49034         
49035         this.footDisp.dom.innerHTML = html;
49036             
49037         
49038     },
49039    
49040        
49041     // private
49042     onDestroy : function(){
49043         if(this.rendered){
49044             
49045             this.tb.items.each(function(item){
49046                 if(item.menu){
49047                     item.menu.removeAll();
49048                     if(item.menu.el){
49049                         item.menu.el.destroy();
49050                     }
49051                 }
49052                 item.destroy();
49053             });
49054              
49055         }
49056     },
49057     onFirstFocus: function() {
49058         // need to do this for all the toolbars..
49059         this.tb.items.each(function(item){
49060            item.enable();
49061         });
49062     },
49063     buildToolbar: function(tlist, nm, friendly_name)
49064     {
49065         var editor = this.editor;
49066         var editorcore = this.editorcore;
49067          // create a new element.
49068         var wdiv = editor.wrap.createChild({
49069                 tag: 'div'
49070             }, editor.wrap.dom.firstChild.nextSibling, true);
49071         
49072        
49073         var tb = new Roo.Toolbar(wdiv);
49074         tb.name = nm;
49075         
49076         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49077         
49078         var styles = Array.from(this.styles);
49079         
49080         
49081         // styles...
49082         if (styles && styles.length) {
49083             
49084             // this needs a multi-select checkbox...
49085             tb.addField( new Roo.form.ComboBox({
49086                 store: new Roo.data.SimpleStore({
49087                     id : 'val',
49088                     fields: ['val', 'selected'],
49089                     data : [] 
49090                 }),
49091                 name : '-roo-edit-className',
49092                 attrname : 'className',
49093                 displayField: 'val',
49094                 typeAhead: false,
49095                 mode: 'local',
49096                 editable : false,
49097                 triggerAction: 'all',
49098                 emptyText:'Select Style',
49099                 selectOnFocus:true,
49100                 width: 130,
49101                 listeners : {
49102                     'select': function(c, r, i) {
49103                         // initial support only for on class per el..
49104                         tb.selectedNode.className =  r ? r.get('val') : '';
49105                         editorcore.syncValue();
49106                     }
49107                 }
49108     
49109             }));
49110         }
49111         
49112         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49113         
49114         
49115         for (var i in tlist) {
49116             
49117             var item = tlist[i];
49118             tb.add(item.title + ":&nbsp;");
49119             
49120             
49121             //optname == used so you can configure the options available..
49122             var opts = item.opts ? item.opts : false;
49123             if (item.optname) { // use the b
49124                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49125            
49126             }
49127             
49128             if (opts) {
49129                 // opts == pulldown..
49130                 tb.addField( new Roo.form.ComboBox({
49131                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49132                         id : 'val',
49133                         fields: ['val', 'display'],
49134                         data : opts  
49135                     }),
49136                     name : '-roo-edit-' + i,
49137                     
49138                     attrname : i,
49139                     stylename : item.style ? item.style : false,
49140                     
49141                     displayField: item.displayField ? item.displayField : 'val',
49142                     valueField :  'val',
49143                     typeAhead: false,
49144                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
49145                     editable : false,
49146                     triggerAction: 'all',
49147                     emptyText:'Select',
49148                     selectOnFocus:true,
49149                     width: item.width ? item.width  : 130,
49150                     listeners : {
49151                         'select': function(c, r, i) {
49152                             if (tb.selectedNode.hasAttribute('data-block')) {
49153                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49154                                 b[c.attrname] = r.get('val');
49155                                 b.updateElement(tb.selectedNode);
49156                                 editorcore.syncValue();
49157                                 return;
49158                             }
49159                             
49160                             if (c.stylename) {
49161                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49162                                 editorcore.syncValue();
49163                                 return;
49164                             }
49165                             if (r === false) {
49166                                 tb.selectedNode.removeAttribute(c.attrname);
49167                                 editorcore.syncValue();
49168                                 return;
49169                             }
49170                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49171                             editorcore.syncValue();
49172                         }
49173                     }
49174
49175                 }));
49176                 continue;
49177                     
49178                  
49179                 /*
49180                 tb.addField( new Roo.form.TextField({
49181                     name: i,
49182                     width: 100,
49183                     //allowBlank:false,
49184                     value: ''
49185                 }));
49186                 continue;
49187                 */
49188             }
49189             tb.addField( new Roo.form.TextField({
49190                 name: '-roo-edit-' + i,
49191                 attrname : i,
49192                 
49193                 width: item.width,
49194                 //allowBlank:true,
49195                 value: '',
49196                 listeners: {
49197                     'change' : function(f, nv, ov) {
49198                         
49199                         if (tb.selectedNode.hasAttribute('data-block')) {
49200                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49201                             b[f.attrname] = nv;
49202                             b.updateElement(tb.selectedNode);
49203                             editorcore.syncValue();
49204                             return;
49205                         }
49206                         
49207                         tb.selectedNode.setAttribute(f.attrname, nv);
49208                         editorcore.syncValue();
49209                     }
49210                 }
49211             }));
49212              
49213         }
49214         
49215         var _this = this;
49216         
49217         if(nm == 'BODY'){
49218             tb.addSeparator();
49219         
49220             tb.addButton( {
49221                 text: 'Stylesheets',
49222
49223                 listeners : {
49224                     click : function ()
49225                     {
49226                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49227                     }
49228                 }
49229             });
49230         }
49231         
49232         tb.addFill();
49233         tb.addButton({
49234             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49235     
49236             listeners : {
49237                 click : function ()
49238                 {
49239                     // remove
49240                     // undo does not work.
49241                     var sn = tb.selectedNode;
49242                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49243                     if (sn.hasAttribute('data-block')) {
49244                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49245                         sn.parentNode.removeChild(sn);
49246                         
49247                     } else {
49248                         // remove and keep parents.
49249                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49250                         a.removeTag(sn);
49251                     }
49252                     
49253                     
49254                     var range = editorcore.createRange();
49255         
49256                     range.setStart(stn,0);
49257                     range.setEnd(stn,0); 
49258                     var selection = editorcore.getSelection();
49259                     selection.removeAllRanges();
49260                     selection.addRange(range);
49261                     
49262                     
49263                     //_this.updateToolbar(null, null, pn);
49264                     _this.updateToolbar(null, null, null);
49265                     _this.updateFooter(false);
49266                     
49267                 }
49268             }
49269             
49270                     
49271                 
49272             
49273         });
49274         
49275         
49276         tb.el.on('click', function(e){
49277             e.preventDefault(); // what does this do?
49278         });
49279         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49280         tb.el.hide();
49281         
49282         // dont need to disable them... as they will get hidden
49283         return tb;
49284          
49285         
49286     },
49287     buildFooter : function()
49288     {
49289         
49290         var fel = this.editor.wrap.createChild();
49291         this.footer = new Roo.Toolbar(fel);
49292         // toolbar has scrolly on left / right?
49293         var footDisp= new Roo.Toolbar.Fill();
49294         var _t = this;
49295         this.footer.add(
49296             {
49297                 text : '&lt;',
49298                 xtype: 'Button',
49299                 handler : function() {
49300                     _t.footDisp.scrollTo('left',0,true)
49301                 }
49302             }
49303         );
49304         this.footer.add( footDisp );
49305         this.footer.add( 
49306             {
49307                 text : '&gt;',
49308                 xtype: 'Button',
49309                 handler : function() {
49310                     // no animation..
49311                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49312                 }
49313             }
49314         );
49315         var fel = Roo.get(footDisp.el);
49316         fel.addClass('x-editor-context');
49317         this.footDispWrap = fel; 
49318         this.footDispWrap.overflow  = 'hidden';
49319         
49320         this.footDisp = fel.createChild();
49321         this.footDispWrap.on('click', this.onContextClick, this)
49322         
49323         
49324     },
49325     onContextClick : function (ev,dom)
49326     {
49327         ev.preventDefault();
49328         var  cn = dom.className;
49329         //Roo.log(cn);
49330         if (!cn.match(/x-ed-loc-/)) {
49331             return;
49332         }
49333         var n = cn.split('-').pop();
49334         var ans = this.footerEls;
49335         var sel = ans[n];
49336         
49337          // pick
49338         var range = this.editorcore.createRange();
49339         
49340         range.selectNodeContents(sel);
49341         //range.selectNode(sel);
49342         
49343         
49344         var selection = this.editorcore.getSelection();
49345         selection.removeAllRanges();
49346         selection.addRange(range);
49347         
49348         
49349         
49350         this.updateToolbar(null, null, sel);
49351         
49352         
49353     }
49354     
49355     
49356     
49357     
49358     
49359 });
49360
49361
49362
49363
49364
49365 /*
49366  * Based on:
49367  * Ext JS Library 1.1.1
49368  * Copyright(c) 2006-2007, Ext JS, LLC.
49369  *
49370  * Originally Released Under LGPL - original licence link has changed is not relivant.
49371  *
49372  * Fork - LGPL
49373  * <script type="text/javascript">
49374  */
49375  
49376 /**
49377  * @class Roo.form.BasicForm
49378  * @extends Roo.util.Observable
49379  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49380  * @constructor
49381  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49382  * @param {Object} config Configuration options
49383  */
49384 Roo.form.BasicForm = function(el, config){
49385     this.allItems = [];
49386     this.childForms = [];
49387     Roo.apply(this, config);
49388     /*
49389      * The Roo.form.Field items in this form.
49390      * @type MixedCollection
49391      */
49392      
49393      
49394     this.items = new Roo.util.MixedCollection(false, function(o){
49395         return o.id || (o.id = Roo.id());
49396     });
49397     this.addEvents({
49398         /**
49399          * @event beforeaction
49400          * Fires before any action is performed. Return false to cancel the action.
49401          * @param {Form} this
49402          * @param {Action} action The action to be performed
49403          */
49404         beforeaction: true,
49405         /**
49406          * @event actionfailed
49407          * Fires when an action fails.
49408          * @param {Form} this
49409          * @param {Action} action The action that failed
49410          */
49411         actionfailed : true,
49412         /**
49413          * @event actioncomplete
49414          * Fires when an action is completed.
49415          * @param {Form} this
49416          * @param {Action} action The action that completed
49417          */
49418         actioncomplete : true
49419     });
49420     if(el){
49421         this.initEl(el);
49422     }
49423     Roo.form.BasicForm.superclass.constructor.call(this);
49424     
49425     Roo.form.BasicForm.popover.apply();
49426 };
49427
49428 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
49429     /**
49430      * @cfg {String} method
49431      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
49432      */
49433     /**
49434      * @cfg {DataReader} reader
49435      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
49436      * This is optional as there is built-in support for processing JSON.
49437      */
49438     /**
49439      * @cfg {DataReader} errorReader
49440      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
49441      * This is completely optional as there is built-in support for processing JSON.
49442      */
49443     /**
49444      * @cfg {String} url
49445      * The URL to use for form actions if one isn't supplied in the action options.
49446      */
49447     /**
49448      * @cfg {Boolean} fileUpload
49449      * Set to true if this form is a file upload.
49450      */
49451      
49452     /**
49453      * @cfg {Object} baseParams
49454      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
49455      */
49456      /**
49457      
49458     /**
49459      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
49460      */
49461     timeout: 30,
49462
49463     // private
49464     activeAction : null,
49465
49466     /**
49467      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
49468      * or setValues() data instead of when the form was first created.
49469      */
49470     trackResetOnLoad : false,
49471     
49472     
49473     /**
49474      * childForms - used for multi-tab forms
49475      * @type {Array}
49476      */
49477     childForms : false,
49478     
49479     /**
49480      * allItems - full list of fields.
49481      * @type {Array}
49482      */
49483     allItems : false,
49484     
49485     /**
49486      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
49487      * element by passing it or its id or mask the form itself by passing in true.
49488      * @type Mixed
49489      */
49490     waitMsgTarget : false,
49491     
49492     /**
49493      * @type Boolean
49494      */
49495     disableMask : false,
49496     
49497     /**
49498      * @cfg {Boolean} errorMask (true|false) default false
49499      */
49500     errorMask : false,
49501     
49502     /**
49503      * @cfg {Number} maskOffset Default 100
49504      */
49505     maskOffset : 100,
49506
49507     // private
49508     initEl : function(el){
49509         this.el = Roo.get(el);
49510         this.id = this.el.id || Roo.id();
49511         this.el.on('submit', this.onSubmit, this);
49512         this.el.addClass('x-form');
49513     },
49514
49515     // private
49516     onSubmit : function(e){
49517         e.stopEvent();
49518     },
49519
49520     /**
49521      * Returns true if client-side validation on the form is successful.
49522      * @return Boolean
49523      */
49524     isValid : function(){
49525         var valid = true;
49526         var target = false;
49527         this.items.each(function(f){
49528             if(f.validate()){
49529                 return;
49530             }
49531             
49532             valid = false;
49533                 
49534             if(!target && f.el.isVisible(true)){
49535                 target = f;
49536             }
49537         });
49538         
49539         if(this.errorMask && !valid){
49540             Roo.form.BasicForm.popover.mask(this, target);
49541         }
49542         
49543         return valid;
49544     },
49545     /**
49546      * Returns array of invalid form fields.
49547      * @return Array
49548      */
49549     
49550     invalidFields : function()
49551     {
49552         var ret = [];
49553         this.items.each(function(f){
49554             if(f.validate()){
49555                 return;
49556             }
49557             ret.push(f);
49558             
49559         });
49560         
49561         return ret;
49562     },
49563     
49564     
49565     /**
49566      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
49567      * @return Boolean
49568      */
49569     isDirty : function(){
49570         var dirty = false;
49571         this.items.each(function(f){
49572            if(f.isDirty()){
49573                dirty = true;
49574                return false;
49575            }
49576         });
49577         return dirty;
49578     },
49579     
49580     /**
49581      * Returns true if any fields in this form have changed since their original load. (New version)
49582      * @return Boolean
49583      */
49584     
49585     hasChanged : function()
49586     {
49587         var dirty = false;
49588         this.items.each(function(f){
49589            if(f.hasChanged()){
49590                dirty = true;
49591                return false;
49592            }
49593         });
49594         return dirty;
49595         
49596     },
49597     /**
49598      * Resets all hasChanged to 'false' -
49599      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
49600      * So hasChanged storage is only to be used for this purpose
49601      * @return Boolean
49602      */
49603     resetHasChanged : function()
49604     {
49605         this.items.each(function(f){
49606            f.resetHasChanged();
49607         });
49608         
49609     },
49610     
49611     
49612     /**
49613      * Performs a predefined action (submit or load) or custom actions you define on this form.
49614      * @param {String} actionName The name of the action type
49615      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
49616      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
49617      * accept other config options):
49618      * <pre>
49619 Property          Type             Description
49620 ----------------  ---------------  ----------------------------------------------------------------------------------
49621 url               String           The url for the action (defaults to the form's url)
49622 method            String           The form method to use (defaults to the form's method, or POST if not defined)
49623 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
49624 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
49625                                    validate the form on the client (defaults to false)
49626      * </pre>
49627      * @return {BasicForm} this
49628      */
49629     doAction : function(action, options){
49630         if(typeof action == 'string'){
49631             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
49632         }
49633         if(this.fireEvent('beforeaction', this, action) !== false){
49634             this.beforeAction(action);
49635             action.run.defer(100, action);
49636         }
49637         return this;
49638     },
49639
49640     /**
49641      * Shortcut to do a submit action.
49642      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49643      * @return {BasicForm} this
49644      */
49645     submit : function(options){
49646         this.doAction('submit', options);
49647         return this;
49648     },
49649
49650     /**
49651      * Shortcut to do a load action.
49652      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49653      * @return {BasicForm} this
49654      */
49655     load : function(options){
49656         this.doAction('load', options);
49657         return this;
49658     },
49659
49660     /**
49661      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
49662      * @param {Record} record The record to edit
49663      * @return {BasicForm} this
49664      */
49665     updateRecord : function(record){
49666         record.beginEdit();
49667         var fs = record.fields;
49668         fs.each(function(f){
49669             var field = this.findField(f.name);
49670             if(field){
49671                 record.set(f.name, field.getValue());
49672             }
49673         }, this);
49674         record.endEdit();
49675         return this;
49676     },
49677
49678     /**
49679      * Loads an Roo.data.Record into this form.
49680      * @param {Record} record The record to load
49681      * @return {BasicForm} this
49682      */
49683     loadRecord : function(record){
49684         this.setValues(record.data);
49685         return this;
49686     },
49687
49688     // private
49689     beforeAction : function(action){
49690         var o = action.options;
49691         
49692         if(!this.disableMask) {
49693             if(this.waitMsgTarget === true){
49694                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
49695             }else if(this.waitMsgTarget){
49696                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
49697                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
49698             }else {
49699                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
49700             }
49701         }
49702         
49703          
49704     },
49705
49706     // private
49707     afterAction : function(action, success){
49708         this.activeAction = null;
49709         var o = action.options;
49710         
49711         if(!this.disableMask) {
49712             if(this.waitMsgTarget === true){
49713                 this.el.unmask();
49714             }else if(this.waitMsgTarget){
49715                 this.waitMsgTarget.unmask();
49716             }else{
49717                 Roo.MessageBox.updateProgress(1);
49718                 Roo.MessageBox.hide();
49719             }
49720         }
49721         
49722         if(success){
49723             if(o.reset){
49724                 this.reset();
49725             }
49726             Roo.callback(o.success, o.scope, [this, action]);
49727             this.fireEvent('actioncomplete', this, action);
49728             
49729         }else{
49730             
49731             // failure condition..
49732             // we have a scenario where updates need confirming.
49733             // eg. if a locking scenario exists..
49734             // we look for { errors : { needs_confirm : true }} in the response.
49735             if (
49736                 (typeof(action.result) != 'undefined')  &&
49737                 (typeof(action.result.errors) != 'undefined')  &&
49738                 (typeof(action.result.errors.needs_confirm) != 'undefined')
49739            ){
49740                 var _t = this;
49741                 Roo.MessageBox.confirm(
49742                     "Change requires confirmation",
49743                     action.result.errorMsg,
49744                     function(r) {
49745                         if (r != 'yes') {
49746                             return;
49747                         }
49748                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49749                     }
49750                     
49751                 );
49752                 
49753                 
49754                 
49755                 return;
49756             }
49757             
49758             Roo.callback(o.failure, o.scope, [this, action]);
49759             // show an error message if no failed handler is set..
49760             if (!this.hasListener('actionfailed')) {
49761                 Roo.MessageBox.alert("Error",
49762                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49763                         action.result.errorMsg :
49764                         "Saving Failed, please check your entries or try again"
49765                 );
49766             }
49767             
49768             this.fireEvent('actionfailed', this, action);
49769         }
49770         
49771     },
49772
49773     /**
49774      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49775      * @param {String} id The value to search for
49776      * @return Field
49777      */
49778     findField : function(id){
49779         var field = this.items.get(id);
49780         if(!field){
49781             this.items.each(function(f){
49782                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49783                     field = f;
49784                     return false;
49785                 }
49786             });
49787         }
49788         return field || null;
49789     },
49790
49791     /**
49792      * Add a secondary form to this one, 
49793      * Used to provide tabbed forms. One form is primary, with hidden values 
49794      * which mirror the elements from the other forms.
49795      * 
49796      * @param {Roo.form.Form} form to add.
49797      * 
49798      */
49799     addForm : function(form)
49800     {
49801        
49802         if (this.childForms.indexOf(form) > -1) {
49803             // already added..
49804             return;
49805         }
49806         this.childForms.push(form);
49807         var n = '';
49808         Roo.each(form.allItems, function (fe) {
49809             
49810             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49811             if (this.findField(n)) { // already added..
49812                 return;
49813             }
49814             var add = new Roo.form.Hidden({
49815                 name : n
49816             });
49817             add.render(this.el);
49818             
49819             this.add( add );
49820         }, this);
49821         
49822     },
49823     /**
49824      * Mark fields in this form invalid in bulk.
49825      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49826      * @return {BasicForm} this
49827      */
49828     markInvalid : function(errors){
49829         if(errors instanceof Array){
49830             for(var i = 0, len = errors.length; i < len; i++){
49831                 var fieldError = errors[i];
49832                 var f = this.findField(fieldError.id);
49833                 if(f){
49834                     f.markInvalid(fieldError.msg);
49835                 }
49836             }
49837         }else{
49838             var field, id;
49839             for(id in errors){
49840                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49841                     field.markInvalid(errors[id]);
49842                 }
49843             }
49844         }
49845         Roo.each(this.childForms || [], function (f) {
49846             f.markInvalid(errors);
49847         });
49848         
49849         return this;
49850     },
49851
49852     /**
49853      * Set values for fields in this form in bulk.
49854      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49855      * @return {BasicForm} this
49856      */
49857     setValues : function(values){
49858         if(values instanceof Array){ // array of objects
49859             for(var i = 0, len = values.length; i < len; i++){
49860                 var v = values[i];
49861                 var f = this.findField(v.id);
49862                 if(f){
49863                     f.setValue(v.value);
49864                     if(this.trackResetOnLoad){
49865                         f.originalValue = f.getValue();
49866                     }
49867                 }
49868             }
49869         }else{ // object hash
49870             var field, id;
49871             for(id in values){
49872                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49873                     
49874                     if (field.setFromData && 
49875                         field.valueField && 
49876                         field.displayField &&
49877                         // combos' with local stores can 
49878                         // be queried via setValue()
49879                         // to set their value..
49880                         (field.store && !field.store.isLocal)
49881                         ) {
49882                         // it's a combo
49883                         var sd = { };
49884                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49885                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49886                         field.setFromData(sd);
49887                         
49888                     } else {
49889                         field.setValue(values[id]);
49890                     }
49891                     
49892                     
49893                     if(this.trackResetOnLoad){
49894                         field.originalValue = field.getValue();
49895                     }
49896                 }
49897             }
49898         }
49899         this.resetHasChanged();
49900         
49901         
49902         Roo.each(this.childForms || [], function (f) {
49903             f.setValues(values);
49904             f.resetHasChanged();
49905         });
49906                 
49907         return this;
49908     },
49909  
49910     /**
49911      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49912      * they are returned as an array.
49913      * @param {Boolean} asString
49914      * @return {Object}
49915      */
49916     getValues : function(asString){
49917         if (this.childForms) {
49918             // copy values from the child forms
49919             Roo.each(this.childForms, function (f) {
49920                 this.setValues(f.getValues());
49921             }, this);
49922         }
49923         
49924         // use formdata
49925         if (typeof(FormData) != 'undefined' && asString !== true) {
49926             // this relies on a 'recent' version of chrome apparently...
49927             try {
49928                 var fd = (new FormData(this.el.dom)).entries();
49929                 var ret = {};
49930                 var ent = fd.next();
49931                 while (!ent.done) {
49932                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49933                     ent = fd.next();
49934                 };
49935                 return ret;
49936             } catch(e) {
49937                 
49938             }
49939             
49940         }
49941         
49942         
49943         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49944         if(asString === true){
49945             return fs;
49946         }
49947         return Roo.urlDecode(fs);
49948     },
49949     
49950     /**
49951      * Returns the fields in this form as an object with key/value pairs. 
49952      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49953      * @return {Object}
49954      */
49955     getFieldValues : function(with_hidden)
49956     {
49957         if (this.childForms) {
49958             // copy values from the child forms
49959             // should this call getFieldValues - probably not as we do not currently copy
49960             // hidden fields when we generate..
49961             Roo.each(this.childForms, function (f) {
49962                 this.setValues(f.getValues());
49963             }, this);
49964         }
49965         
49966         var ret = {};
49967         this.items.each(function(f){
49968             if (!f.getName()) {
49969                 return;
49970             }
49971             var v = f.getValue();
49972             if (f.inputType =='radio') {
49973                 if (typeof(ret[f.getName()]) == 'undefined') {
49974                     ret[f.getName()] = ''; // empty..
49975                 }
49976                 
49977                 if (!f.el.dom.checked) {
49978                     return;
49979                     
49980                 }
49981                 v = f.el.dom.value;
49982                 
49983             }
49984             
49985             // not sure if this supported any more..
49986             if ((typeof(v) == 'object') && f.getRawValue) {
49987                 v = f.getRawValue() ; // dates..
49988             }
49989             // combo boxes where name != hiddenName...
49990             if (f.name != f.getName()) {
49991                 ret[f.name] = f.getRawValue();
49992             }
49993             ret[f.getName()] = v;
49994         });
49995         
49996         return ret;
49997     },
49998
49999     /**
50000      * Clears all invalid messages in this form.
50001      * @return {BasicForm} this
50002      */
50003     clearInvalid : function(){
50004         this.items.each(function(f){
50005            f.clearInvalid();
50006         });
50007         
50008         Roo.each(this.childForms || [], function (f) {
50009             f.clearInvalid();
50010         });
50011         
50012         
50013         return this;
50014     },
50015
50016     /**
50017      * Resets this form.
50018      * @return {BasicForm} this
50019      */
50020     reset : function(){
50021         this.items.each(function(f){
50022             f.reset();
50023         });
50024         
50025         Roo.each(this.childForms || [], function (f) {
50026             f.reset();
50027         });
50028         this.resetHasChanged();
50029         
50030         return this;
50031     },
50032
50033     /**
50034      * Add Roo.form components to this form.
50035      * @param {Field} field1
50036      * @param {Field} field2 (optional)
50037      * @param {Field} etc (optional)
50038      * @return {BasicForm} this
50039      */
50040     add : function(){
50041         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50042         return this;
50043     },
50044
50045
50046     /**
50047      * Removes a field from the items collection (does NOT remove its markup).
50048      * @param {Field} field
50049      * @return {BasicForm} this
50050      */
50051     remove : function(field){
50052         this.items.remove(field);
50053         return this;
50054     },
50055
50056     /**
50057      * Looks at the fields in this form, checks them for an id attribute,
50058      * and calls applyTo on the existing dom element with that id.
50059      * @return {BasicForm} this
50060      */
50061     render : function(){
50062         this.items.each(function(f){
50063             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50064                 f.applyTo(f.id);
50065             }
50066         });
50067         return this;
50068     },
50069
50070     /**
50071      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50072      * @param {Object} values
50073      * @return {BasicForm} this
50074      */
50075     applyToFields : function(o){
50076         this.items.each(function(f){
50077            Roo.apply(f, o);
50078         });
50079         return this;
50080     },
50081
50082     /**
50083      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50084      * @param {Object} values
50085      * @return {BasicForm} this
50086      */
50087     applyIfToFields : function(o){
50088         this.items.each(function(f){
50089            Roo.applyIf(f, o);
50090         });
50091         return this;
50092     }
50093 });
50094
50095 // back compat
50096 Roo.BasicForm = Roo.form.BasicForm;
50097
50098 Roo.apply(Roo.form.BasicForm, {
50099     
50100     popover : {
50101         
50102         padding : 5,
50103         
50104         isApplied : false,
50105         
50106         isMasked : false,
50107         
50108         form : false,
50109         
50110         target : false,
50111         
50112         intervalID : false,
50113         
50114         maskEl : false,
50115         
50116         apply : function()
50117         {
50118             if(this.isApplied){
50119                 return;
50120             }
50121             
50122             this.maskEl = {
50123                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50124                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50125                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50126                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50127             };
50128             
50129             this.maskEl.top.enableDisplayMode("block");
50130             this.maskEl.left.enableDisplayMode("block");
50131             this.maskEl.bottom.enableDisplayMode("block");
50132             this.maskEl.right.enableDisplayMode("block");
50133             
50134             Roo.get(document.body).on('click', function(){
50135                 this.unmask();
50136             }, this);
50137             
50138             Roo.get(document.body).on('touchstart', function(){
50139                 this.unmask();
50140             }, this);
50141             
50142             this.isApplied = true
50143         },
50144         
50145         mask : function(form, target)
50146         {
50147             this.form = form;
50148             
50149             this.target = target;
50150             
50151             if(!this.form.errorMask || !target.el){
50152                 return;
50153             }
50154             
50155             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50156             
50157             var ot = this.target.el.calcOffsetsTo(scrollable);
50158             
50159             var scrollTo = ot[1] - this.form.maskOffset;
50160             
50161             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50162             
50163             scrollable.scrollTo('top', scrollTo);
50164             
50165             var el = this.target.wrap || this.target.el;
50166             
50167             var box = el.getBox();
50168             
50169             this.maskEl.top.setStyle('position', 'absolute');
50170             this.maskEl.top.setStyle('z-index', 10000);
50171             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50172             this.maskEl.top.setLeft(0);
50173             this.maskEl.top.setTop(0);
50174             this.maskEl.top.show();
50175             
50176             this.maskEl.left.setStyle('position', 'absolute');
50177             this.maskEl.left.setStyle('z-index', 10000);
50178             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50179             this.maskEl.left.setLeft(0);
50180             this.maskEl.left.setTop(box.y - this.padding);
50181             this.maskEl.left.show();
50182
50183             this.maskEl.bottom.setStyle('position', 'absolute');
50184             this.maskEl.bottom.setStyle('z-index', 10000);
50185             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50186             this.maskEl.bottom.setLeft(0);
50187             this.maskEl.bottom.setTop(box.bottom + this.padding);
50188             this.maskEl.bottom.show();
50189
50190             this.maskEl.right.setStyle('position', 'absolute');
50191             this.maskEl.right.setStyle('z-index', 10000);
50192             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50193             this.maskEl.right.setLeft(box.right + this.padding);
50194             this.maskEl.right.setTop(box.y - this.padding);
50195             this.maskEl.right.show();
50196
50197             this.intervalID = window.setInterval(function() {
50198                 Roo.form.BasicForm.popover.unmask();
50199             }, 10000);
50200
50201             window.onwheel = function(){ return false;};
50202             
50203             (function(){ this.isMasked = true; }).defer(500, this);
50204             
50205         },
50206         
50207         unmask : function()
50208         {
50209             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50210                 return;
50211             }
50212             
50213             this.maskEl.top.setStyle('position', 'absolute');
50214             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50215             this.maskEl.top.hide();
50216
50217             this.maskEl.left.setStyle('position', 'absolute');
50218             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50219             this.maskEl.left.hide();
50220
50221             this.maskEl.bottom.setStyle('position', 'absolute');
50222             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50223             this.maskEl.bottom.hide();
50224
50225             this.maskEl.right.setStyle('position', 'absolute');
50226             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50227             this.maskEl.right.hide();
50228             
50229             window.onwheel = function(){ return true;};
50230             
50231             if(this.intervalID){
50232                 window.clearInterval(this.intervalID);
50233                 this.intervalID = false;
50234             }
50235             
50236             this.isMasked = false;
50237             
50238         }
50239         
50240     }
50241     
50242 });/*
50243  * Based on:
50244  * Ext JS Library 1.1.1
50245  * Copyright(c) 2006-2007, Ext JS, LLC.
50246  *
50247  * Originally Released Under LGPL - original licence link has changed is not relivant.
50248  *
50249  * Fork - LGPL
50250  * <script type="text/javascript">
50251  */
50252
50253 /**
50254  * @class Roo.form.Form
50255  * @extends Roo.form.BasicForm
50256  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50257  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50258  * @constructor
50259  * @param {Object} config Configuration options
50260  */
50261 Roo.form.Form = function(config){
50262     var xitems =  [];
50263     if (config.items) {
50264         xitems = config.items;
50265         delete config.items;
50266     }
50267    
50268     
50269     Roo.form.Form.superclass.constructor.call(this, null, config);
50270     this.url = this.url || this.action;
50271     if(!this.root){
50272         this.root = new Roo.form.Layout(Roo.applyIf({
50273             id: Roo.id()
50274         }, config));
50275     }
50276     this.active = this.root;
50277     /**
50278      * Array of all the buttons that have been added to this form via {@link addButton}
50279      * @type Array
50280      */
50281     this.buttons = [];
50282     this.allItems = [];
50283     this.addEvents({
50284         /**
50285          * @event clientvalidation
50286          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50287          * @param {Form} this
50288          * @param {Boolean} valid true if the form has passed client-side validation
50289          */
50290         clientvalidation: true,
50291         /**
50292          * @event rendered
50293          * Fires when the form is rendered
50294          * @param {Roo.form.Form} form
50295          */
50296         rendered : true
50297     });
50298     
50299     if (this.progressUrl) {
50300             // push a hidden field onto the list of fields..
50301             this.addxtype( {
50302                     xns: Roo.form, 
50303                     xtype : 'Hidden', 
50304                     name : 'UPLOAD_IDENTIFIER' 
50305             });
50306         }
50307         
50308     
50309     Roo.each(xitems, this.addxtype, this);
50310     
50311 };
50312
50313 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50314      /**
50315      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50316      */
50317     
50318     /**
50319      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50320      */
50321     /**
50322      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50323      */
50324     /**
50325      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50326      */
50327     buttonAlign:'center',
50328
50329     /**
50330      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50331      */
50332     minButtonWidth:75,
50333
50334     /**
50335      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50336      * This property cascades to child containers if not set.
50337      */
50338     labelAlign:'left',
50339
50340     /**
50341      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50342      * fires a looping event with that state. This is required to bind buttons to the valid
50343      * state using the config value formBind:true on the button.
50344      */
50345     monitorValid : false,
50346
50347     /**
50348      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50349      */
50350     monitorPoll : 200,
50351     
50352     /**
50353      * @cfg {String} progressUrl - Url to return progress data 
50354      */
50355     
50356     progressUrl : false,
50357     /**
50358      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50359      * sending a formdata with extra parameters - eg uploaded elements.
50360      */
50361     
50362     formData : false,
50363     
50364     /**
50365      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50366      * fields are added and the column is closed. If no fields are passed the column remains open
50367      * until end() is called.
50368      * @param {Object} config The config to pass to the column
50369      * @param {Field} field1 (optional)
50370      * @param {Field} field2 (optional)
50371      * @param {Field} etc (optional)
50372      * @return Column The column container object
50373      */
50374     column : function(c){
50375         var col = new Roo.form.Column(c);
50376         this.start(col);
50377         if(arguments.length > 1){ // duplicate code required because of Opera
50378             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50379             this.end();
50380         }
50381         return col;
50382     },
50383
50384     /**
50385      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50386      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50387      * until end() is called.
50388      * @param {Object} config The config to pass to the fieldset
50389      * @param {Field} field1 (optional)
50390      * @param {Field} field2 (optional)
50391      * @param {Field} etc (optional)
50392      * @return FieldSet The fieldset container object
50393      */
50394     fieldset : function(c){
50395         var fs = new Roo.form.FieldSet(c);
50396         this.start(fs);
50397         if(arguments.length > 1){ // duplicate code required because of Opera
50398             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50399             this.end();
50400         }
50401         return fs;
50402     },
50403
50404     /**
50405      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50406      * fields are added and the container is closed. If no fields are passed the container remains open
50407      * until end() is called.
50408      * @param {Object} config The config to pass to the Layout
50409      * @param {Field} field1 (optional)
50410      * @param {Field} field2 (optional)
50411      * @param {Field} etc (optional)
50412      * @return Layout The container object
50413      */
50414     container : function(c){
50415         var l = new Roo.form.Layout(c);
50416         this.start(l);
50417         if(arguments.length > 1){ // duplicate code required because of Opera
50418             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50419             this.end();
50420         }
50421         return l;
50422     },
50423
50424     /**
50425      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
50426      * @param {Object} container A Roo.form.Layout or subclass of Layout
50427      * @return {Form} this
50428      */
50429     start : function(c){
50430         // cascade label info
50431         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
50432         this.active.stack.push(c);
50433         c.ownerCt = this.active;
50434         this.active = c;
50435         return this;
50436     },
50437
50438     /**
50439      * Closes the current open container
50440      * @return {Form} this
50441      */
50442     end : function(){
50443         if(this.active == this.root){
50444             return this;
50445         }
50446         this.active = this.active.ownerCt;
50447         return this;
50448     },
50449
50450     /**
50451      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
50452      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
50453      * as the label of the field.
50454      * @param {Field} field1
50455      * @param {Field} field2 (optional)
50456      * @param {Field} etc. (optional)
50457      * @return {Form} this
50458      */
50459     add : function(){
50460         this.active.stack.push.apply(this.active.stack, arguments);
50461         this.allItems.push.apply(this.allItems,arguments);
50462         var r = [];
50463         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
50464             if(a[i].isFormField){
50465                 r.push(a[i]);
50466             }
50467         }
50468         if(r.length > 0){
50469             Roo.form.Form.superclass.add.apply(this, r);
50470         }
50471         return this;
50472     },
50473     
50474
50475     
50476     
50477     
50478      /**
50479      * Find any element that has been added to a form, using it's ID or name
50480      * This can include framesets, columns etc. along with regular fields..
50481      * @param {String} id - id or name to find.
50482      
50483      * @return {Element} e - or false if nothing found.
50484      */
50485     findbyId : function(id)
50486     {
50487         var ret = false;
50488         if (!id) {
50489             return ret;
50490         }
50491         Roo.each(this.allItems, function(f){
50492             if (f.id == id || f.name == id ){
50493                 ret = f;
50494                 return false;
50495             }
50496         });
50497         return ret;
50498     },
50499
50500     
50501     
50502     /**
50503      * Render this form into the passed container. This should only be called once!
50504      * @param {String/HTMLElement/Element} container The element this component should be rendered into
50505      * @return {Form} this
50506      */
50507     render : function(ct)
50508     {
50509         
50510         
50511         
50512         ct = Roo.get(ct);
50513         var o = this.autoCreate || {
50514             tag: 'form',
50515             method : this.method || 'POST',
50516             id : this.id || Roo.id()
50517         };
50518         this.initEl(ct.createChild(o));
50519
50520         this.root.render(this.el);
50521         
50522        
50523              
50524         this.items.each(function(f){
50525             f.render('x-form-el-'+f.id);
50526         });
50527
50528         if(this.buttons.length > 0){
50529             // tables are required to maintain order and for correct IE layout
50530             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
50531                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
50532                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
50533             }}, null, true);
50534             var tr = tb.getElementsByTagName('tr')[0];
50535             for(var i = 0, len = this.buttons.length; i < len; i++) {
50536                 var b = this.buttons[i];
50537                 var td = document.createElement('td');
50538                 td.className = 'x-form-btn-td';
50539                 b.render(tr.appendChild(td));
50540             }
50541         }
50542         if(this.monitorValid){ // initialize after render
50543             this.startMonitoring();
50544         }
50545         this.fireEvent('rendered', this);
50546         return this;
50547     },
50548
50549     /**
50550      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
50551      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
50552      * object or a valid Roo.DomHelper element config
50553      * @param {Function} handler The function called when the button is clicked
50554      * @param {Object} scope (optional) The scope of the handler function
50555      * @return {Roo.Button}
50556      */
50557     addButton : function(config, handler, scope){
50558         var bc = {
50559             handler: handler,
50560             scope: scope,
50561             minWidth: this.minButtonWidth,
50562             hideParent:true
50563         };
50564         if(typeof config == "string"){
50565             bc.text = config;
50566         }else{
50567             Roo.apply(bc, config);
50568         }
50569         var btn = new Roo.Button(null, bc);
50570         this.buttons.push(btn);
50571         return btn;
50572     },
50573
50574      /**
50575      * Adds a series of form elements (using the xtype property as the factory method.
50576      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
50577      * @param {Object} config 
50578      */
50579     
50580     addxtype : function()
50581     {
50582         var ar = Array.prototype.slice.call(arguments, 0);
50583         var ret = false;
50584         for(var i = 0; i < ar.length; i++) {
50585             if (!ar[i]) {
50586                 continue; // skip -- if this happends something invalid got sent, we 
50587                 // should ignore it, as basically that interface element will not show up
50588                 // and that should be pretty obvious!!
50589             }
50590             
50591             if (Roo.form[ar[i].xtype]) {
50592                 ar[i].form = this;
50593                 var fe = Roo.factory(ar[i], Roo.form);
50594                 if (!ret) {
50595                     ret = fe;
50596                 }
50597                 fe.form = this;
50598                 if (fe.store) {
50599                     fe.store.form = this;
50600                 }
50601                 if (fe.isLayout) {  
50602                          
50603                     this.start(fe);
50604                     this.allItems.push(fe);
50605                     if (fe.items && fe.addxtype) {
50606                         fe.addxtype.apply(fe, fe.items);
50607                         delete fe.items;
50608                     }
50609                      this.end();
50610                     continue;
50611                 }
50612                 
50613                 
50614                  
50615                 this.add(fe);
50616               //  console.log('adding ' + ar[i].xtype);
50617             }
50618             if (ar[i].xtype == 'Button') {  
50619                 //console.log('adding button');
50620                 //console.log(ar[i]);
50621                 this.addButton(ar[i]);
50622                 this.allItems.push(fe);
50623                 continue;
50624             }
50625             
50626             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
50627                 alert('end is not supported on xtype any more, use items');
50628             //    this.end();
50629             //    //console.log('adding end');
50630             }
50631             
50632         }
50633         return ret;
50634     },
50635     
50636     /**
50637      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
50638      * option "monitorValid"
50639      */
50640     startMonitoring : function(){
50641         if(!this.bound){
50642             this.bound = true;
50643             Roo.TaskMgr.start({
50644                 run : this.bindHandler,
50645                 interval : this.monitorPoll || 200,
50646                 scope: this
50647             });
50648         }
50649     },
50650
50651     /**
50652      * Stops monitoring of the valid state of this form
50653      */
50654     stopMonitoring : function(){
50655         this.bound = false;
50656     },
50657
50658     // private
50659     bindHandler : function(){
50660         if(!this.bound){
50661             return false; // stops binding
50662         }
50663         var valid = true;
50664         this.items.each(function(f){
50665             if(!f.isValid(true)){
50666                 valid = false;
50667                 return false;
50668             }
50669         });
50670         for(var i = 0, len = this.buttons.length; i < len; i++){
50671             var btn = this.buttons[i];
50672             if(btn.formBind === true && btn.disabled === valid){
50673                 btn.setDisabled(!valid);
50674             }
50675         }
50676         this.fireEvent('clientvalidation', this, valid);
50677     }
50678     
50679     
50680     
50681     
50682     
50683     
50684     
50685     
50686 });
50687
50688
50689 // back compat
50690 Roo.Form = Roo.form.Form;
50691 /*
50692  * Based on:
50693  * Ext JS Library 1.1.1
50694  * Copyright(c) 2006-2007, Ext JS, LLC.
50695  *
50696  * Originally Released Under LGPL - original licence link has changed is not relivant.
50697  *
50698  * Fork - LGPL
50699  * <script type="text/javascript">
50700  */
50701
50702 // as we use this in bootstrap.
50703 Roo.namespace('Roo.form');
50704  /**
50705  * @class Roo.form.Action
50706  * Internal Class used to handle form actions
50707  * @constructor
50708  * @param {Roo.form.BasicForm} el The form element or its id
50709  * @param {Object} config Configuration options
50710  */
50711
50712  
50713  
50714 // define the action interface
50715 Roo.form.Action = function(form, options){
50716     this.form = form;
50717     this.options = options || {};
50718 };
50719 /**
50720  * Client Validation Failed
50721  * @const 
50722  */
50723 Roo.form.Action.CLIENT_INVALID = 'client';
50724 /**
50725  * Server Validation Failed
50726  * @const 
50727  */
50728 Roo.form.Action.SERVER_INVALID = 'server';
50729  /**
50730  * Connect to Server Failed
50731  * @const 
50732  */
50733 Roo.form.Action.CONNECT_FAILURE = 'connect';
50734 /**
50735  * Reading Data from Server Failed
50736  * @const 
50737  */
50738 Roo.form.Action.LOAD_FAILURE = 'load';
50739
50740 Roo.form.Action.prototype = {
50741     type : 'default',
50742     failureType : undefined,
50743     response : undefined,
50744     result : undefined,
50745
50746     // interface method
50747     run : function(options){
50748
50749     },
50750
50751     // interface method
50752     success : function(response){
50753
50754     },
50755
50756     // interface method
50757     handleResponse : function(response){
50758
50759     },
50760
50761     // default connection failure
50762     failure : function(response){
50763         
50764         this.response = response;
50765         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50766         this.form.afterAction(this, false);
50767     },
50768
50769     processResponse : function(response){
50770         this.response = response;
50771         if(!response.responseText){
50772             return true;
50773         }
50774         this.result = this.handleResponse(response);
50775         return this.result;
50776     },
50777
50778     // utility functions used internally
50779     getUrl : function(appendParams){
50780         var url = this.options.url || this.form.url || this.form.el.dom.action;
50781         if(appendParams){
50782             var p = this.getParams();
50783             if(p){
50784                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50785             }
50786         }
50787         return url;
50788     },
50789
50790     getMethod : function(){
50791         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50792     },
50793
50794     getParams : function(){
50795         var bp = this.form.baseParams;
50796         var p = this.options.params;
50797         if(p){
50798             if(typeof p == "object"){
50799                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50800             }else if(typeof p == 'string' && bp){
50801                 p += '&' + Roo.urlEncode(bp);
50802             }
50803         }else if(bp){
50804             p = Roo.urlEncode(bp);
50805         }
50806         return p;
50807     },
50808
50809     createCallback : function(){
50810         return {
50811             success: this.success,
50812             failure: this.failure,
50813             scope: this,
50814             timeout: (this.form.timeout*1000),
50815             upload: this.form.fileUpload ? this.success : undefined
50816         };
50817     }
50818 };
50819
50820 Roo.form.Action.Submit = function(form, options){
50821     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50822 };
50823
50824 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50825     type : 'submit',
50826
50827     haveProgress : false,
50828     uploadComplete : false,
50829     
50830     // uploadProgress indicator.
50831     uploadProgress : function()
50832     {
50833         if (!this.form.progressUrl) {
50834             return;
50835         }
50836         
50837         if (!this.haveProgress) {
50838             Roo.MessageBox.progress("Uploading", "Uploading");
50839         }
50840         if (this.uploadComplete) {
50841            Roo.MessageBox.hide();
50842            return;
50843         }
50844         
50845         this.haveProgress = true;
50846    
50847         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50848         
50849         var c = new Roo.data.Connection();
50850         c.request({
50851             url : this.form.progressUrl,
50852             params: {
50853                 id : uid
50854             },
50855             method: 'GET',
50856             success : function(req){
50857                //console.log(data);
50858                 var rdata = false;
50859                 var edata;
50860                 try  {
50861                    rdata = Roo.decode(req.responseText)
50862                 } catch (e) {
50863                     Roo.log("Invalid data from server..");
50864                     Roo.log(edata);
50865                     return;
50866                 }
50867                 if (!rdata || !rdata.success) {
50868                     Roo.log(rdata);
50869                     Roo.MessageBox.alert(Roo.encode(rdata));
50870                     return;
50871                 }
50872                 var data = rdata.data;
50873                 
50874                 if (this.uploadComplete) {
50875                    Roo.MessageBox.hide();
50876                    return;
50877                 }
50878                    
50879                 if (data){
50880                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50881                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50882                     );
50883                 }
50884                 this.uploadProgress.defer(2000,this);
50885             },
50886        
50887             failure: function(data) {
50888                 Roo.log('progress url failed ');
50889                 Roo.log(data);
50890             },
50891             scope : this
50892         });
50893            
50894     },
50895     
50896     
50897     run : function()
50898     {
50899         // run get Values on the form, so it syncs any secondary forms.
50900         this.form.getValues();
50901         
50902         var o = this.options;
50903         var method = this.getMethod();
50904         var isPost = method == 'POST';
50905         if(o.clientValidation === false || this.form.isValid()){
50906             
50907             if (this.form.progressUrl) {
50908                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50909                     (new Date() * 1) + '' + Math.random());
50910                     
50911             } 
50912             
50913             
50914             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50915                 form:this.form.el.dom,
50916                 url:this.getUrl(!isPost),
50917                 method: method,
50918                 params:isPost ? this.getParams() : null,
50919                 isUpload: this.form.fileUpload,
50920                 formData : this.form.formData
50921             }));
50922             
50923             this.uploadProgress();
50924
50925         }else if (o.clientValidation !== false){ // client validation failed
50926             this.failureType = Roo.form.Action.CLIENT_INVALID;
50927             this.form.afterAction(this, false);
50928         }
50929     },
50930
50931     success : function(response)
50932     {
50933         this.uploadComplete= true;
50934         if (this.haveProgress) {
50935             Roo.MessageBox.hide();
50936         }
50937         
50938         
50939         var result = this.processResponse(response);
50940         if(result === true || result.success){
50941             this.form.afterAction(this, true);
50942             return;
50943         }
50944         if(result.errors){
50945             this.form.markInvalid(result.errors);
50946             this.failureType = Roo.form.Action.SERVER_INVALID;
50947         }
50948         this.form.afterAction(this, false);
50949     },
50950     failure : function(response)
50951     {
50952         this.uploadComplete= true;
50953         if (this.haveProgress) {
50954             Roo.MessageBox.hide();
50955         }
50956         
50957         this.response = response;
50958         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50959         this.form.afterAction(this, false);
50960     },
50961     
50962     handleResponse : function(response){
50963         if(this.form.errorReader){
50964             var rs = this.form.errorReader.read(response);
50965             var errors = [];
50966             if(rs.records){
50967                 for(var i = 0, len = rs.records.length; i < len; i++) {
50968                     var r = rs.records[i];
50969                     errors[i] = r.data;
50970                 }
50971             }
50972             if(errors.length < 1){
50973                 errors = null;
50974             }
50975             return {
50976                 success : rs.success,
50977                 errors : errors
50978             };
50979         }
50980         var ret = false;
50981         try {
50982             ret = Roo.decode(response.responseText);
50983         } catch (e) {
50984             ret = {
50985                 success: false,
50986                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50987                 errors : []
50988             };
50989         }
50990         return ret;
50991         
50992     }
50993 });
50994
50995
50996 Roo.form.Action.Load = function(form, options){
50997     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50998     this.reader = this.form.reader;
50999 };
51000
51001 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51002     type : 'load',
51003
51004     run : function(){
51005         
51006         Roo.Ajax.request(Roo.apply(
51007                 this.createCallback(), {
51008                     method:this.getMethod(),
51009                     url:this.getUrl(false),
51010                     params:this.getParams()
51011         }));
51012     },
51013
51014     success : function(response){
51015         
51016         var result = this.processResponse(response);
51017         if(result === true || !result.success || !result.data){
51018             this.failureType = Roo.form.Action.LOAD_FAILURE;
51019             this.form.afterAction(this, false);
51020             return;
51021         }
51022         this.form.clearInvalid();
51023         this.form.setValues(result.data);
51024         this.form.afterAction(this, true);
51025     },
51026
51027     handleResponse : function(response){
51028         if(this.form.reader){
51029             var rs = this.form.reader.read(response);
51030             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51031             return {
51032                 success : rs.success,
51033                 data : data
51034             };
51035         }
51036         return Roo.decode(response.responseText);
51037     }
51038 });
51039
51040 Roo.form.Action.ACTION_TYPES = {
51041     'load' : Roo.form.Action.Load,
51042     'submit' : Roo.form.Action.Submit
51043 };/*
51044  * Based on:
51045  * Ext JS Library 1.1.1
51046  * Copyright(c) 2006-2007, Ext JS, LLC.
51047  *
51048  * Originally Released Under LGPL - original licence link has changed is not relivant.
51049  *
51050  * Fork - LGPL
51051  * <script type="text/javascript">
51052  */
51053  
51054 /**
51055  * @class Roo.form.Layout
51056  * @extends Roo.Component
51057  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51058  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51059  * @constructor
51060  * @param {Object} config Configuration options
51061  */
51062 Roo.form.Layout = function(config){
51063     var xitems = [];
51064     if (config.items) {
51065         xitems = config.items;
51066         delete config.items;
51067     }
51068     Roo.form.Layout.superclass.constructor.call(this, config);
51069     this.stack = [];
51070     Roo.each(xitems, this.addxtype, this);
51071      
51072 };
51073
51074 Roo.extend(Roo.form.Layout, Roo.Component, {
51075     /**
51076      * @cfg {String/Object} autoCreate
51077      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51078      */
51079     /**
51080      * @cfg {String/Object/Function} style
51081      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51082      * a function which returns such a specification.
51083      */
51084     /**
51085      * @cfg {String} labelAlign
51086      * Valid values are "left," "top" and "right" (defaults to "left")
51087      */
51088     /**
51089      * @cfg {Number} labelWidth
51090      * Fixed width in pixels of all field labels (defaults to undefined)
51091      */
51092     /**
51093      * @cfg {Boolean} clear
51094      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51095      */
51096     clear : true,
51097     /**
51098      * @cfg {String} labelSeparator
51099      * The separator to use after field labels (defaults to ':')
51100      */
51101     labelSeparator : ':',
51102     /**
51103      * @cfg {Boolean} hideLabels
51104      * True to suppress the display of field labels in this layout (defaults to false)
51105      */
51106     hideLabels : false,
51107
51108     // private
51109     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51110     
51111     isLayout : true,
51112     
51113     // private
51114     onRender : function(ct, position){
51115         if(this.el){ // from markup
51116             this.el = Roo.get(this.el);
51117         }else {  // generate
51118             var cfg = this.getAutoCreate();
51119             this.el = ct.createChild(cfg, position);
51120         }
51121         if(this.style){
51122             this.el.applyStyles(this.style);
51123         }
51124         if(this.labelAlign){
51125             this.el.addClass('x-form-label-'+this.labelAlign);
51126         }
51127         if(this.hideLabels){
51128             this.labelStyle = "display:none";
51129             this.elementStyle = "padding-left:0;";
51130         }else{
51131             if(typeof this.labelWidth == 'number'){
51132                 this.labelStyle = "width:"+this.labelWidth+"px;";
51133                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51134             }
51135             if(this.labelAlign == 'top'){
51136                 this.labelStyle = "width:auto;";
51137                 this.elementStyle = "padding-left:0;";
51138             }
51139         }
51140         var stack = this.stack;
51141         var slen = stack.length;
51142         if(slen > 0){
51143             if(!this.fieldTpl){
51144                 var t = new Roo.Template(
51145                     '<div class="x-form-item {5}">',
51146                         '<label for="{0}" style="{2}">{1}{4}</label>',
51147                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51148                         '</div>',
51149                     '</div><div class="x-form-clear-left"></div>'
51150                 );
51151                 t.disableFormats = true;
51152                 t.compile();
51153                 Roo.form.Layout.prototype.fieldTpl = t;
51154             }
51155             for(var i = 0; i < slen; i++) {
51156                 if(stack[i].isFormField){
51157                     this.renderField(stack[i]);
51158                 }else{
51159                     this.renderComponent(stack[i]);
51160                 }
51161             }
51162         }
51163         if(this.clear){
51164             this.el.createChild({cls:'x-form-clear'});
51165         }
51166     },
51167
51168     // private
51169     renderField : function(f){
51170         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51171                f.id, //0
51172                f.fieldLabel, //1
51173                f.labelStyle||this.labelStyle||'', //2
51174                this.elementStyle||'', //3
51175                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51176                f.itemCls||this.itemCls||''  //5
51177        ], true).getPrevSibling());
51178     },
51179
51180     // private
51181     renderComponent : function(c){
51182         c.render(c.isLayout ? this.el : this.el.createChild());    
51183     },
51184     /**
51185      * Adds a object form elements (using the xtype property as the factory method.)
51186      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51187      * @param {Object} config 
51188      */
51189     addxtype : function(o)
51190     {
51191         // create the lement.
51192         o.form = this.form;
51193         var fe = Roo.factory(o, Roo.form);
51194         this.form.allItems.push(fe);
51195         this.stack.push(fe);
51196         
51197         if (fe.isFormField) {
51198             this.form.items.add(fe);
51199         }
51200          
51201         return fe;
51202     }
51203 });
51204
51205 /**
51206  * @class Roo.form.Column
51207  * @extends Roo.form.Layout
51208  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51209  * @constructor
51210  * @param {Object} config Configuration options
51211  */
51212 Roo.form.Column = function(config){
51213     Roo.form.Column.superclass.constructor.call(this, config);
51214 };
51215
51216 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51217     /**
51218      * @cfg {Number/String} width
51219      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51220      */
51221     /**
51222      * @cfg {String/Object} autoCreate
51223      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51224      */
51225
51226     // private
51227     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51228
51229     // private
51230     onRender : function(ct, position){
51231         Roo.form.Column.superclass.onRender.call(this, ct, position);
51232         if(this.width){
51233             this.el.setWidth(this.width);
51234         }
51235     }
51236 });
51237
51238
51239 /**
51240  * @class Roo.form.Row
51241  * @extends Roo.form.Layout
51242  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51243  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51244  * @constructor
51245  * @param {Object} config Configuration options
51246  */
51247
51248  
51249 Roo.form.Row = function(config){
51250     Roo.form.Row.superclass.constructor.call(this, config);
51251 };
51252  
51253 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51254       /**
51255      * @cfg {Number/String} width
51256      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51257      */
51258     /**
51259      * @cfg {Number/String} height
51260      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51261      */
51262     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51263     
51264     padWidth : 20,
51265     // private
51266     onRender : function(ct, position){
51267         //console.log('row render');
51268         if(!this.rowTpl){
51269             var t = new Roo.Template(
51270                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51271                     '<label for="{0}" style="{2}">{1}{4}</label>',
51272                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51273                     '</div>',
51274                 '</div>'
51275             );
51276             t.disableFormats = true;
51277             t.compile();
51278             Roo.form.Layout.prototype.rowTpl = t;
51279         }
51280         this.fieldTpl = this.rowTpl;
51281         
51282         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51283         var labelWidth = 100;
51284         
51285         if ((this.labelAlign != 'top')) {
51286             if (typeof this.labelWidth == 'number') {
51287                 labelWidth = this.labelWidth
51288             }
51289             this.padWidth =  20 + labelWidth;
51290             
51291         }
51292         
51293         Roo.form.Column.superclass.onRender.call(this, ct, position);
51294         if(this.width){
51295             this.el.setWidth(this.width);
51296         }
51297         if(this.height){
51298             this.el.setHeight(this.height);
51299         }
51300     },
51301     
51302     // private
51303     renderField : function(f){
51304         f.fieldEl = this.fieldTpl.append(this.el, [
51305                f.id, f.fieldLabel,
51306                f.labelStyle||this.labelStyle||'',
51307                this.elementStyle||'',
51308                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51309                f.itemCls||this.itemCls||'',
51310                f.width ? f.width + this.padWidth : 160 + this.padWidth
51311        ],true);
51312     }
51313 });
51314  
51315
51316 /**
51317  * @class Roo.form.FieldSet
51318  * @extends Roo.form.Layout
51319  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51320  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51321  * @constructor
51322  * @param {Object} config Configuration options
51323  */
51324 Roo.form.FieldSet = function(config){
51325     Roo.form.FieldSet.superclass.constructor.call(this, config);
51326 };
51327
51328 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51329     /**
51330      * @cfg {String} legend
51331      * The text to display as the legend for the FieldSet (defaults to '')
51332      */
51333     /**
51334      * @cfg {String/Object} autoCreate
51335      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51336      */
51337
51338     // private
51339     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51340
51341     // private
51342     onRender : function(ct, position){
51343         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51344         if(this.legend){
51345             this.setLegend(this.legend);
51346         }
51347     },
51348
51349     // private
51350     setLegend : function(text){
51351         if(this.rendered){
51352             this.el.child('legend').update(text);
51353         }
51354     }
51355 });/*
51356  * Based on:
51357  * Ext JS Library 1.1.1
51358  * Copyright(c) 2006-2007, Ext JS, LLC.
51359  *
51360  * Originally Released Under LGPL - original licence link has changed is not relivant.
51361  *
51362  * Fork - LGPL
51363  * <script type="text/javascript">
51364  */
51365 /**
51366  * @class Roo.form.VTypes
51367  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51368  * @static
51369  */
51370 Roo.form.VTypes = function(){
51371     // closure these in so they are only created once.
51372     var alpha = /^[a-zA-Z_]+$/;
51373     var alphanum = /^[a-zA-Z0-9_]+$/;
51374     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51375     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51376
51377     // All these messages and functions are configurable
51378     return {
51379         /**
51380          * The function used to validate email addresses
51381          * @param {String} value The email address
51382          */
51383         'email' : function(v){
51384             return email.test(v);
51385         },
51386         /**
51387          * The error text to display when the email validation function returns false
51388          * @type String
51389          */
51390         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51391         /**
51392          * The keystroke filter mask to be applied on email input
51393          * @type RegExp
51394          */
51395         'emailMask' : /[a-z0-9_\.\-@]/i,
51396
51397         /**
51398          * The function used to validate URLs
51399          * @param {String} value The URL
51400          */
51401         'url' : function(v){
51402             return url.test(v);
51403         },
51404         /**
51405          * The error text to display when the url validation function returns false
51406          * @type String
51407          */
51408         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51409         
51410         /**
51411          * The function used to validate alpha values
51412          * @param {String} value The value
51413          */
51414         'alpha' : function(v){
51415             return alpha.test(v);
51416         },
51417         /**
51418          * The error text to display when the alpha validation function returns false
51419          * @type String
51420          */
51421         'alphaText' : 'This field should only contain letters and _',
51422         /**
51423          * The keystroke filter mask to be applied on alpha input
51424          * @type RegExp
51425          */
51426         'alphaMask' : /[a-z_]/i,
51427
51428         /**
51429          * The function used to validate alphanumeric values
51430          * @param {String} value The value
51431          */
51432         'alphanum' : function(v){
51433             return alphanum.test(v);
51434         },
51435         /**
51436          * The error text to display when the alphanumeric validation function returns false
51437          * @type String
51438          */
51439         'alphanumText' : 'This field should only contain letters, numbers and _',
51440         /**
51441          * The keystroke filter mask to be applied on alphanumeric input
51442          * @type RegExp
51443          */
51444         'alphanumMask' : /[a-z0-9_]/i
51445     };
51446 }();//<script type="text/javascript">
51447
51448 /**
51449  * @class Roo.form.FCKeditor
51450  * @extends Roo.form.TextArea
51451  * Wrapper around the FCKEditor http://www.fckeditor.net
51452  * @constructor
51453  * Creates a new FCKeditor
51454  * @param {Object} config Configuration options
51455  */
51456 Roo.form.FCKeditor = function(config){
51457     Roo.form.FCKeditor.superclass.constructor.call(this, config);
51458     this.addEvents({
51459          /**
51460          * @event editorinit
51461          * Fired when the editor is initialized - you can add extra handlers here..
51462          * @param {FCKeditor} this
51463          * @param {Object} the FCK object.
51464          */
51465         editorinit : true
51466     });
51467     
51468     
51469 };
51470 Roo.form.FCKeditor.editors = { };
51471 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
51472 {
51473     //defaultAutoCreate : {
51474     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
51475     //},
51476     // private
51477     /**
51478      * @cfg {Object} fck options - see fck manual for details.
51479      */
51480     fckconfig : false,
51481     
51482     /**
51483      * @cfg {Object} fck toolbar set (Basic or Default)
51484      */
51485     toolbarSet : 'Basic',
51486     /**
51487      * @cfg {Object} fck BasePath
51488      */ 
51489     basePath : '/fckeditor/',
51490     
51491     
51492     frame : false,
51493     
51494     value : '',
51495     
51496    
51497     onRender : function(ct, position)
51498     {
51499         if(!this.el){
51500             this.defaultAutoCreate = {
51501                 tag: "textarea",
51502                 style:"width:300px;height:60px;",
51503                 autocomplete: "new-password"
51504             };
51505         }
51506         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
51507         /*
51508         if(this.grow){
51509             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
51510             if(this.preventScrollbars){
51511                 this.el.setStyle("overflow", "hidden");
51512             }
51513             this.el.setHeight(this.growMin);
51514         }
51515         */
51516         //console.log('onrender' + this.getId() );
51517         Roo.form.FCKeditor.editors[this.getId()] = this;
51518          
51519
51520         this.replaceTextarea() ;
51521         
51522     },
51523     
51524     getEditor : function() {
51525         return this.fckEditor;
51526     },
51527     /**
51528      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
51529      * @param {Mixed} value The value to set
51530      */
51531     
51532     
51533     setValue : function(value)
51534     {
51535         //console.log('setValue: ' + value);
51536         
51537         if(typeof(value) == 'undefined') { // not sure why this is happending...
51538             return;
51539         }
51540         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51541         
51542         //if(!this.el || !this.getEditor()) {
51543         //    this.value = value;
51544             //this.setValue.defer(100,this,[value]);    
51545         //    return;
51546         //} 
51547         
51548         if(!this.getEditor()) {
51549             return;
51550         }
51551         
51552         this.getEditor().SetData(value);
51553         
51554         //
51555
51556     },
51557
51558     /**
51559      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
51560      * @return {Mixed} value The field value
51561      */
51562     getValue : function()
51563     {
51564         
51565         if (this.frame && this.frame.dom.style.display == 'none') {
51566             return Roo.form.FCKeditor.superclass.getValue.call(this);
51567         }
51568         
51569         if(!this.el || !this.getEditor()) {
51570            
51571            // this.getValue.defer(100,this); 
51572             return this.value;
51573         }
51574        
51575         
51576         var value=this.getEditor().GetData();
51577         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51578         return Roo.form.FCKeditor.superclass.getValue.call(this);
51579         
51580
51581     },
51582
51583     /**
51584      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
51585      * @return {Mixed} value The field value
51586      */
51587     getRawValue : function()
51588     {
51589         if (this.frame && this.frame.dom.style.display == 'none') {
51590             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51591         }
51592         
51593         if(!this.el || !this.getEditor()) {
51594             //this.getRawValue.defer(100,this); 
51595             return this.value;
51596             return;
51597         }
51598         
51599         
51600         
51601         var value=this.getEditor().GetData();
51602         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
51603         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51604          
51605     },
51606     
51607     setSize : function(w,h) {
51608         
51609         
51610         
51611         //if (this.frame && this.frame.dom.style.display == 'none') {
51612         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51613         //    return;
51614         //}
51615         //if(!this.el || !this.getEditor()) {
51616         //    this.setSize.defer(100,this, [w,h]); 
51617         //    return;
51618         //}
51619         
51620         
51621         
51622         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51623         
51624         this.frame.dom.setAttribute('width', w);
51625         this.frame.dom.setAttribute('height', h);
51626         this.frame.setSize(w,h);
51627         
51628     },
51629     
51630     toggleSourceEdit : function(value) {
51631         
51632       
51633          
51634         this.el.dom.style.display = value ? '' : 'none';
51635         this.frame.dom.style.display = value ?  'none' : '';
51636         
51637     },
51638     
51639     
51640     focus: function(tag)
51641     {
51642         if (this.frame.dom.style.display == 'none') {
51643             return Roo.form.FCKeditor.superclass.focus.call(this);
51644         }
51645         if(!this.el || !this.getEditor()) {
51646             this.focus.defer(100,this, [tag]); 
51647             return;
51648         }
51649         
51650         
51651         
51652         
51653         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
51654         this.getEditor().Focus();
51655         if (tgs.length) {
51656             if (!this.getEditor().Selection.GetSelection()) {
51657                 this.focus.defer(100,this, [tag]); 
51658                 return;
51659             }
51660             
51661             
51662             var r = this.getEditor().EditorDocument.createRange();
51663             r.setStart(tgs[0],0);
51664             r.setEnd(tgs[0],0);
51665             this.getEditor().Selection.GetSelection().removeAllRanges();
51666             this.getEditor().Selection.GetSelection().addRange(r);
51667             this.getEditor().Focus();
51668         }
51669         
51670     },
51671     
51672     
51673     
51674     replaceTextarea : function()
51675     {
51676         if ( document.getElementById( this.getId() + '___Frame' ) ) {
51677             return ;
51678         }
51679         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
51680         //{
51681             // We must check the elements firstly using the Id and then the name.
51682         var oTextarea = document.getElementById( this.getId() );
51683         
51684         var colElementsByName = document.getElementsByName( this.getId() ) ;
51685          
51686         oTextarea.style.display = 'none' ;
51687
51688         if ( oTextarea.tabIndex ) {            
51689             this.TabIndex = oTextarea.tabIndex ;
51690         }
51691         
51692         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
51693         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
51694         this.frame = Roo.get(this.getId() + '___Frame')
51695     },
51696     
51697     _getConfigHtml : function()
51698     {
51699         var sConfig = '' ;
51700
51701         for ( var o in this.fckconfig ) {
51702             sConfig += sConfig.length > 0  ? '&amp;' : '';
51703             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
51704         }
51705
51706         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
51707     },
51708     
51709     
51710     _getIFrameHtml : function()
51711     {
51712         var sFile = 'fckeditor.html' ;
51713         /* no idea what this is about..
51714         try
51715         {
51716             if ( (/fcksource=true/i).test( window.top.location.search ) )
51717                 sFile = 'fckeditor.original.html' ;
51718         }
51719         catch (e) { 
51720         */
51721
51722         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
51723         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
51724         
51725         
51726         var html = '<iframe id="' + this.getId() +
51727             '___Frame" src="' + sLink +
51728             '" width="' + this.width +
51729             '" height="' + this.height + '"' +
51730             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
51731             ' frameborder="0" scrolling="no"></iframe>' ;
51732
51733         return html ;
51734     },
51735     
51736     _insertHtmlBefore : function( html, element )
51737     {
51738         if ( element.insertAdjacentHTML )       {
51739             // IE
51740             element.insertAdjacentHTML( 'beforeBegin', html ) ;
51741         } else { // Gecko
51742             var oRange = document.createRange() ;
51743             oRange.setStartBefore( element ) ;
51744             var oFragment = oRange.createContextualFragment( html );
51745             element.parentNode.insertBefore( oFragment, element ) ;
51746         }
51747     }
51748     
51749     
51750   
51751     
51752     
51753     
51754     
51755
51756 });
51757
51758 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51759
51760 function FCKeditor_OnComplete(editorInstance){
51761     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51762     f.fckEditor = editorInstance;
51763     //console.log("loaded");
51764     f.fireEvent('editorinit', f, editorInstance);
51765
51766   
51767
51768  
51769
51770
51771
51772
51773
51774
51775
51776
51777
51778
51779
51780
51781
51782
51783
51784 //<script type="text/javascript">
51785 /**
51786  * @class Roo.form.GridField
51787  * @extends Roo.form.Field
51788  * Embed a grid (or editable grid into a form)
51789  * STATUS ALPHA
51790  * 
51791  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51792  * it needs 
51793  * xgrid.store = Roo.data.Store
51794  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51795  * xgrid.store.reader = Roo.data.JsonReader 
51796  * 
51797  * 
51798  * @constructor
51799  * Creates a new GridField
51800  * @param {Object} config Configuration options
51801  */
51802 Roo.form.GridField = function(config){
51803     Roo.form.GridField.superclass.constructor.call(this, config);
51804      
51805 };
51806
51807 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51808     /**
51809      * @cfg {Number} width  - used to restrict width of grid..
51810      */
51811     width : 100,
51812     /**
51813      * @cfg {Number} height - used to restrict height of grid..
51814      */
51815     height : 50,
51816      /**
51817      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51818          * 
51819          *}
51820      */
51821     xgrid : false, 
51822     /**
51823      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51824      * {tag: "input", type: "checkbox", autocomplete: "off"})
51825      */
51826    // defaultAutoCreate : { tag: 'div' },
51827     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51828     /**
51829      * @cfg {String} addTitle Text to include for adding a title.
51830      */
51831     addTitle : false,
51832     //
51833     onResize : function(){
51834         Roo.form.Field.superclass.onResize.apply(this, arguments);
51835     },
51836
51837     initEvents : function(){
51838         // Roo.form.Checkbox.superclass.initEvents.call(this);
51839         // has no events...
51840        
51841     },
51842
51843
51844     getResizeEl : function(){
51845         return this.wrap;
51846     },
51847
51848     getPositionEl : function(){
51849         return this.wrap;
51850     },
51851
51852     // private
51853     onRender : function(ct, position){
51854         
51855         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51856         var style = this.style;
51857         delete this.style;
51858         
51859         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51860         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51861         this.viewEl = this.wrap.createChild({ tag: 'div' });
51862         if (style) {
51863             this.viewEl.applyStyles(style);
51864         }
51865         if (this.width) {
51866             this.viewEl.setWidth(this.width);
51867         }
51868         if (this.height) {
51869             this.viewEl.setHeight(this.height);
51870         }
51871         //if(this.inputValue !== undefined){
51872         //this.setValue(this.value);
51873         
51874         
51875         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51876         
51877         
51878         this.grid.render();
51879         this.grid.getDataSource().on('remove', this.refreshValue, this);
51880         this.grid.getDataSource().on('update', this.refreshValue, this);
51881         this.grid.on('afteredit', this.refreshValue, this);
51882  
51883     },
51884      
51885     
51886     /**
51887      * Sets the value of the item. 
51888      * @param {String} either an object  or a string..
51889      */
51890     setValue : function(v){
51891         //this.value = v;
51892         v = v || []; // empty set..
51893         // this does not seem smart - it really only affects memoryproxy grids..
51894         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51895             var ds = this.grid.getDataSource();
51896             // assumes a json reader..
51897             var data = {}
51898             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51899             ds.loadData( data);
51900         }
51901         // clear selection so it does not get stale.
51902         if (this.grid.sm) { 
51903             this.grid.sm.clearSelections();
51904         }
51905         
51906         Roo.form.GridField.superclass.setValue.call(this, v);
51907         this.refreshValue();
51908         // should load data in the grid really....
51909     },
51910     
51911     // private
51912     refreshValue: function() {
51913          var val = [];
51914         this.grid.getDataSource().each(function(r) {
51915             val.push(r.data);
51916         });
51917         this.el.dom.value = Roo.encode(val);
51918     }
51919     
51920      
51921     
51922     
51923 });/*
51924  * Based on:
51925  * Ext JS Library 1.1.1
51926  * Copyright(c) 2006-2007, Ext JS, LLC.
51927  *
51928  * Originally Released Under LGPL - original licence link has changed is not relivant.
51929  *
51930  * Fork - LGPL
51931  * <script type="text/javascript">
51932  */
51933 /**
51934  * @class Roo.form.DisplayField
51935  * @extends Roo.form.Field
51936  * A generic Field to display non-editable data.
51937  * @cfg {Boolean} closable (true|false) default false
51938  * @constructor
51939  * Creates a new Display Field item.
51940  * @param {Object} config Configuration options
51941  */
51942 Roo.form.DisplayField = function(config){
51943     Roo.form.DisplayField.superclass.constructor.call(this, config);
51944     
51945     this.addEvents({
51946         /**
51947          * @event close
51948          * Fires after the click the close btn
51949              * @param {Roo.form.DisplayField} this
51950              */
51951         close : true
51952     });
51953 };
51954
51955 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51956     inputType:      'hidden',
51957     allowBlank:     true,
51958     readOnly:         true,
51959     
51960  
51961     /**
51962      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51963      */
51964     focusClass : undefined,
51965     /**
51966      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51967      */
51968     fieldClass: 'x-form-field',
51969     
51970      /**
51971      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51972      */
51973     valueRenderer: undefined,
51974     
51975     width: 100,
51976     /**
51977      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51978      * {tag: "input", type: "checkbox", autocomplete: "off"})
51979      */
51980      
51981  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51982  
51983     closable : false,
51984     
51985     onResize : function(){
51986         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51987         
51988     },
51989
51990     initEvents : function(){
51991         // Roo.form.Checkbox.superclass.initEvents.call(this);
51992         // has no events...
51993         
51994         if(this.closable){
51995             this.closeEl.on('click', this.onClose, this);
51996         }
51997        
51998     },
51999
52000
52001     getResizeEl : function(){
52002         return this.wrap;
52003     },
52004
52005     getPositionEl : function(){
52006         return this.wrap;
52007     },
52008
52009     // private
52010     onRender : function(ct, position){
52011         
52012         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52013         //if(this.inputValue !== undefined){
52014         this.wrap = this.el.wrap();
52015         
52016         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52017         
52018         if(this.closable){
52019             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52020         }
52021         
52022         if (this.bodyStyle) {
52023             this.viewEl.applyStyles(this.bodyStyle);
52024         }
52025         //this.viewEl.setStyle('padding', '2px');
52026         
52027         this.setValue(this.value);
52028         
52029     },
52030 /*
52031     // private
52032     initValue : Roo.emptyFn,
52033
52034   */
52035
52036         // private
52037     onClick : function(){
52038         
52039     },
52040
52041     /**
52042      * Sets the checked state of the checkbox.
52043      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52044      */
52045     setValue : function(v){
52046         this.value = v;
52047         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52048         // this might be called before we have a dom element..
52049         if (!this.viewEl) {
52050             return;
52051         }
52052         this.viewEl.dom.innerHTML = html;
52053         Roo.form.DisplayField.superclass.setValue.call(this, v);
52054
52055     },
52056     
52057     onClose : function(e)
52058     {
52059         e.preventDefault();
52060         
52061         this.fireEvent('close', this);
52062     }
52063 });/*
52064  * 
52065  * Licence- LGPL
52066  * 
52067  */
52068
52069 /**
52070  * @class Roo.form.DayPicker
52071  * @extends Roo.form.Field
52072  * A Day picker show [M] [T] [W] ....
52073  * @constructor
52074  * Creates a new Day Picker
52075  * @param {Object} config Configuration options
52076  */
52077 Roo.form.DayPicker= function(config){
52078     Roo.form.DayPicker.superclass.constructor.call(this, config);
52079      
52080 };
52081
52082 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52083     /**
52084      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52085      */
52086     focusClass : undefined,
52087     /**
52088      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52089      */
52090     fieldClass: "x-form-field",
52091    
52092     /**
52093      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52094      * {tag: "input", type: "checkbox", autocomplete: "off"})
52095      */
52096     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52097     
52098    
52099     actionMode : 'viewEl', 
52100     //
52101     // private
52102  
52103     inputType : 'hidden',
52104     
52105      
52106     inputElement: false, // real input element?
52107     basedOn: false, // ????
52108     
52109     isFormField: true, // not sure where this is needed!!!!
52110
52111     onResize : function(){
52112         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52113         if(!this.boxLabel){
52114             this.el.alignTo(this.wrap, 'c-c');
52115         }
52116     },
52117
52118     initEvents : function(){
52119         Roo.form.Checkbox.superclass.initEvents.call(this);
52120         this.el.on("click", this.onClick,  this);
52121         this.el.on("change", this.onClick,  this);
52122     },
52123
52124
52125     getResizeEl : function(){
52126         return this.wrap;
52127     },
52128
52129     getPositionEl : function(){
52130         return this.wrap;
52131     },
52132
52133     
52134     // private
52135     onRender : function(ct, position){
52136         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52137        
52138         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52139         
52140         var r1 = '<table><tr>';
52141         var r2 = '<tr class="x-form-daypick-icons">';
52142         for (var i=0; i < 7; i++) {
52143             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52144             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52145         }
52146         
52147         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52148         viewEl.select('img').on('click', this.onClick, this);
52149         this.viewEl = viewEl;   
52150         
52151         
52152         // this will not work on Chrome!!!
52153         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52154         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52155         
52156         
52157           
52158
52159     },
52160
52161     // private
52162     initValue : Roo.emptyFn,
52163
52164     /**
52165      * Returns the checked state of the checkbox.
52166      * @return {Boolean} True if checked, else false
52167      */
52168     getValue : function(){
52169         return this.el.dom.value;
52170         
52171     },
52172
52173         // private
52174     onClick : function(e){ 
52175         //this.setChecked(!this.checked);
52176         Roo.get(e.target).toggleClass('x-menu-item-checked');
52177         this.refreshValue();
52178         //if(this.el.dom.checked != this.checked){
52179         //    this.setValue(this.el.dom.checked);
52180        // }
52181     },
52182     
52183     // private
52184     refreshValue : function()
52185     {
52186         var val = '';
52187         this.viewEl.select('img',true).each(function(e,i,n)  {
52188             val += e.is(".x-menu-item-checked") ? String(n) : '';
52189         });
52190         this.setValue(val, true);
52191     },
52192
52193     /**
52194      * Sets the checked state of the checkbox.
52195      * On is always based on a string comparison between inputValue and the param.
52196      * @param {Boolean/String} value - the value to set 
52197      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52198      */
52199     setValue : function(v,suppressEvent){
52200         if (!this.el.dom) {
52201             return;
52202         }
52203         var old = this.el.dom.value ;
52204         this.el.dom.value = v;
52205         if (suppressEvent) {
52206             return ;
52207         }
52208          
52209         // update display..
52210         this.viewEl.select('img',true).each(function(e,i,n)  {
52211             
52212             var on = e.is(".x-menu-item-checked");
52213             var newv = v.indexOf(String(n)) > -1;
52214             if (on != newv) {
52215                 e.toggleClass('x-menu-item-checked');
52216             }
52217             
52218         });
52219         
52220         
52221         this.fireEvent('change', this, v, old);
52222         
52223         
52224     },
52225    
52226     // handle setting of hidden value by some other method!!?!?
52227     setFromHidden: function()
52228     {
52229         if(!this.el){
52230             return;
52231         }
52232         //console.log("SET FROM HIDDEN");
52233         //alert('setFrom hidden');
52234         this.setValue(this.el.dom.value);
52235     },
52236     
52237     onDestroy : function()
52238     {
52239         if(this.viewEl){
52240             Roo.get(this.viewEl).remove();
52241         }
52242          
52243         Roo.form.DayPicker.superclass.onDestroy.call(this);
52244     }
52245
52246 });/*
52247  * RooJS Library 1.1.1
52248  * Copyright(c) 2008-2011  Alan Knowles
52249  *
52250  * License - LGPL
52251  */
52252  
52253
52254 /**
52255  * @class Roo.form.ComboCheck
52256  * @extends Roo.form.ComboBox
52257  * A combobox for multiple select items.
52258  *
52259  * FIXME - could do with a reset button..
52260  * 
52261  * @constructor
52262  * Create a new ComboCheck
52263  * @param {Object} config Configuration options
52264  */
52265 Roo.form.ComboCheck = function(config){
52266     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52267     // should verify some data...
52268     // like
52269     // hiddenName = required..
52270     // displayField = required
52271     // valudField == required
52272     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52273     var _t = this;
52274     Roo.each(req, function(e) {
52275         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52276             throw "Roo.form.ComboCheck : missing value for: " + e;
52277         }
52278     });
52279     
52280     
52281 };
52282
52283 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52284      
52285      
52286     editable : false,
52287      
52288     selectedClass: 'x-menu-item-checked', 
52289     
52290     // private
52291     onRender : function(ct, position){
52292         var _t = this;
52293         
52294         
52295         
52296         if(!this.tpl){
52297             var cls = 'x-combo-list';
52298
52299             
52300             this.tpl =  new Roo.Template({
52301                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52302                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52303                    '<span>{' + this.displayField + '}</span>' +
52304                     '</div>' 
52305                 
52306             });
52307         }
52308  
52309         
52310         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52311         this.view.singleSelect = false;
52312         this.view.multiSelect = true;
52313         this.view.toggleSelect = true;
52314         this.pageTb.add(new Roo.Toolbar.Fill(), {
52315             
52316             text: 'Done',
52317             handler: function()
52318             {
52319                 _t.collapse();
52320             }
52321         });
52322     },
52323     
52324     onViewOver : function(e, t){
52325         // do nothing...
52326         return;
52327         
52328     },
52329     
52330     onViewClick : function(doFocus,index){
52331         return;
52332         
52333     },
52334     select: function () {
52335         //Roo.log("SELECT CALLED");
52336     },
52337      
52338     selectByValue : function(xv, scrollIntoView){
52339         var ar = this.getValueArray();
52340         var sels = [];
52341         
52342         Roo.each(ar, function(v) {
52343             if(v === undefined || v === null){
52344                 return;
52345             }
52346             var r = this.findRecord(this.valueField, v);
52347             if(r){
52348                 sels.push(this.store.indexOf(r))
52349                 
52350             }
52351         },this);
52352         this.view.select(sels);
52353         return false;
52354     },
52355     
52356     
52357     
52358     onSelect : function(record, index){
52359        // Roo.log("onselect Called");
52360        // this is only called by the clear button now..
52361         this.view.clearSelections();
52362         this.setValue('[]');
52363         if (this.value != this.valueBefore) {
52364             this.fireEvent('change', this, this.value, this.valueBefore);
52365             this.valueBefore = this.value;
52366         }
52367     },
52368     getValueArray : function()
52369     {
52370         var ar = [] ;
52371         
52372         try {
52373             //Roo.log(this.value);
52374             if (typeof(this.value) == 'undefined') {
52375                 return [];
52376             }
52377             var ar = Roo.decode(this.value);
52378             return  ar instanceof Array ? ar : []; //?? valid?
52379             
52380         } catch(e) {
52381             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52382             return [];
52383         }
52384          
52385     },
52386     expand : function ()
52387     {
52388         
52389         Roo.form.ComboCheck.superclass.expand.call(this);
52390         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52391         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52392         
52393
52394     },
52395     
52396     collapse : function(){
52397         Roo.form.ComboCheck.superclass.collapse.call(this);
52398         var sl = this.view.getSelectedIndexes();
52399         var st = this.store;
52400         var nv = [];
52401         var tv = [];
52402         var r;
52403         Roo.each(sl, function(i) {
52404             r = st.getAt(i);
52405             nv.push(r.get(this.valueField));
52406         },this);
52407         this.setValue(Roo.encode(nv));
52408         if (this.value != this.valueBefore) {
52409
52410             this.fireEvent('change', this, this.value, this.valueBefore);
52411             this.valueBefore = this.value;
52412         }
52413         
52414     },
52415     
52416     setValue : function(v){
52417         // Roo.log(v);
52418         this.value = v;
52419         
52420         var vals = this.getValueArray();
52421         var tv = [];
52422         Roo.each(vals, function(k) {
52423             var r = this.findRecord(this.valueField, k);
52424             if(r){
52425                 tv.push(r.data[this.displayField]);
52426             }else if(this.valueNotFoundText !== undefined){
52427                 tv.push( this.valueNotFoundText );
52428             }
52429         },this);
52430        // Roo.log(tv);
52431         
52432         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
52433         this.hiddenField.value = v;
52434         this.value = v;
52435     }
52436     
52437 });/*
52438  * Based on:
52439  * Ext JS Library 1.1.1
52440  * Copyright(c) 2006-2007, Ext JS, LLC.
52441  *
52442  * Originally Released Under LGPL - original licence link has changed is not relivant.
52443  *
52444  * Fork - LGPL
52445  * <script type="text/javascript">
52446  */
52447  
52448 /**
52449  * @class Roo.form.Signature
52450  * @extends Roo.form.Field
52451  * Signature field.  
52452  * @constructor
52453  * 
52454  * @param {Object} config Configuration options
52455  */
52456
52457 Roo.form.Signature = function(config){
52458     Roo.form.Signature.superclass.constructor.call(this, config);
52459     
52460     this.addEvents({// not in used??
52461          /**
52462          * @event confirm
52463          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
52464              * @param {Roo.form.Signature} combo This combo box
52465              */
52466         'confirm' : true,
52467         /**
52468          * @event reset
52469          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
52470              * @param {Roo.form.ComboBox} combo This combo box
52471              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
52472              */
52473         'reset' : true
52474     });
52475 };
52476
52477 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
52478     /**
52479      * @cfg {Object} labels Label to use when rendering a form.
52480      * defaults to 
52481      * labels : { 
52482      *      clear : "Clear",
52483      *      confirm : "Confirm"
52484      *  }
52485      */
52486     labels : { 
52487         clear : "Clear",
52488         confirm : "Confirm"
52489     },
52490     /**
52491      * @cfg {Number} width The signature panel width (defaults to 300)
52492      */
52493     width: 300,
52494     /**
52495      * @cfg {Number} height The signature panel height (defaults to 100)
52496      */
52497     height : 100,
52498     /**
52499      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
52500      */
52501     allowBlank : false,
52502     
52503     //private
52504     // {Object} signPanel The signature SVG panel element (defaults to {})
52505     signPanel : {},
52506     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
52507     isMouseDown : false,
52508     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
52509     isConfirmed : false,
52510     // {String} signatureTmp SVG mapping string (defaults to empty string)
52511     signatureTmp : '',
52512     
52513     
52514     defaultAutoCreate : { // modified by initCompnoent..
52515         tag: "input",
52516         type:"hidden"
52517     },
52518
52519     // private
52520     onRender : function(ct, position){
52521         
52522         Roo.form.Signature.superclass.onRender.call(this, ct, position);
52523         
52524         this.wrap = this.el.wrap({
52525             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
52526         });
52527         
52528         this.createToolbar(this);
52529         this.signPanel = this.wrap.createChild({
52530                 tag: 'div',
52531                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
52532             }, this.el
52533         );
52534             
52535         this.svgID = Roo.id();
52536         this.svgEl = this.signPanel.createChild({
52537               xmlns : 'http://www.w3.org/2000/svg',
52538               tag : 'svg',
52539               id : this.svgID + "-svg",
52540               width: this.width,
52541               height: this.height,
52542               viewBox: '0 0 '+this.width+' '+this.height,
52543               cn : [
52544                 {
52545                     tag: "rect",
52546                     id: this.svgID + "-svg-r",
52547                     width: this.width,
52548                     height: this.height,
52549                     fill: "#ffa"
52550                 },
52551                 {
52552                     tag: "line",
52553                     id: this.svgID + "-svg-l",
52554                     x1: "0", // start
52555                     y1: (this.height*0.8), // start set the line in 80% of height
52556                     x2: this.width, // end
52557                     y2: (this.height*0.8), // end set the line in 80% of height
52558                     'stroke': "#666",
52559                     'stroke-width': "1",
52560                     'stroke-dasharray': "3",
52561                     'shape-rendering': "crispEdges",
52562                     'pointer-events': "none"
52563                 },
52564                 {
52565                     tag: "path",
52566                     id: this.svgID + "-svg-p",
52567                     'stroke': "navy",
52568                     'stroke-width': "3",
52569                     'fill': "none",
52570                     'pointer-events': 'none'
52571                 }
52572               ]
52573         });
52574         this.createSVG();
52575         this.svgBox = this.svgEl.dom.getScreenCTM();
52576     },
52577     createSVG : function(){ 
52578         var svg = this.signPanel;
52579         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
52580         var t = this;
52581
52582         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
52583         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
52584         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
52585         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
52586         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
52587         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
52588         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
52589         
52590     },
52591     isTouchEvent : function(e){
52592         return e.type.match(/^touch/);
52593     },
52594     getCoords : function (e) {
52595         var pt    = this.svgEl.dom.createSVGPoint();
52596         pt.x = e.clientX; 
52597         pt.y = e.clientY;
52598         if (this.isTouchEvent(e)) {
52599             pt.x =  e.targetTouches[0].clientX;
52600             pt.y = e.targetTouches[0].clientY;
52601         }
52602         var a = this.svgEl.dom.getScreenCTM();
52603         var b = a.inverse();
52604         var mx = pt.matrixTransform(b);
52605         return mx.x + ',' + mx.y;
52606     },
52607     //mouse event headler 
52608     down : function (e) {
52609         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
52610         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
52611         
52612         this.isMouseDown = true;
52613         
52614         e.preventDefault();
52615     },
52616     move : function (e) {
52617         if (this.isMouseDown) {
52618             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
52619             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
52620         }
52621         
52622         e.preventDefault();
52623     },
52624     up : function (e) {
52625         this.isMouseDown = false;
52626         var sp = this.signatureTmp.split(' ');
52627         
52628         if(sp.length > 1){
52629             if(!sp[sp.length-2].match(/^L/)){
52630                 sp.pop();
52631                 sp.pop();
52632                 sp.push("");
52633                 this.signatureTmp = sp.join(" ");
52634             }
52635         }
52636         if(this.getValue() != this.signatureTmp){
52637             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52638             this.isConfirmed = false;
52639         }
52640         e.preventDefault();
52641     },
52642     
52643     /**
52644      * Protected method that will not generally be called directly. It
52645      * is called when the editor creates its toolbar. Override this method if you need to
52646      * add custom toolbar buttons.
52647      * @param {HtmlEditor} editor
52648      */
52649     createToolbar : function(editor){
52650          function btn(id, toggle, handler){
52651             var xid = fid + '-'+ id ;
52652             return {
52653                 id : xid,
52654                 cmd : id,
52655                 cls : 'x-btn-icon x-edit-'+id,
52656                 enableToggle:toggle !== false,
52657                 scope: editor, // was editor...
52658                 handler:handler||editor.relayBtnCmd,
52659                 clickEvent:'mousedown',
52660                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52661                 tabIndex:-1
52662             };
52663         }
52664         
52665         
52666         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52667         this.tb = tb;
52668         this.tb.add(
52669            {
52670                 cls : ' x-signature-btn x-signature-'+id,
52671                 scope: editor, // was editor...
52672                 handler: this.reset,
52673                 clickEvent:'mousedown',
52674                 text: this.labels.clear
52675             },
52676             {
52677                  xtype : 'Fill',
52678                  xns: Roo.Toolbar
52679             }, 
52680             {
52681                 cls : '  x-signature-btn x-signature-'+id,
52682                 scope: editor, // was editor...
52683                 handler: this.confirmHandler,
52684                 clickEvent:'mousedown',
52685                 text: this.labels.confirm
52686             }
52687         );
52688     
52689     },
52690     //public
52691     /**
52692      * when user is clicked confirm then show this image.....
52693      * 
52694      * @return {String} Image Data URI
52695      */
52696     getImageDataURI : function(){
52697         var svg = this.svgEl.dom.parentNode.innerHTML;
52698         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
52699         return src; 
52700     },
52701     /**
52702      * 
52703      * @return {Boolean} this.isConfirmed
52704      */
52705     getConfirmed : function(){
52706         return this.isConfirmed;
52707     },
52708     /**
52709      * 
52710      * @return {Number} this.width
52711      */
52712     getWidth : function(){
52713         return this.width;
52714     },
52715     /**
52716      * 
52717      * @return {Number} this.height
52718      */
52719     getHeight : function(){
52720         return this.height;
52721     },
52722     // private
52723     getSignature : function(){
52724         return this.signatureTmp;
52725     },
52726     // private
52727     reset : function(){
52728         this.signatureTmp = '';
52729         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52730         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
52731         this.isConfirmed = false;
52732         Roo.form.Signature.superclass.reset.call(this);
52733     },
52734     setSignature : function(s){
52735         this.signatureTmp = s;
52736         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52737         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
52738         this.setValue(s);
52739         this.isConfirmed = false;
52740         Roo.form.Signature.superclass.reset.call(this);
52741     }, 
52742     test : function(){
52743 //        Roo.log(this.signPanel.dom.contentWindow.up())
52744     },
52745     //private
52746     setConfirmed : function(){
52747         
52748         
52749         
52750 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52751     },
52752     // private
52753     confirmHandler : function(){
52754         if(!this.getSignature()){
52755             return;
52756         }
52757         
52758         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52759         this.setValue(this.getSignature());
52760         this.isConfirmed = true;
52761         
52762         this.fireEvent('confirm', this);
52763     },
52764     // private
52765     // Subclasses should provide the validation implementation by overriding this
52766     validateValue : function(value){
52767         if(this.allowBlank){
52768             return true;
52769         }
52770         
52771         if(this.isConfirmed){
52772             return true;
52773         }
52774         return false;
52775     }
52776 });/*
52777  * Based on:
52778  * Ext JS Library 1.1.1
52779  * Copyright(c) 2006-2007, Ext JS, LLC.
52780  *
52781  * Originally Released Under LGPL - original licence link has changed is not relivant.
52782  *
52783  * Fork - LGPL
52784  * <script type="text/javascript">
52785  */
52786  
52787
52788 /**
52789  * @class Roo.form.ComboBox
52790  * @extends Roo.form.TriggerField
52791  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52792  * @constructor
52793  * Create a new ComboBox.
52794  * @param {Object} config Configuration options
52795  */
52796 Roo.form.Select = function(config){
52797     Roo.form.Select.superclass.constructor.call(this, config);
52798      
52799 };
52800
52801 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52802     /**
52803      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52804      */
52805     /**
52806      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52807      * rendering into an Roo.Editor, defaults to false)
52808      */
52809     /**
52810      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52811      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52812      */
52813     /**
52814      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52815      */
52816     /**
52817      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52818      * the dropdown list (defaults to undefined, with no header element)
52819      */
52820
52821      /**
52822      * @cfg {String/Roo.Template} tpl The template to use to render the output
52823      */
52824      
52825     // private
52826     defaultAutoCreate : {tag: "select"  },
52827     /**
52828      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52829      */
52830     listWidth: undefined,
52831     /**
52832      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52833      * mode = 'remote' or 'text' if mode = 'local')
52834      */
52835     displayField: undefined,
52836     /**
52837      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52838      * mode = 'remote' or 'value' if mode = 'local'). 
52839      * Note: use of a valueField requires the user make a selection
52840      * in order for a value to be mapped.
52841      */
52842     valueField: undefined,
52843     
52844     
52845     /**
52846      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52847      * field's data value (defaults to the underlying DOM element's name)
52848      */
52849     hiddenName: undefined,
52850     /**
52851      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52852      */
52853     listClass: '',
52854     /**
52855      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52856      */
52857     selectedClass: 'x-combo-selected',
52858     /**
52859      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52860      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52861      * which displays a downward arrow icon).
52862      */
52863     triggerClass : 'x-form-arrow-trigger',
52864     /**
52865      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52866      */
52867     shadow:'sides',
52868     /**
52869      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52870      * anchor positions (defaults to 'tl-bl')
52871      */
52872     listAlign: 'tl-bl?',
52873     /**
52874      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52875      */
52876     maxHeight: 300,
52877     /**
52878      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52879      * query specified by the allQuery config option (defaults to 'query')
52880      */
52881     triggerAction: 'query',
52882     /**
52883      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52884      * (defaults to 4, does not apply if editable = false)
52885      */
52886     minChars : 4,
52887     /**
52888      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52889      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52890      */
52891     typeAhead: false,
52892     /**
52893      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52894      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52895      */
52896     queryDelay: 500,
52897     /**
52898      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52899      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52900      */
52901     pageSize: 0,
52902     /**
52903      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52904      * when editable = true (defaults to false)
52905      */
52906     selectOnFocus:false,
52907     /**
52908      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52909      */
52910     queryParam: 'query',
52911     /**
52912      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52913      * when mode = 'remote' (defaults to 'Loading...')
52914      */
52915     loadingText: 'Loading...',
52916     /**
52917      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52918      */
52919     resizable: false,
52920     /**
52921      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52922      */
52923     handleHeight : 8,
52924     /**
52925      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52926      * traditional select (defaults to true)
52927      */
52928     editable: true,
52929     /**
52930      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52931      */
52932     allQuery: '',
52933     /**
52934      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52935      */
52936     mode: 'remote',
52937     /**
52938      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52939      * listWidth has a higher value)
52940      */
52941     minListWidth : 70,
52942     /**
52943      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52944      * allow the user to set arbitrary text into the field (defaults to false)
52945      */
52946     forceSelection:false,
52947     /**
52948      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52949      * if typeAhead = true (defaults to 250)
52950      */
52951     typeAheadDelay : 250,
52952     /**
52953      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52954      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52955      */
52956     valueNotFoundText : undefined,
52957     
52958     /**
52959      * @cfg {String} defaultValue The value displayed after loading the store.
52960      */
52961     defaultValue: '',
52962     
52963     /**
52964      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52965      */
52966     blockFocus : false,
52967     
52968     /**
52969      * @cfg {Boolean} disableClear Disable showing of clear button.
52970      */
52971     disableClear : false,
52972     /**
52973      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52974      */
52975     alwaysQuery : false,
52976     
52977     //private
52978     addicon : false,
52979     editicon: false,
52980     
52981     // element that contains real text value.. (when hidden is used..)
52982      
52983     // private
52984     onRender : function(ct, position){
52985         Roo.form.Field.prototype.onRender.call(this, ct, position);
52986         
52987         if(this.store){
52988             this.store.on('beforeload', this.onBeforeLoad, this);
52989             this.store.on('load', this.onLoad, this);
52990             this.store.on('loadexception', this.onLoadException, this);
52991             this.store.load({});
52992         }
52993         
52994         
52995         
52996     },
52997
52998     // private
52999     initEvents : function(){
53000         //Roo.form.ComboBox.superclass.initEvents.call(this);
53001  
53002     },
53003
53004     onDestroy : function(){
53005        
53006         if(this.store){
53007             this.store.un('beforeload', this.onBeforeLoad, this);
53008             this.store.un('load', this.onLoad, this);
53009             this.store.un('loadexception', this.onLoadException, this);
53010         }
53011         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53012     },
53013
53014     // private
53015     fireKey : function(e){
53016         if(e.isNavKeyPress() && !this.list.isVisible()){
53017             this.fireEvent("specialkey", this, e);
53018         }
53019     },
53020
53021     // private
53022     onResize: function(w, h){
53023         
53024         return; 
53025     
53026         
53027     },
53028
53029     /**
53030      * Allow or prevent the user from directly editing the field text.  If false is passed,
53031      * the user will only be able to select from the items defined in the dropdown list.  This method
53032      * is the runtime equivalent of setting the 'editable' config option at config time.
53033      * @param {Boolean} value True to allow the user to directly edit the field text
53034      */
53035     setEditable : function(value){
53036          
53037     },
53038
53039     // private
53040     onBeforeLoad : function(){
53041         
53042         Roo.log("Select before load");
53043         return;
53044     
53045         this.innerList.update(this.loadingText ?
53046                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53047         //this.restrictHeight();
53048         this.selectedIndex = -1;
53049     },
53050
53051     // private
53052     onLoad : function(){
53053
53054     
53055         var dom = this.el.dom;
53056         dom.innerHTML = '';
53057          var od = dom.ownerDocument;
53058          
53059         if (this.emptyText) {
53060             var op = od.createElement('option');
53061             op.setAttribute('value', '');
53062             op.innerHTML = String.format('{0}', this.emptyText);
53063             dom.appendChild(op);
53064         }
53065         if(this.store.getCount() > 0){
53066            
53067             var vf = this.valueField;
53068             var df = this.displayField;
53069             this.store.data.each(function(r) {
53070                 // which colmsn to use... testing - cdoe / title..
53071                 var op = od.createElement('option');
53072                 op.setAttribute('value', r.data[vf]);
53073                 op.innerHTML = String.format('{0}', r.data[df]);
53074                 dom.appendChild(op);
53075             });
53076             if (typeof(this.defaultValue != 'undefined')) {
53077                 this.setValue(this.defaultValue);
53078             }
53079             
53080              
53081         }else{
53082             //this.onEmptyResults();
53083         }
53084         //this.el.focus();
53085     },
53086     // private
53087     onLoadException : function()
53088     {
53089         dom.innerHTML = '';
53090             
53091         Roo.log("Select on load exception");
53092         return;
53093     
53094         this.collapse();
53095         Roo.log(this.store.reader.jsonData);
53096         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53097             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53098         }
53099         
53100         
53101     },
53102     // private
53103     onTypeAhead : function(){
53104          
53105     },
53106
53107     // private
53108     onSelect : function(record, index){
53109         Roo.log('on select?');
53110         return;
53111         if(this.fireEvent('beforeselect', this, record, index) !== false){
53112             this.setFromData(index > -1 ? record.data : false);
53113             this.collapse();
53114             this.fireEvent('select', this, record, index);
53115         }
53116     },
53117
53118     /**
53119      * Returns the currently selected field value or empty string if no value is set.
53120      * @return {String} value The selected value
53121      */
53122     getValue : function(){
53123         var dom = this.el.dom;
53124         this.value = dom.options[dom.selectedIndex].value;
53125         return this.value;
53126         
53127     },
53128
53129     /**
53130      * Clears any text/value currently set in the field
53131      */
53132     clearValue : function(){
53133         this.value = '';
53134         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53135         
53136     },
53137
53138     /**
53139      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53140      * will be displayed in the field.  If the value does not match the data value of an existing item,
53141      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53142      * Otherwise the field will be blank (although the value will still be set).
53143      * @param {String} value The value to match
53144      */
53145     setValue : function(v){
53146         var d = this.el.dom;
53147         for (var i =0; i < d.options.length;i++) {
53148             if (v == d.options[i].value) {
53149                 d.selectedIndex = i;
53150                 this.value = v;
53151                 return;
53152             }
53153         }
53154         this.clearValue();
53155     },
53156     /**
53157      * @property {Object} the last set data for the element
53158      */
53159     
53160     lastData : false,
53161     /**
53162      * Sets the value of the field based on a object which is related to the record format for the store.
53163      * @param {Object} value the value to set as. or false on reset?
53164      */
53165     setFromData : function(o){
53166         Roo.log('setfrom data?');
53167          
53168         
53169         
53170     },
53171     // private
53172     reset : function(){
53173         this.clearValue();
53174     },
53175     // private
53176     findRecord : function(prop, value){
53177         
53178         return false;
53179     
53180         var record;
53181         if(this.store.getCount() > 0){
53182             this.store.each(function(r){
53183                 if(r.data[prop] == value){
53184                     record = r;
53185                     return false;
53186                 }
53187                 return true;
53188             });
53189         }
53190         return record;
53191     },
53192     
53193     getName: function()
53194     {
53195         // returns hidden if it's set..
53196         if (!this.rendered) {return ''};
53197         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53198         
53199     },
53200      
53201
53202     
53203
53204     // private
53205     onEmptyResults : function(){
53206         Roo.log('empty results');
53207         //this.collapse();
53208     },
53209
53210     /**
53211      * Returns true if the dropdown list is expanded, else false.
53212      */
53213     isExpanded : function(){
53214         return false;
53215     },
53216
53217     /**
53218      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53219      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53220      * @param {String} value The data value of the item to select
53221      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53222      * selected item if it is not currently in view (defaults to true)
53223      * @return {Boolean} True if the value matched an item in the list, else false
53224      */
53225     selectByValue : function(v, scrollIntoView){
53226         Roo.log('select By Value');
53227         return false;
53228     
53229         if(v !== undefined && v !== null){
53230             var r = this.findRecord(this.valueField || this.displayField, v);
53231             if(r){
53232                 this.select(this.store.indexOf(r), scrollIntoView);
53233                 return true;
53234             }
53235         }
53236         return false;
53237     },
53238
53239     /**
53240      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53241      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53242      * @param {Number} index The zero-based index of the list item to select
53243      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53244      * selected item if it is not currently in view (defaults to true)
53245      */
53246     select : function(index, scrollIntoView){
53247         Roo.log('select ');
53248         return  ;
53249         
53250         this.selectedIndex = index;
53251         this.view.select(index);
53252         if(scrollIntoView !== false){
53253             var el = this.view.getNode(index);
53254             if(el){
53255                 this.innerList.scrollChildIntoView(el, false);
53256             }
53257         }
53258     },
53259
53260       
53261
53262     // private
53263     validateBlur : function(){
53264         
53265         return;
53266         
53267     },
53268
53269     // private
53270     initQuery : function(){
53271         this.doQuery(this.getRawValue());
53272     },
53273
53274     // private
53275     doForce : function(){
53276         if(this.el.dom.value.length > 0){
53277             this.el.dom.value =
53278                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53279              
53280         }
53281     },
53282
53283     /**
53284      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53285      * query allowing the query action to be canceled if needed.
53286      * @param {String} query The SQL query to execute
53287      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53288      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53289      * saved in the current store (defaults to false)
53290      */
53291     doQuery : function(q, forceAll){
53292         
53293         Roo.log('doQuery?');
53294         if(q === undefined || q === null){
53295             q = '';
53296         }
53297         var qe = {
53298             query: q,
53299             forceAll: forceAll,
53300             combo: this,
53301             cancel:false
53302         };
53303         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53304             return false;
53305         }
53306         q = qe.query;
53307         forceAll = qe.forceAll;
53308         if(forceAll === true || (q.length >= this.minChars)){
53309             if(this.lastQuery != q || this.alwaysQuery){
53310                 this.lastQuery = q;
53311                 if(this.mode == 'local'){
53312                     this.selectedIndex = -1;
53313                     if(forceAll){
53314                         this.store.clearFilter();
53315                     }else{
53316                         this.store.filter(this.displayField, q);
53317                     }
53318                     this.onLoad();
53319                 }else{
53320                     this.store.baseParams[this.queryParam] = q;
53321                     this.store.load({
53322                         params: this.getParams(q)
53323                     });
53324                     this.expand();
53325                 }
53326             }else{
53327                 this.selectedIndex = -1;
53328                 this.onLoad();   
53329             }
53330         }
53331     },
53332
53333     // private
53334     getParams : function(q){
53335         var p = {};
53336         //p[this.queryParam] = q;
53337         if(this.pageSize){
53338             p.start = 0;
53339             p.limit = this.pageSize;
53340         }
53341         return p;
53342     },
53343
53344     /**
53345      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53346      */
53347     collapse : function(){
53348         
53349     },
53350
53351     // private
53352     collapseIf : function(e){
53353         
53354     },
53355
53356     /**
53357      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53358      */
53359     expand : function(){
53360         
53361     } ,
53362
53363     // private
53364      
53365
53366     /** 
53367     * @cfg {Boolean} grow 
53368     * @hide 
53369     */
53370     /** 
53371     * @cfg {Number} growMin 
53372     * @hide 
53373     */
53374     /** 
53375     * @cfg {Number} growMax 
53376     * @hide 
53377     */
53378     /**
53379      * @hide
53380      * @method autoSize
53381      */
53382     
53383     setWidth : function()
53384     {
53385         
53386     },
53387     getResizeEl : function(){
53388         return this.el;
53389     }
53390 });//<script type="text/javasscript">
53391  
53392
53393 /**
53394  * @class Roo.DDView
53395  * A DnD enabled version of Roo.View.
53396  * @param {Element/String} container The Element in which to create the View.
53397  * @param {String} tpl The template string used to create the markup for each element of the View
53398  * @param {Object} config The configuration properties. These include all the config options of
53399  * {@link Roo.View} plus some specific to this class.<br>
53400  * <p>
53401  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53402  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53403  * <p>
53404  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53405 .x-view-drag-insert-above {
53406         border-top:1px dotted #3366cc;
53407 }
53408 .x-view-drag-insert-below {
53409         border-bottom:1px dotted #3366cc;
53410 }
53411 </code></pre>
53412  * 
53413  */
53414  
53415 Roo.DDView = function(container, tpl, config) {
53416     Roo.DDView.superclass.constructor.apply(this, arguments);
53417     this.getEl().setStyle("outline", "0px none");
53418     this.getEl().unselectable();
53419     if (this.dragGroup) {
53420         this.setDraggable(this.dragGroup.split(","));
53421     }
53422     if (this.dropGroup) {
53423         this.setDroppable(this.dropGroup.split(","));
53424     }
53425     if (this.deletable) {
53426         this.setDeletable();
53427     }
53428     this.isDirtyFlag = false;
53429         this.addEvents({
53430                 "drop" : true
53431         });
53432 };
53433
53434 Roo.extend(Roo.DDView, Roo.View, {
53435 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
53436 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
53437 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
53438 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
53439
53440         isFormField: true,
53441
53442         reset: Roo.emptyFn,
53443         
53444         clearInvalid: Roo.form.Field.prototype.clearInvalid,
53445
53446         validate: function() {
53447                 return true;
53448         },
53449         
53450         destroy: function() {
53451                 this.purgeListeners();
53452                 this.getEl.removeAllListeners();
53453                 this.getEl().remove();
53454                 if (this.dragZone) {
53455                         if (this.dragZone.destroy) {
53456                                 this.dragZone.destroy();
53457                         }
53458                 }
53459                 if (this.dropZone) {
53460                         if (this.dropZone.destroy) {
53461                                 this.dropZone.destroy();
53462                         }
53463                 }
53464         },
53465
53466 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
53467         getName: function() {
53468                 return this.name;
53469         },
53470
53471 /**     Loads the View from a JSON string representing the Records to put into the Store. */
53472         setValue: function(v) {
53473                 if (!this.store) {
53474                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
53475                 }
53476                 var data = {};
53477                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
53478                 this.store.proxy = new Roo.data.MemoryProxy(data);
53479                 this.store.load();
53480         },
53481
53482 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
53483         getValue: function() {
53484                 var result = '(';
53485                 this.store.each(function(rec) {
53486                         result += rec.id + ',';
53487                 });
53488                 return result.substr(0, result.length - 1) + ')';
53489         },
53490         
53491         getIds: function() {
53492                 var i = 0, result = new Array(this.store.getCount());
53493                 this.store.each(function(rec) {
53494                         result[i++] = rec.id;
53495                 });
53496                 return result;
53497         },
53498         
53499         isDirty: function() {
53500                 return this.isDirtyFlag;
53501         },
53502
53503 /**
53504  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
53505  *      whole Element becomes the target, and this causes the drop gesture to append.
53506  */
53507     getTargetFromEvent : function(e) {
53508                 var target = e.getTarget();
53509                 while ((target !== null) && (target.parentNode != this.el.dom)) {
53510                 target = target.parentNode;
53511                 }
53512                 if (!target) {
53513                         target = this.el.dom.lastChild || this.el.dom;
53514                 }
53515                 return target;
53516     },
53517
53518 /**
53519  *      Create the drag data which consists of an object which has the property "ddel" as
53520  *      the drag proxy element. 
53521  */
53522     getDragData : function(e) {
53523         var target = this.findItemFromChild(e.getTarget());
53524                 if(target) {
53525                         this.handleSelection(e);
53526                         var selNodes = this.getSelectedNodes();
53527             var dragData = {
53528                 source: this,
53529                 copy: this.copy || (this.allowCopy && e.ctrlKey),
53530                 nodes: selNodes,
53531                 records: []
53532                         };
53533                         var selectedIndices = this.getSelectedIndexes();
53534                         for (var i = 0; i < selectedIndices.length; i++) {
53535                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
53536                         }
53537                         if (selNodes.length == 1) {
53538                                 dragData.ddel = target.cloneNode(true); // the div element
53539                         } else {
53540                                 var div = document.createElement('div'); // create the multi element drag "ghost"
53541                                 div.className = 'multi-proxy';
53542                                 for (var i = 0, len = selNodes.length; i < len; i++) {
53543                                         div.appendChild(selNodes[i].cloneNode(true));
53544                                 }
53545                                 dragData.ddel = div;
53546                         }
53547             //console.log(dragData)
53548             //console.log(dragData.ddel.innerHTML)
53549                         return dragData;
53550                 }
53551         //console.log('nodragData')
53552                 return false;
53553     },
53554     
53555 /**     Specify to which ddGroup items in this DDView may be dragged. */
53556     setDraggable: function(ddGroup) {
53557         if (ddGroup instanceof Array) {
53558                 Roo.each(ddGroup, this.setDraggable, this);
53559                 return;
53560         }
53561         if (this.dragZone) {
53562                 this.dragZone.addToGroup(ddGroup);
53563         } else {
53564                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
53565                                 containerScroll: true,
53566                                 ddGroup: ddGroup 
53567
53568                         });
53569 //                      Draggability implies selection. DragZone's mousedown selects the element.
53570                         if (!this.multiSelect) { this.singleSelect = true; }
53571
53572 //                      Wire the DragZone's handlers up to methods in *this*
53573                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
53574                 }
53575     },
53576
53577 /**     Specify from which ddGroup this DDView accepts drops. */
53578     setDroppable: function(ddGroup) {
53579         if (ddGroup instanceof Array) {
53580                 Roo.each(ddGroup, this.setDroppable, this);
53581                 return;
53582         }
53583         if (this.dropZone) {
53584                 this.dropZone.addToGroup(ddGroup);
53585         } else {
53586                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
53587                                 containerScroll: true,
53588                                 ddGroup: ddGroup
53589                         });
53590
53591 //                      Wire the DropZone's handlers up to methods in *this*
53592                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
53593                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
53594                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
53595                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
53596                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
53597                 }
53598     },
53599
53600 /**     Decide whether to drop above or below a View node. */
53601     getDropPoint : function(e, n, dd){
53602         if (n == this.el.dom) { return "above"; }
53603                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
53604                 var c = t + (b - t) / 2;
53605                 var y = Roo.lib.Event.getPageY(e);
53606                 if(y <= c) {
53607                         return "above";
53608                 }else{
53609                         return "below";
53610                 }
53611     },
53612
53613     onNodeEnter : function(n, dd, e, data){
53614                 return false;
53615     },
53616     
53617     onNodeOver : function(n, dd, e, data){
53618                 var pt = this.getDropPoint(e, n, dd);
53619                 // set the insert point style on the target node
53620                 var dragElClass = this.dropNotAllowed;
53621                 if (pt) {
53622                         var targetElClass;
53623                         if (pt == "above"){
53624                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
53625                                 targetElClass = "x-view-drag-insert-above";
53626                         } else {
53627                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
53628                                 targetElClass = "x-view-drag-insert-below";
53629                         }
53630                         if (this.lastInsertClass != targetElClass){
53631                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
53632                                 this.lastInsertClass = targetElClass;
53633                         }
53634                 }
53635                 return dragElClass;
53636         },
53637
53638     onNodeOut : function(n, dd, e, data){
53639                 this.removeDropIndicators(n);
53640     },
53641
53642     onNodeDrop : function(n, dd, e, data){
53643         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
53644                 return false;
53645         }
53646         var pt = this.getDropPoint(e, n, dd);
53647                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
53648                 if (pt == "below") { insertAt++; }
53649                 for (var i = 0; i < data.records.length; i++) {
53650                         var r = data.records[i];
53651                         var dup = this.store.getById(r.id);
53652                         if (dup && (dd != this.dragZone)) {
53653                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
53654                         } else {
53655                                 if (data.copy) {
53656                                         this.store.insert(insertAt++, r.copy());
53657                                 } else {
53658                                         data.source.isDirtyFlag = true;
53659                                         r.store.remove(r);
53660                                         this.store.insert(insertAt++, r);
53661                                 }
53662                                 this.isDirtyFlag = true;
53663                         }
53664                 }
53665                 this.dragZone.cachedTarget = null;
53666                 return true;
53667     },
53668
53669     removeDropIndicators : function(n){
53670                 if(n){
53671                         Roo.fly(n).removeClass([
53672                                 "x-view-drag-insert-above",
53673                                 "x-view-drag-insert-below"]);
53674                         this.lastInsertClass = "_noclass";
53675                 }
53676     },
53677
53678 /**
53679  *      Utility method. Add a delete option to the DDView's context menu.
53680  *      @param {String} imageUrl The URL of the "delete" icon image.
53681  */
53682         setDeletable: function(imageUrl) {
53683                 if (!this.singleSelect && !this.multiSelect) {
53684                         this.singleSelect = true;
53685                 }
53686                 var c = this.getContextMenu();
53687                 this.contextMenu.on("itemclick", function(item) {
53688                         switch (item.id) {
53689                                 case "delete":
53690                                         this.remove(this.getSelectedIndexes());
53691                                         break;
53692                         }
53693                 }, this);
53694                 this.contextMenu.add({
53695                         icon: imageUrl,
53696                         id: "delete",
53697                         text: 'Delete'
53698                 });
53699         },
53700         
53701 /**     Return the context menu for this DDView. */
53702         getContextMenu: function() {
53703                 if (!this.contextMenu) {
53704 //                      Create the View's context menu
53705                         this.contextMenu = new Roo.menu.Menu({
53706                                 id: this.id + "-contextmenu"
53707                         });
53708                         this.el.on("contextmenu", this.showContextMenu, this);
53709                 }
53710                 return this.contextMenu;
53711         },
53712         
53713         disableContextMenu: function() {
53714                 if (this.contextMenu) {
53715                         this.el.un("contextmenu", this.showContextMenu, this);
53716                 }
53717         },
53718
53719         showContextMenu: function(e, item) {
53720         item = this.findItemFromChild(e.getTarget());
53721                 if (item) {
53722                         e.stopEvent();
53723                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
53724                         this.contextMenu.showAt(e.getXY());
53725             }
53726     },
53727
53728 /**
53729  *      Remove {@link Roo.data.Record}s at the specified indices.
53730  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
53731  */
53732     remove: function(selectedIndices) {
53733                 selectedIndices = [].concat(selectedIndices);
53734                 for (var i = 0; i < selectedIndices.length; i++) {
53735                         var rec = this.store.getAt(selectedIndices[i]);
53736                         this.store.remove(rec);
53737                 }
53738     },
53739
53740 /**
53741  *      Double click fires the event, but also, if this is draggable, and there is only one other
53742  *      related DropZone, it transfers the selected node.
53743  */
53744     onDblClick : function(e){
53745         var item = this.findItemFromChild(e.getTarget());
53746         if(item){
53747             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53748                 return false;
53749             }
53750             if (this.dragGroup) {
53751                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53752                     while (targets.indexOf(this.dropZone) > -1) {
53753                             targets.remove(this.dropZone);
53754                                 }
53755                     if (targets.length == 1) {
53756                                         this.dragZone.cachedTarget = null;
53757                         var el = Roo.get(targets[0].getEl());
53758                         var box = el.getBox(true);
53759                         targets[0].onNodeDrop(el.dom, {
53760                                 target: el.dom,
53761                                 xy: [box.x, box.y + box.height - 1]
53762                         }, null, this.getDragData(e));
53763                     }
53764                 }
53765         }
53766     },
53767     
53768     handleSelection: function(e) {
53769                 this.dragZone.cachedTarget = null;
53770         var item = this.findItemFromChild(e.getTarget());
53771         if (!item) {
53772                 this.clearSelections(true);
53773                 return;
53774         }
53775                 if (item && (this.multiSelect || this.singleSelect)){
53776                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53777                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53778                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53779                                 this.unselect(item);
53780                         } else {
53781                                 this.select(item, this.multiSelect && e.ctrlKey);
53782                                 this.lastSelection = item;
53783                         }
53784                 }
53785     },
53786
53787     onItemClick : function(item, index, e){
53788                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53789                         return false;
53790                 }
53791                 return true;
53792     },
53793
53794     unselect : function(nodeInfo, suppressEvent){
53795                 var node = this.getNode(nodeInfo);
53796                 if(node && this.isSelected(node)){
53797                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53798                                 Roo.fly(node).removeClass(this.selectedClass);
53799                                 this.selections.remove(node);
53800                                 if(!suppressEvent){
53801                                         this.fireEvent("selectionchange", this, this.selections);
53802                                 }
53803                         }
53804                 }
53805     }
53806 });
53807 /*
53808  * Based on:
53809  * Ext JS Library 1.1.1
53810  * Copyright(c) 2006-2007, Ext JS, LLC.
53811  *
53812  * Originally Released Under LGPL - original licence link has changed is not relivant.
53813  *
53814  * Fork - LGPL
53815  * <script type="text/javascript">
53816  */
53817  
53818 /**
53819  * @class Roo.LayoutManager
53820  * @extends Roo.util.Observable
53821  * Base class for layout managers.
53822  */
53823 Roo.LayoutManager = function(container, config){
53824     Roo.LayoutManager.superclass.constructor.call(this);
53825     this.el = Roo.get(container);
53826     // ie scrollbar fix
53827     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53828         document.body.scroll = "no";
53829     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53830         this.el.position('relative');
53831     }
53832     this.id = this.el.id;
53833     this.el.addClass("x-layout-container");
53834     /** false to disable window resize monitoring @type Boolean */
53835     this.monitorWindowResize = true;
53836     this.regions = {};
53837     this.addEvents({
53838         /**
53839          * @event layout
53840          * Fires when a layout is performed. 
53841          * @param {Roo.LayoutManager} this
53842          */
53843         "layout" : true,
53844         /**
53845          * @event regionresized
53846          * Fires when the user resizes a region. 
53847          * @param {Roo.LayoutRegion} region The resized region
53848          * @param {Number} newSize The new size (width for east/west, height for north/south)
53849          */
53850         "regionresized" : true,
53851         /**
53852          * @event regioncollapsed
53853          * Fires when a region is collapsed. 
53854          * @param {Roo.LayoutRegion} region The collapsed region
53855          */
53856         "regioncollapsed" : true,
53857         /**
53858          * @event regionexpanded
53859          * Fires when a region is expanded.  
53860          * @param {Roo.LayoutRegion} region The expanded region
53861          */
53862         "regionexpanded" : true
53863     });
53864     this.updating = false;
53865     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53866 };
53867
53868 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53869     /**
53870      * Returns true if this layout is currently being updated
53871      * @return {Boolean}
53872      */
53873     isUpdating : function(){
53874         return this.updating; 
53875     },
53876     
53877     /**
53878      * Suspend the LayoutManager from doing auto-layouts while
53879      * making multiple add or remove calls
53880      */
53881     beginUpdate : function(){
53882         this.updating = true;    
53883     },
53884     
53885     /**
53886      * Restore auto-layouts and optionally disable the manager from performing a layout
53887      * @param {Boolean} noLayout true to disable a layout update 
53888      */
53889     endUpdate : function(noLayout){
53890         this.updating = false;
53891         if(!noLayout){
53892             this.layout();
53893         }    
53894     },
53895     
53896     layout: function(){
53897         
53898     },
53899     
53900     onRegionResized : function(region, newSize){
53901         this.fireEvent("regionresized", region, newSize);
53902         this.layout();
53903     },
53904     
53905     onRegionCollapsed : function(region){
53906         this.fireEvent("regioncollapsed", region);
53907     },
53908     
53909     onRegionExpanded : function(region){
53910         this.fireEvent("regionexpanded", region);
53911     },
53912         
53913     /**
53914      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53915      * performs box-model adjustments.
53916      * @return {Object} The size as an object {width: (the width), height: (the height)}
53917      */
53918     getViewSize : function(){
53919         var size;
53920         if(this.el.dom != document.body){
53921             size = this.el.getSize();
53922         }else{
53923             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53924         }
53925         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53926         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53927         return size;
53928     },
53929     
53930     /**
53931      * Returns the Element this layout is bound to.
53932      * @return {Roo.Element}
53933      */
53934     getEl : function(){
53935         return this.el;
53936     },
53937     
53938     /**
53939      * Returns the specified region.
53940      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53941      * @return {Roo.LayoutRegion}
53942      */
53943     getRegion : function(target){
53944         return this.regions[target.toLowerCase()];
53945     },
53946     
53947     onWindowResize : function(){
53948         if(this.monitorWindowResize){
53949             this.layout();
53950         }
53951     }
53952 });/*
53953  * Based on:
53954  * Ext JS Library 1.1.1
53955  * Copyright(c) 2006-2007, Ext JS, LLC.
53956  *
53957  * Originally Released Under LGPL - original licence link has changed is not relivant.
53958  *
53959  * Fork - LGPL
53960  * <script type="text/javascript">
53961  */
53962 /**
53963  * @class Roo.BorderLayout
53964  * @extends Roo.LayoutManager
53965  * @children Roo.ContentPanel
53966  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53967  * please see: <br><br>
53968  * <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>
53969  * <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>
53970  * Example:
53971  <pre><code>
53972  var layout = new Roo.BorderLayout(document.body, {
53973     north: {
53974         initialSize: 25,
53975         titlebar: false
53976     },
53977     west: {
53978         split:true,
53979         initialSize: 200,
53980         minSize: 175,
53981         maxSize: 400,
53982         titlebar: true,
53983         collapsible: true
53984     },
53985     east: {
53986         split:true,
53987         initialSize: 202,
53988         minSize: 175,
53989         maxSize: 400,
53990         titlebar: true,
53991         collapsible: true
53992     },
53993     south: {
53994         split:true,
53995         initialSize: 100,
53996         minSize: 100,
53997         maxSize: 200,
53998         titlebar: true,
53999         collapsible: true
54000     },
54001     center: {
54002         titlebar: true,
54003         autoScroll:true,
54004         resizeTabs: true,
54005         minTabWidth: 50,
54006         preferredTabWidth: 150
54007     }
54008 });
54009
54010 // shorthand
54011 var CP = Roo.ContentPanel;
54012
54013 layout.beginUpdate();
54014 layout.add("north", new CP("north", "North"));
54015 layout.add("south", new CP("south", {title: "South", closable: true}));
54016 layout.add("west", new CP("west", {title: "West"}));
54017 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54018 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54019 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54020 layout.getRegion("center").showPanel("center1");
54021 layout.endUpdate();
54022 </code></pre>
54023
54024 <b>The container the layout is rendered into can be either the body element or any other element.
54025 If it is not the body element, the container needs to either be an absolute positioned element,
54026 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54027 the container size if it is not the body element.</b>
54028
54029 * @constructor
54030 * Create a new BorderLayout
54031 * @param {String/HTMLElement/Element} container The container this layout is bound to
54032 * @param {Object} config Configuration options
54033  */
54034 Roo.BorderLayout = function(container, config){
54035     config = config || {};
54036     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54037     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54038     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54039         var target = this.factory.validRegions[i];
54040         if(config[target]){
54041             this.addRegion(target, config[target]);
54042         }
54043     }
54044 };
54045
54046 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54047         
54048         /**
54049          * @cfg {Roo.LayoutRegion} east
54050          */
54051         /**
54052          * @cfg {Roo.LayoutRegion} west
54053          */
54054         /**
54055          * @cfg {Roo.LayoutRegion} north
54056          */
54057         /**
54058          * @cfg {Roo.LayoutRegion} south
54059          */
54060         /**
54061          * @cfg {Roo.LayoutRegion} center
54062          */
54063     /**
54064      * Creates and adds a new region if it doesn't already exist.
54065      * @param {String} target The target region key (north, south, east, west or center).
54066      * @param {Object} config The regions config object
54067      * @return {BorderLayoutRegion} The new region
54068      */
54069     addRegion : function(target, config){
54070         if(!this.regions[target]){
54071             var r = this.factory.create(target, this, config);
54072             this.bindRegion(target, r);
54073         }
54074         return this.regions[target];
54075     },
54076
54077     // private (kinda)
54078     bindRegion : function(name, r){
54079         this.regions[name] = r;
54080         r.on("visibilitychange", this.layout, this);
54081         r.on("paneladded", this.layout, this);
54082         r.on("panelremoved", this.layout, this);
54083         r.on("invalidated", this.layout, this);
54084         r.on("resized", this.onRegionResized, this);
54085         r.on("collapsed", this.onRegionCollapsed, this);
54086         r.on("expanded", this.onRegionExpanded, this);
54087     },
54088
54089     /**
54090      * Performs a layout update.
54091      */
54092     layout : function(){
54093         if(this.updating) {
54094             return;
54095         }
54096         var size = this.getViewSize();
54097         var w = size.width;
54098         var h = size.height;
54099         var centerW = w;
54100         var centerH = h;
54101         var centerY = 0;
54102         var centerX = 0;
54103         //var x = 0, y = 0;
54104
54105         var rs = this.regions;
54106         var north = rs["north"];
54107         var south = rs["south"]; 
54108         var west = rs["west"];
54109         var east = rs["east"];
54110         var center = rs["center"];
54111         //if(this.hideOnLayout){ // not supported anymore
54112             //c.el.setStyle("display", "none");
54113         //}
54114         if(north && north.isVisible()){
54115             var b = north.getBox();
54116             var m = north.getMargins();
54117             b.width = w - (m.left+m.right);
54118             b.x = m.left;
54119             b.y = m.top;
54120             centerY = b.height + b.y + m.bottom;
54121             centerH -= centerY;
54122             north.updateBox(this.safeBox(b));
54123         }
54124         if(south && south.isVisible()){
54125             var b = south.getBox();
54126             var m = south.getMargins();
54127             b.width = w - (m.left+m.right);
54128             b.x = m.left;
54129             var totalHeight = (b.height + m.top + m.bottom);
54130             b.y = h - totalHeight + m.top;
54131             centerH -= totalHeight;
54132             south.updateBox(this.safeBox(b));
54133         }
54134         if(west && west.isVisible()){
54135             var b = west.getBox();
54136             var m = west.getMargins();
54137             b.height = centerH - (m.top+m.bottom);
54138             b.x = m.left;
54139             b.y = centerY + m.top;
54140             var totalWidth = (b.width + m.left + m.right);
54141             centerX += totalWidth;
54142             centerW -= totalWidth;
54143             west.updateBox(this.safeBox(b));
54144         }
54145         if(east && east.isVisible()){
54146             var b = east.getBox();
54147             var m = east.getMargins();
54148             b.height = centerH - (m.top+m.bottom);
54149             var totalWidth = (b.width + m.left + m.right);
54150             b.x = w - totalWidth + m.left;
54151             b.y = centerY + m.top;
54152             centerW -= totalWidth;
54153             east.updateBox(this.safeBox(b));
54154         }
54155         if(center){
54156             var m = center.getMargins();
54157             var centerBox = {
54158                 x: centerX + m.left,
54159                 y: centerY + m.top,
54160                 width: centerW - (m.left+m.right),
54161                 height: centerH - (m.top+m.bottom)
54162             };
54163             //if(this.hideOnLayout){
54164                 //center.el.setStyle("display", "block");
54165             //}
54166             center.updateBox(this.safeBox(centerBox));
54167         }
54168         this.el.repaint();
54169         this.fireEvent("layout", this);
54170     },
54171
54172     // private
54173     safeBox : function(box){
54174         box.width = Math.max(0, box.width);
54175         box.height = Math.max(0, box.height);
54176         return box;
54177     },
54178
54179     /**
54180      * Adds a ContentPanel (or subclass) to this layout.
54181      * @param {String} target The target region key (north, south, east, west or center).
54182      * @param {Roo.ContentPanel} panel The panel to add
54183      * @return {Roo.ContentPanel} The added panel
54184      */
54185     add : function(target, panel){
54186          
54187         target = target.toLowerCase();
54188         return this.regions[target].add(panel);
54189     },
54190
54191     /**
54192      * Remove a ContentPanel (or subclass) to this layout.
54193      * @param {String} target The target region key (north, south, east, west or center).
54194      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54195      * @return {Roo.ContentPanel} The removed panel
54196      */
54197     remove : function(target, panel){
54198         target = target.toLowerCase();
54199         return this.regions[target].remove(panel);
54200     },
54201
54202     /**
54203      * Searches all regions for a panel with the specified id
54204      * @param {String} panelId
54205      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54206      */
54207     findPanel : function(panelId){
54208         var rs = this.regions;
54209         for(var target in rs){
54210             if(typeof rs[target] != "function"){
54211                 var p = rs[target].getPanel(panelId);
54212                 if(p){
54213                     return p;
54214                 }
54215             }
54216         }
54217         return null;
54218     },
54219
54220     /**
54221      * Searches all regions for a panel with the specified id and activates (shows) it.
54222      * @param {String/ContentPanel} panelId The panels id or the panel itself
54223      * @return {Roo.ContentPanel} The shown panel or null
54224      */
54225     showPanel : function(panelId) {
54226       var rs = this.regions;
54227       for(var target in rs){
54228          var r = rs[target];
54229          if(typeof r != "function"){
54230             if(r.hasPanel(panelId)){
54231                return r.showPanel(panelId);
54232             }
54233          }
54234       }
54235       return null;
54236    },
54237
54238    /**
54239      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54240      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54241      */
54242     restoreState : function(provider){
54243         if(!provider){
54244             provider = Roo.state.Manager;
54245         }
54246         var sm = new Roo.LayoutStateManager();
54247         sm.init(this, provider);
54248     },
54249
54250     /**
54251      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54252      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54253      * a valid ContentPanel config object.  Example:
54254      * <pre><code>
54255 // Create the main layout
54256 var layout = new Roo.BorderLayout('main-ct', {
54257     west: {
54258         split:true,
54259         minSize: 175,
54260         titlebar: true
54261     },
54262     center: {
54263         title:'Components'
54264     }
54265 }, 'main-ct');
54266
54267 // Create and add multiple ContentPanels at once via configs
54268 layout.batchAdd({
54269    west: {
54270        id: 'source-files',
54271        autoCreate:true,
54272        title:'Ext Source Files',
54273        autoScroll:true,
54274        fitToFrame:true
54275    },
54276    center : {
54277        el: cview,
54278        autoScroll:true,
54279        fitToFrame:true,
54280        toolbar: tb,
54281        resizeEl:'cbody'
54282    }
54283 });
54284 </code></pre>
54285      * @param {Object} regions An object containing ContentPanel configs by region name
54286      */
54287     batchAdd : function(regions){
54288         this.beginUpdate();
54289         for(var rname in regions){
54290             var lr = this.regions[rname];
54291             if(lr){
54292                 this.addTypedPanels(lr, regions[rname]);
54293             }
54294         }
54295         this.endUpdate();
54296     },
54297
54298     // private
54299     addTypedPanels : function(lr, ps){
54300         if(typeof ps == 'string'){
54301             lr.add(new Roo.ContentPanel(ps));
54302         }
54303         else if(ps instanceof Array){
54304             for(var i =0, len = ps.length; i < len; i++){
54305                 this.addTypedPanels(lr, ps[i]);
54306             }
54307         }
54308         else if(!ps.events){ // raw config?
54309             var el = ps.el;
54310             delete ps.el; // prevent conflict
54311             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54312         }
54313         else {  // panel object assumed!
54314             lr.add(ps);
54315         }
54316     },
54317     /**
54318      * Adds a xtype elements to the layout.
54319      * <pre><code>
54320
54321 layout.addxtype({
54322        xtype : 'ContentPanel',
54323        region: 'west',
54324        items: [ .... ]
54325    }
54326 );
54327
54328 layout.addxtype({
54329         xtype : 'NestedLayoutPanel',
54330         region: 'west',
54331         layout: {
54332            center: { },
54333            west: { }   
54334         },
54335         items : [ ... list of content panels or nested layout panels.. ]
54336    }
54337 );
54338 </code></pre>
54339      * @param {Object} cfg Xtype definition of item to add.
54340      */
54341     addxtype : function(cfg)
54342     {
54343         // basically accepts a pannel...
54344         // can accept a layout region..!?!?
54345         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54346         
54347         if (!cfg.xtype.match(/Panel$/)) {
54348             return false;
54349         }
54350         var ret = false;
54351         
54352         if (typeof(cfg.region) == 'undefined') {
54353             Roo.log("Failed to add Panel, region was not set");
54354             Roo.log(cfg);
54355             return false;
54356         }
54357         var region = cfg.region;
54358         delete cfg.region;
54359         
54360           
54361         var xitems = [];
54362         if (cfg.items) {
54363             xitems = cfg.items;
54364             delete cfg.items;
54365         }
54366         var nb = false;
54367         
54368         switch(cfg.xtype) 
54369         {
54370             case 'ContentPanel':  // ContentPanel (el, cfg)
54371             case 'ScrollPanel':  // ContentPanel (el, cfg)
54372             case 'ViewPanel': 
54373                 if(cfg.autoCreate) {
54374                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54375                 } else {
54376                     var el = this.el.createChild();
54377                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54378                 }
54379                 
54380                 this.add(region, ret);
54381                 break;
54382             
54383             
54384             case 'TreePanel': // our new panel!
54385                 cfg.el = this.el.createChild();
54386                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54387                 this.add(region, ret);
54388                 break;
54389             
54390             case 'NestedLayoutPanel': 
54391                 // create a new Layout (which is  a Border Layout...
54392                 var el = this.el.createChild();
54393                 var clayout = cfg.layout;
54394                 delete cfg.layout;
54395                 clayout.items   = clayout.items  || [];
54396                 // replace this exitems with the clayout ones..
54397                 xitems = clayout.items;
54398                  
54399                 
54400                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54401                     cfg.background = false;
54402                 }
54403                 var layout = new Roo.BorderLayout(el, clayout);
54404                 
54405                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54406                 //console.log('adding nested layout panel '  + cfg.toSource());
54407                 this.add(region, ret);
54408                 nb = {}; /// find first...
54409                 break;
54410                 
54411             case 'GridPanel': 
54412             
54413                 // needs grid and region
54414                 
54415                 //var el = this.getRegion(region).el.createChild();
54416                 var el = this.el.createChild();
54417                 // create the grid first...
54418                 
54419                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
54420                 delete cfg.grid;
54421                 if (region == 'center' && this.active ) {
54422                     cfg.background = false;
54423                 }
54424                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
54425                 
54426                 this.add(region, ret);
54427                 if (cfg.background) {
54428                     ret.on('activate', function(gp) {
54429                         if (!gp.grid.rendered) {
54430                             gp.grid.render();
54431                         }
54432                     });
54433                 } else {
54434                     grid.render();
54435                 }
54436                 break;
54437            
54438            
54439            
54440                 
54441                 
54442                 
54443             default:
54444                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
54445                     
54446                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54447                     this.add(region, ret);
54448                 } else {
54449                 
54450                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
54451                     return null;
54452                 }
54453                 
54454              // GridPanel (grid, cfg)
54455             
54456         }
54457         this.beginUpdate();
54458         // add children..
54459         var region = '';
54460         var abn = {};
54461         Roo.each(xitems, function(i)  {
54462             region = nb && i.region ? i.region : false;
54463             
54464             var add = ret.addxtype(i);
54465            
54466             if (region) {
54467                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
54468                 if (!i.background) {
54469                     abn[region] = nb[region] ;
54470                 }
54471             }
54472             
54473         });
54474         this.endUpdate();
54475
54476         // make the last non-background panel active..
54477         //if (nb) { Roo.log(abn); }
54478         if (nb) {
54479             
54480             for(var r in abn) {
54481                 region = this.getRegion(r);
54482                 if (region) {
54483                     // tried using nb[r], but it does not work..
54484                      
54485                     region.showPanel(abn[r]);
54486                    
54487                 }
54488             }
54489         }
54490         return ret;
54491         
54492     }
54493 });
54494
54495 /**
54496  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
54497  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
54498  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
54499  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
54500  * <pre><code>
54501 // shorthand
54502 var CP = Roo.ContentPanel;
54503
54504 var layout = Roo.BorderLayout.create({
54505     north: {
54506         initialSize: 25,
54507         titlebar: false,
54508         panels: [new CP("north", "North")]
54509     },
54510     west: {
54511         split:true,
54512         initialSize: 200,
54513         minSize: 175,
54514         maxSize: 400,
54515         titlebar: true,
54516         collapsible: true,
54517         panels: [new CP("west", {title: "West"})]
54518     },
54519     east: {
54520         split:true,
54521         initialSize: 202,
54522         minSize: 175,
54523         maxSize: 400,
54524         titlebar: true,
54525         collapsible: true,
54526         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
54527     },
54528     south: {
54529         split:true,
54530         initialSize: 100,
54531         minSize: 100,
54532         maxSize: 200,
54533         titlebar: true,
54534         collapsible: true,
54535         panels: [new CP("south", {title: "South", closable: true})]
54536     },
54537     center: {
54538         titlebar: true,
54539         autoScroll:true,
54540         resizeTabs: true,
54541         minTabWidth: 50,
54542         preferredTabWidth: 150,
54543         panels: [
54544             new CP("center1", {title: "Close Me", closable: true}),
54545             new CP("center2", {title: "Center Panel", closable: false})
54546         ]
54547     }
54548 }, document.body);
54549
54550 layout.getRegion("center").showPanel("center1");
54551 </code></pre>
54552  * @param config
54553  * @param targetEl
54554  */
54555 Roo.BorderLayout.create = function(config, targetEl){
54556     var layout = new Roo.BorderLayout(targetEl || document.body, config);
54557     layout.beginUpdate();
54558     var regions = Roo.BorderLayout.RegionFactory.validRegions;
54559     for(var j = 0, jlen = regions.length; j < jlen; j++){
54560         var lr = regions[j];
54561         if(layout.regions[lr] && config[lr].panels){
54562             var r = layout.regions[lr];
54563             var ps = config[lr].panels;
54564             layout.addTypedPanels(r, ps);
54565         }
54566     }
54567     layout.endUpdate();
54568     return layout;
54569 };
54570
54571 // private
54572 Roo.BorderLayout.RegionFactory = {
54573     // private
54574     validRegions : ["north","south","east","west","center"],
54575
54576     // private
54577     create : function(target, mgr, config){
54578         target = target.toLowerCase();
54579         if(config.lightweight || config.basic){
54580             return new Roo.BasicLayoutRegion(mgr, config, target);
54581         }
54582         switch(target){
54583             case "north":
54584                 return new Roo.NorthLayoutRegion(mgr, config);
54585             case "south":
54586                 return new Roo.SouthLayoutRegion(mgr, config);
54587             case "east":
54588                 return new Roo.EastLayoutRegion(mgr, config);
54589             case "west":
54590                 return new Roo.WestLayoutRegion(mgr, config);
54591             case "center":
54592                 return new Roo.CenterLayoutRegion(mgr, config);
54593         }
54594         throw 'Layout region "'+target+'" not supported.';
54595     }
54596 };/*
54597  * Based on:
54598  * Ext JS Library 1.1.1
54599  * Copyright(c) 2006-2007, Ext JS, LLC.
54600  *
54601  * Originally Released Under LGPL - original licence link has changed is not relivant.
54602  *
54603  * Fork - LGPL
54604  * <script type="text/javascript">
54605  */
54606  
54607 /**
54608  * @class Roo.BasicLayoutRegion
54609  * @extends Roo.util.Observable
54610  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
54611  * and does not have a titlebar, tabs or any other features. All it does is size and position 
54612  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
54613  */
54614 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
54615     this.mgr = mgr;
54616     this.position  = pos;
54617     this.events = {
54618         /**
54619          * @scope Roo.BasicLayoutRegion
54620          */
54621         
54622         /**
54623          * @event beforeremove
54624          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
54625          * @param {Roo.LayoutRegion} this
54626          * @param {Roo.ContentPanel} panel The panel
54627          * @param {Object} e The cancel event object
54628          */
54629         "beforeremove" : true,
54630         /**
54631          * @event invalidated
54632          * Fires when the layout for this region is changed.
54633          * @param {Roo.LayoutRegion} this
54634          */
54635         "invalidated" : true,
54636         /**
54637          * @event visibilitychange
54638          * Fires when this region is shown or hidden 
54639          * @param {Roo.LayoutRegion} this
54640          * @param {Boolean} visibility true or false
54641          */
54642         "visibilitychange" : true,
54643         /**
54644          * @event paneladded
54645          * Fires when a panel is added. 
54646          * @param {Roo.LayoutRegion} this
54647          * @param {Roo.ContentPanel} panel The panel
54648          */
54649         "paneladded" : true,
54650         /**
54651          * @event panelremoved
54652          * Fires when a panel is removed. 
54653          * @param {Roo.LayoutRegion} this
54654          * @param {Roo.ContentPanel} panel The panel
54655          */
54656         "panelremoved" : true,
54657         /**
54658          * @event beforecollapse
54659          * Fires when this region before collapse.
54660          * @param {Roo.LayoutRegion} this
54661          */
54662         "beforecollapse" : true,
54663         /**
54664          * @event collapsed
54665          * Fires when this region is collapsed.
54666          * @param {Roo.LayoutRegion} this
54667          */
54668         "collapsed" : true,
54669         /**
54670          * @event expanded
54671          * Fires when this region is expanded.
54672          * @param {Roo.LayoutRegion} this
54673          */
54674         "expanded" : true,
54675         /**
54676          * @event slideshow
54677          * Fires when this region is slid into view.
54678          * @param {Roo.LayoutRegion} this
54679          */
54680         "slideshow" : true,
54681         /**
54682          * @event slidehide
54683          * Fires when this region slides out of view. 
54684          * @param {Roo.LayoutRegion} this
54685          */
54686         "slidehide" : true,
54687         /**
54688          * @event panelactivated
54689          * Fires when a panel is activated. 
54690          * @param {Roo.LayoutRegion} this
54691          * @param {Roo.ContentPanel} panel The activated panel
54692          */
54693         "panelactivated" : true,
54694         /**
54695          * @event resized
54696          * Fires when the user resizes this region. 
54697          * @param {Roo.LayoutRegion} this
54698          * @param {Number} newSize The new size (width for east/west, height for north/south)
54699          */
54700         "resized" : true
54701     };
54702     /** A collection of panels in this region. @type Roo.util.MixedCollection */
54703     this.panels = new Roo.util.MixedCollection();
54704     this.panels.getKey = this.getPanelId.createDelegate(this);
54705     this.box = null;
54706     this.activePanel = null;
54707     // ensure listeners are added...
54708     
54709     if (config.listeners || config.events) {
54710         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
54711             listeners : config.listeners || {},
54712             events : config.events || {}
54713         });
54714     }
54715     
54716     if(skipConfig !== true){
54717         this.applyConfig(config);
54718     }
54719 };
54720
54721 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
54722     getPanelId : function(p){
54723         return p.getId();
54724     },
54725     
54726     applyConfig : function(config){
54727         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54728         this.config = config;
54729         
54730     },
54731     
54732     /**
54733      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
54734      * the width, for horizontal (north, south) the height.
54735      * @param {Number} newSize The new width or height
54736      */
54737     resizeTo : function(newSize){
54738         var el = this.el ? this.el :
54739                  (this.activePanel ? this.activePanel.getEl() : null);
54740         if(el){
54741             switch(this.position){
54742                 case "east":
54743                 case "west":
54744                     el.setWidth(newSize);
54745                     this.fireEvent("resized", this, newSize);
54746                 break;
54747                 case "north":
54748                 case "south":
54749                     el.setHeight(newSize);
54750                     this.fireEvent("resized", this, newSize);
54751                 break;                
54752             }
54753         }
54754     },
54755     
54756     getBox : function(){
54757         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54758     },
54759     
54760     getMargins : function(){
54761         return this.margins;
54762     },
54763     
54764     updateBox : function(box){
54765         this.box = box;
54766         var el = this.activePanel.getEl();
54767         el.dom.style.left = box.x + "px";
54768         el.dom.style.top = box.y + "px";
54769         this.activePanel.setSize(box.width, box.height);
54770     },
54771     
54772     /**
54773      * Returns the container element for this region.
54774      * @return {Roo.Element}
54775      */
54776     getEl : function(){
54777         return this.activePanel;
54778     },
54779     
54780     /**
54781      * Returns true if this region is currently visible.
54782      * @return {Boolean}
54783      */
54784     isVisible : function(){
54785         return this.activePanel ? true : false;
54786     },
54787     
54788     setActivePanel : function(panel){
54789         panel = this.getPanel(panel);
54790         if(this.activePanel && this.activePanel != panel){
54791             this.activePanel.setActiveState(false);
54792             this.activePanel.getEl().setLeftTop(-10000,-10000);
54793         }
54794         this.activePanel = panel;
54795         panel.setActiveState(true);
54796         if(this.box){
54797             panel.setSize(this.box.width, this.box.height);
54798         }
54799         this.fireEvent("panelactivated", this, panel);
54800         this.fireEvent("invalidated");
54801     },
54802     
54803     /**
54804      * Show the specified panel.
54805      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54806      * @return {Roo.ContentPanel} The shown panel or null
54807      */
54808     showPanel : function(panel){
54809         if(panel = this.getPanel(panel)){
54810             this.setActivePanel(panel);
54811         }
54812         return panel;
54813     },
54814     
54815     /**
54816      * Get the active panel for this region.
54817      * @return {Roo.ContentPanel} The active panel or null
54818      */
54819     getActivePanel : function(){
54820         return this.activePanel;
54821     },
54822     
54823     /**
54824      * Add the passed ContentPanel(s)
54825      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54826      * @return {Roo.ContentPanel} The panel added (if only one was added)
54827      */
54828     add : function(panel){
54829         if(arguments.length > 1){
54830             for(var i = 0, len = arguments.length; i < len; i++) {
54831                 this.add(arguments[i]);
54832             }
54833             return null;
54834         }
54835         if(this.hasPanel(panel)){
54836             this.showPanel(panel);
54837             return panel;
54838         }
54839         var el = panel.getEl();
54840         if(el.dom.parentNode != this.mgr.el.dom){
54841             this.mgr.el.dom.appendChild(el.dom);
54842         }
54843         if(panel.setRegion){
54844             panel.setRegion(this);
54845         }
54846         this.panels.add(panel);
54847         el.setStyle("position", "absolute");
54848         if(!panel.background){
54849             this.setActivePanel(panel);
54850             if(this.config.initialSize && this.panels.getCount()==1){
54851                 this.resizeTo(this.config.initialSize);
54852             }
54853         }
54854         this.fireEvent("paneladded", this, panel);
54855         return panel;
54856     },
54857     
54858     /**
54859      * Returns true if the panel is in this region.
54860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54861      * @return {Boolean}
54862      */
54863     hasPanel : function(panel){
54864         if(typeof panel == "object"){ // must be panel obj
54865             panel = panel.getId();
54866         }
54867         return this.getPanel(panel) ? true : false;
54868     },
54869     
54870     /**
54871      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54872      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54873      * @param {Boolean} preservePanel Overrides the config preservePanel option
54874      * @return {Roo.ContentPanel} The panel that was removed
54875      */
54876     remove : function(panel, preservePanel){
54877         panel = this.getPanel(panel);
54878         if(!panel){
54879             return null;
54880         }
54881         var e = {};
54882         this.fireEvent("beforeremove", this, panel, e);
54883         if(e.cancel === true){
54884             return null;
54885         }
54886         var panelId = panel.getId();
54887         this.panels.removeKey(panelId);
54888         return panel;
54889     },
54890     
54891     /**
54892      * Returns the panel specified or null if it's not in this region.
54893      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54894      * @return {Roo.ContentPanel}
54895      */
54896     getPanel : function(id){
54897         if(typeof id == "object"){ // must be panel obj
54898             return id;
54899         }
54900         return this.panels.get(id);
54901     },
54902     
54903     /**
54904      * Returns this regions position (north/south/east/west/center).
54905      * @return {String} 
54906      */
54907     getPosition: function(){
54908         return this.position;    
54909     }
54910 });/*
54911  * Based on:
54912  * Ext JS Library 1.1.1
54913  * Copyright(c) 2006-2007, Ext JS, LLC.
54914  *
54915  * Originally Released Under LGPL - original licence link has changed is not relivant.
54916  *
54917  * Fork - LGPL
54918  * <script type="text/javascript">
54919  */
54920  
54921 /**
54922  * @class Roo.LayoutRegion
54923  * @extends Roo.BasicLayoutRegion
54924  * This class represents a region in a layout manager.
54925  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54926  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54927  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54928  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54929  * @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})
54930  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54931  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54932  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54933  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54934  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54935  * @cfg {String}    title           The title for the region (overrides panel titles)
54936  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54937  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54938  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54939  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54940  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54941  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54942  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54943  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54944  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54945  * @cfg {Boolean}   showPin         True to show a pin button
54946  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54947  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54948  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54949  * @cfg {Number}    width           For East/West panels
54950  * @cfg {Number}    height          For North/South panels
54951  * @cfg {Boolean}   split           To show the splitter
54952  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54953  */
54954 Roo.LayoutRegion = function(mgr, config, pos){
54955     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54956     var dh = Roo.DomHelper;
54957     /** This region's container element 
54958     * @type Roo.Element */
54959     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54960     /** This region's title element 
54961     * @type Roo.Element */
54962
54963     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54964         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54965         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54966     ]}, true);
54967     this.titleEl.enableDisplayMode();
54968     /** This region's title text element 
54969     * @type HTMLElement */
54970     this.titleTextEl = this.titleEl.dom.firstChild;
54971     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54972     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54973     this.closeBtn.enableDisplayMode();
54974     this.closeBtn.on("click", this.closeClicked, this);
54975     this.closeBtn.hide();
54976
54977     this.createBody(config);
54978     this.visible = true;
54979     this.collapsed = false;
54980
54981     if(config.hideWhenEmpty){
54982         this.hide();
54983         this.on("paneladded", this.validateVisibility, this);
54984         this.on("panelremoved", this.validateVisibility, this);
54985     }
54986     this.applyConfig(config);
54987 };
54988
54989 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54990
54991     createBody : function(){
54992         /** This region's body element 
54993         * @type Roo.Element */
54994         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54995     },
54996
54997     applyConfig : function(c){
54998         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54999             var dh = Roo.DomHelper;
55000             if(c.titlebar !== false){
55001                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55002                 this.collapseBtn.on("click", this.collapse, this);
55003                 this.collapseBtn.enableDisplayMode();
55004
55005                 if(c.showPin === true || this.showPin){
55006                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55007                     this.stickBtn.enableDisplayMode();
55008                     this.stickBtn.on("click", this.expand, this);
55009                     this.stickBtn.hide();
55010                 }
55011             }
55012             /** This region's collapsed element
55013             * @type Roo.Element */
55014             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55015                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55016             ]}, true);
55017             if(c.floatable !== false){
55018                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55019                this.collapsedEl.on("click", this.collapseClick, this);
55020             }
55021
55022             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55023                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55024                    id: "message", unselectable: "on", style:{"float":"left"}});
55025                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55026              }
55027             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55028             this.expandBtn.on("click", this.expand, this);
55029         }
55030         if(this.collapseBtn){
55031             this.collapseBtn.setVisible(c.collapsible == true);
55032         }
55033         this.cmargins = c.cmargins || this.cmargins ||
55034                          (this.position == "west" || this.position == "east" ?
55035                              {top: 0, left: 2, right:2, bottom: 0} :
55036                              {top: 2, left: 0, right:0, bottom: 2});
55037         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55038         this.bottomTabs = c.tabPosition != "top";
55039         this.autoScroll = c.autoScroll || false;
55040         if(this.autoScroll){
55041             this.bodyEl.setStyle("overflow", "auto");
55042         }else{
55043             this.bodyEl.setStyle("overflow", "hidden");
55044         }
55045         //if(c.titlebar !== false){
55046             if((!c.titlebar && !c.title) || c.titlebar === false){
55047                 this.titleEl.hide();
55048             }else{
55049                 this.titleEl.show();
55050                 if(c.title){
55051                     this.titleTextEl.innerHTML = c.title;
55052                 }
55053             }
55054         //}
55055         this.duration = c.duration || .30;
55056         this.slideDuration = c.slideDuration || .45;
55057         this.config = c;
55058         if(c.collapsed){
55059             this.collapse(true);
55060         }
55061         if(c.hidden){
55062             this.hide();
55063         }
55064     },
55065     /**
55066      * Returns true if this region is currently visible.
55067      * @return {Boolean}
55068      */
55069     isVisible : function(){
55070         return this.visible;
55071     },
55072
55073     /**
55074      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55075      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55076      */
55077     setCollapsedTitle : function(title){
55078         title = title || "&#160;";
55079         if(this.collapsedTitleTextEl){
55080             this.collapsedTitleTextEl.innerHTML = title;
55081         }
55082     },
55083
55084     getBox : function(){
55085         var b;
55086         if(!this.collapsed){
55087             b = this.el.getBox(false, true);
55088         }else{
55089             b = this.collapsedEl.getBox(false, true);
55090         }
55091         return b;
55092     },
55093
55094     getMargins : function(){
55095         return this.collapsed ? this.cmargins : this.margins;
55096     },
55097
55098     highlight : function(){
55099         this.el.addClass("x-layout-panel-dragover");
55100     },
55101
55102     unhighlight : function(){
55103         this.el.removeClass("x-layout-panel-dragover");
55104     },
55105
55106     updateBox : function(box){
55107         this.box = box;
55108         if(!this.collapsed){
55109             this.el.dom.style.left = box.x + "px";
55110             this.el.dom.style.top = box.y + "px";
55111             this.updateBody(box.width, box.height);
55112         }else{
55113             this.collapsedEl.dom.style.left = box.x + "px";
55114             this.collapsedEl.dom.style.top = box.y + "px";
55115             this.collapsedEl.setSize(box.width, box.height);
55116         }
55117         if(this.tabs){
55118             this.tabs.autoSizeTabs();
55119         }
55120     },
55121
55122     updateBody : function(w, h){
55123         if(w !== null){
55124             this.el.setWidth(w);
55125             w -= this.el.getBorderWidth("rl");
55126             if(this.config.adjustments){
55127                 w += this.config.adjustments[0];
55128             }
55129         }
55130         if(h !== null){
55131             this.el.setHeight(h);
55132             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55133             h -= this.el.getBorderWidth("tb");
55134             if(this.config.adjustments){
55135                 h += this.config.adjustments[1];
55136             }
55137             this.bodyEl.setHeight(h);
55138             if(this.tabs){
55139                 h = this.tabs.syncHeight(h);
55140             }
55141         }
55142         if(this.panelSize){
55143             w = w !== null ? w : this.panelSize.width;
55144             h = h !== null ? h : this.panelSize.height;
55145         }
55146         if(this.activePanel){
55147             var el = this.activePanel.getEl();
55148             w = w !== null ? w : el.getWidth();
55149             h = h !== null ? h : el.getHeight();
55150             this.panelSize = {width: w, height: h};
55151             this.activePanel.setSize(w, h);
55152         }
55153         if(Roo.isIE && this.tabs){
55154             this.tabs.el.repaint();
55155         }
55156     },
55157
55158     /**
55159      * Returns the container element for this region.
55160      * @return {Roo.Element}
55161      */
55162     getEl : function(){
55163         return this.el;
55164     },
55165
55166     /**
55167      * Hides this region.
55168      */
55169     hide : function(){
55170         if(!this.collapsed){
55171             this.el.dom.style.left = "-2000px";
55172             this.el.hide();
55173         }else{
55174             this.collapsedEl.dom.style.left = "-2000px";
55175             this.collapsedEl.hide();
55176         }
55177         this.visible = false;
55178         this.fireEvent("visibilitychange", this, false);
55179     },
55180
55181     /**
55182      * Shows this region if it was previously hidden.
55183      */
55184     show : function(){
55185         if(!this.collapsed){
55186             this.el.show();
55187         }else{
55188             this.collapsedEl.show();
55189         }
55190         this.visible = true;
55191         this.fireEvent("visibilitychange", this, true);
55192     },
55193
55194     closeClicked : function(){
55195         if(this.activePanel){
55196             this.remove(this.activePanel);
55197         }
55198     },
55199
55200     collapseClick : function(e){
55201         if(this.isSlid){
55202            e.stopPropagation();
55203            this.slideIn();
55204         }else{
55205            e.stopPropagation();
55206            this.slideOut();
55207         }
55208     },
55209
55210     /**
55211      * Collapses this region.
55212      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55213      */
55214     collapse : function(skipAnim, skipCheck){
55215         if(this.collapsed) {
55216             return;
55217         }
55218         
55219         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55220             
55221             this.collapsed = true;
55222             if(this.split){
55223                 this.split.el.hide();
55224             }
55225             if(this.config.animate && skipAnim !== true){
55226                 this.fireEvent("invalidated", this);
55227                 this.animateCollapse();
55228             }else{
55229                 this.el.setLocation(-20000,-20000);
55230                 this.el.hide();
55231                 this.collapsedEl.show();
55232                 this.fireEvent("collapsed", this);
55233                 this.fireEvent("invalidated", this);
55234             }
55235         }
55236         
55237     },
55238
55239     animateCollapse : function(){
55240         // overridden
55241     },
55242
55243     /**
55244      * Expands this region if it was previously collapsed.
55245      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55246      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55247      */
55248     expand : function(e, skipAnim){
55249         if(e) {
55250             e.stopPropagation();
55251         }
55252         if(!this.collapsed || this.el.hasActiveFx()) {
55253             return;
55254         }
55255         if(this.isSlid){
55256             this.afterSlideIn();
55257             skipAnim = true;
55258         }
55259         this.collapsed = false;
55260         if(this.config.animate && skipAnim !== true){
55261             this.animateExpand();
55262         }else{
55263             this.el.show();
55264             if(this.split){
55265                 this.split.el.show();
55266             }
55267             this.collapsedEl.setLocation(-2000,-2000);
55268             this.collapsedEl.hide();
55269             this.fireEvent("invalidated", this);
55270             this.fireEvent("expanded", this);
55271         }
55272     },
55273
55274     animateExpand : function(){
55275         // overridden
55276     },
55277
55278     initTabs : function()
55279     {
55280         this.bodyEl.setStyle("overflow", "hidden");
55281         var ts = new Roo.TabPanel(
55282                 this.bodyEl.dom,
55283                 {
55284                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55285                     disableTooltips: this.config.disableTabTips,
55286                     toolbar : this.config.toolbar
55287                 }
55288         );
55289         if(this.config.hideTabs){
55290             ts.stripWrap.setDisplayed(false);
55291         }
55292         this.tabs = ts;
55293         ts.resizeTabs = this.config.resizeTabs === true;
55294         ts.minTabWidth = this.config.minTabWidth || 40;
55295         ts.maxTabWidth = this.config.maxTabWidth || 250;
55296         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55297         ts.monitorResize = false;
55298         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55299         ts.bodyEl.addClass('x-layout-tabs-body');
55300         this.panels.each(this.initPanelAsTab, this);
55301     },
55302
55303     initPanelAsTab : function(panel){
55304         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55305                     this.config.closeOnTab && panel.isClosable());
55306         if(panel.tabTip !== undefined){
55307             ti.setTooltip(panel.tabTip);
55308         }
55309         ti.on("activate", function(){
55310               this.setActivePanel(panel);
55311         }, this);
55312         if(this.config.closeOnTab){
55313             ti.on("beforeclose", function(t, e){
55314                 e.cancel = true;
55315                 this.remove(panel);
55316             }, this);
55317         }
55318         return ti;
55319     },
55320
55321     updatePanelTitle : function(panel, title){
55322         if(this.activePanel == panel){
55323             this.updateTitle(title);
55324         }
55325         if(this.tabs){
55326             var ti = this.tabs.getTab(panel.getEl().id);
55327             ti.setText(title);
55328             if(panel.tabTip !== undefined){
55329                 ti.setTooltip(panel.tabTip);
55330             }
55331         }
55332     },
55333
55334     updateTitle : function(title){
55335         if(this.titleTextEl && !this.config.title){
55336             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55337         }
55338     },
55339
55340     setActivePanel : function(panel){
55341         panel = this.getPanel(panel);
55342         if(this.activePanel && this.activePanel != panel){
55343             this.activePanel.setActiveState(false);
55344         }
55345         this.activePanel = panel;
55346         panel.setActiveState(true);
55347         if(this.panelSize){
55348             panel.setSize(this.panelSize.width, this.panelSize.height);
55349         }
55350         if(this.closeBtn){
55351             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55352         }
55353         this.updateTitle(panel.getTitle());
55354         if(this.tabs){
55355             this.fireEvent("invalidated", this);
55356         }
55357         this.fireEvent("panelactivated", this, panel);
55358     },
55359
55360     /**
55361      * Shows the specified panel.
55362      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55363      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55364      */
55365     showPanel : function(panel)
55366     {
55367         panel = this.getPanel(panel);
55368         if(panel){
55369             if(this.tabs){
55370                 var tab = this.tabs.getTab(panel.getEl().id);
55371                 if(tab.isHidden()){
55372                     this.tabs.unhideTab(tab.id);
55373                 }
55374                 tab.activate();
55375             }else{
55376                 this.setActivePanel(panel);
55377             }
55378         }
55379         return panel;
55380     },
55381
55382     /**
55383      * Get the active panel for this region.
55384      * @return {Roo.ContentPanel} The active panel or null
55385      */
55386     getActivePanel : function(){
55387         return this.activePanel;
55388     },
55389
55390     validateVisibility : function(){
55391         if(this.panels.getCount() < 1){
55392             this.updateTitle("&#160;");
55393             this.closeBtn.hide();
55394             this.hide();
55395         }else{
55396             if(!this.isVisible()){
55397                 this.show();
55398             }
55399         }
55400     },
55401
55402     /**
55403      * Adds the passed ContentPanel(s) to this region.
55404      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55405      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55406      */
55407     add : function(panel){
55408         if(arguments.length > 1){
55409             for(var i = 0, len = arguments.length; i < len; i++) {
55410                 this.add(arguments[i]);
55411             }
55412             return null;
55413         }
55414         if(this.hasPanel(panel)){
55415             this.showPanel(panel);
55416             return panel;
55417         }
55418         panel.setRegion(this);
55419         this.panels.add(panel);
55420         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
55421             this.bodyEl.dom.appendChild(panel.getEl().dom);
55422             if(panel.background !== true){
55423                 this.setActivePanel(panel);
55424             }
55425             this.fireEvent("paneladded", this, panel);
55426             return panel;
55427         }
55428         if(!this.tabs){
55429             this.initTabs();
55430         }else{
55431             this.initPanelAsTab(panel);
55432         }
55433         if(panel.background !== true){
55434             this.tabs.activate(panel.getEl().id);
55435         }
55436         this.fireEvent("paneladded", this, panel);
55437         return panel;
55438     },
55439
55440     /**
55441      * Hides the tab for the specified panel.
55442      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55443      */
55444     hidePanel : function(panel){
55445         if(this.tabs && (panel = this.getPanel(panel))){
55446             this.tabs.hideTab(panel.getEl().id);
55447         }
55448     },
55449
55450     /**
55451      * Unhides the tab for a previously hidden panel.
55452      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55453      */
55454     unhidePanel : function(panel){
55455         if(this.tabs && (panel = this.getPanel(panel))){
55456             this.tabs.unhideTab(panel.getEl().id);
55457         }
55458     },
55459
55460     clearPanels : function(){
55461         while(this.panels.getCount() > 0){
55462              this.remove(this.panels.first());
55463         }
55464     },
55465
55466     /**
55467      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55468      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55469      * @param {Boolean} preservePanel Overrides the config preservePanel option
55470      * @return {Roo.ContentPanel} The panel that was removed
55471      */
55472     remove : function(panel, preservePanel){
55473         panel = this.getPanel(panel);
55474         if(!panel){
55475             return null;
55476         }
55477         var e = {};
55478         this.fireEvent("beforeremove", this, panel, e);
55479         if(e.cancel === true){
55480             return null;
55481         }
55482         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
55483         var panelId = panel.getId();
55484         this.panels.removeKey(panelId);
55485         if(preservePanel){
55486             document.body.appendChild(panel.getEl().dom);
55487         }
55488         if(this.tabs){
55489             this.tabs.removeTab(panel.getEl().id);
55490         }else if (!preservePanel){
55491             this.bodyEl.dom.removeChild(panel.getEl().dom);
55492         }
55493         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
55494             var p = this.panels.first();
55495             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
55496             tempEl.appendChild(p.getEl().dom);
55497             this.bodyEl.update("");
55498             this.bodyEl.dom.appendChild(p.getEl().dom);
55499             tempEl = null;
55500             this.updateTitle(p.getTitle());
55501             this.tabs = null;
55502             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55503             this.setActivePanel(p);
55504         }
55505         panel.setRegion(null);
55506         if(this.activePanel == panel){
55507             this.activePanel = null;
55508         }
55509         if(this.config.autoDestroy !== false && preservePanel !== true){
55510             try{panel.destroy();}catch(e){}
55511         }
55512         this.fireEvent("panelremoved", this, panel);
55513         return panel;
55514     },
55515
55516     /**
55517      * Returns the TabPanel component used by this region
55518      * @return {Roo.TabPanel}
55519      */
55520     getTabs : function(){
55521         return this.tabs;
55522     },
55523
55524     createTool : function(parentEl, className){
55525         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
55526             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
55527         btn.addClassOnOver("x-layout-tools-button-over");
55528         return btn;
55529     }
55530 });/*
55531  * Based on:
55532  * Ext JS Library 1.1.1
55533  * Copyright(c) 2006-2007, Ext JS, LLC.
55534  *
55535  * Originally Released Under LGPL - original licence link has changed is not relivant.
55536  *
55537  * Fork - LGPL
55538  * <script type="text/javascript">
55539  */
55540  
55541
55542
55543 /**
55544  * @class Roo.SplitLayoutRegion
55545  * @extends Roo.LayoutRegion
55546  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
55547  */
55548 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
55549     this.cursor = cursor;
55550     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
55551 };
55552
55553 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
55554     splitTip : "Drag to resize.",
55555     collapsibleSplitTip : "Drag to resize. Double click to hide.",
55556     useSplitTips : false,
55557
55558     applyConfig : function(config){
55559         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
55560         if(config.split){
55561             if(!this.split){
55562                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
55563                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
55564                 /** The SplitBar for this region 
55565                 * @type Roo.SplitBar */
55566                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
55567                 this.split.on("moved", this.onSplitMove, this);
55568                 this.split.useShim = config.useShim === true;
55569                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
55570                 if(this.useSplitTips){
55571                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
55572                 }
55573                 if(config.collapsible){
55574                     this.split.el.on("dblclick", this.collapse,  this);
55575                 }
55576             }
55577             if(typeof config.minSize != "undefined"){
55578                 this.split.minSize = config.minSize;
55579             }
55580             if(typeof config.maxSize != "undefined"){
55581                 this.split.maxSize = config.maxSize;
55582             }
55583             if(config.hideWhenEmpty || config.hidden || config.collapsed){
55584                 this.hideSplitter();
55585             }
55586         }
55587     },
55588
55589     getHMaxSize : function(){
55590          var cmax = this.config.maxSize || 10000;
55591          var center = this.mgr.getRegion("center");
55592          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
55593     },
55594
55595     getVMaxSize : function(){
55596          var cmax = this.config.maxSize || 10000;
55597          var center = this.mgr.getRegion("center");
55598          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
55599     },
55600
55601     onSplitMove : function(split, newSize){
55602         this.fireEvent("resized", this, newSize);
55603     },
55604     
55605     /** 
55606      * Returns the {@link Roo.SplitBar} for this region.
55607      * @return {Roo.SplitBar}
55608      */
55609     getSplitBar : function(){
55610         return this.split;
55611     },
55612     
55613     hide : function(){
55614         this.hideSplitter();
55615         Roo.SplitLayoutRegion.superclass.hide.call(this);
55616     },
55617
55618     hideSplitter : function(){
55619         if(this.split){
55620             this.split.el.setLocation(-2000,-2000);
55621             this.split.el.hide();
55622         }
55623     },
55624
55625     show : function(){
55626         if(this.split){
55627             this.split.el.show();
55628         }
55629         Roo.SplitLayoutRegion.superclass.show.call(this);
55630     },
55631     
55632     beforeSlide: function(){
55633         if(Roo.isGecko){// firefox overflow auto bug workaround
55634             this.bodyEl.clip();
55635             if(this.tabs) {
55636                 this.tabs.bodyEl.clip();
55637             }
55638             if(this.activePanel){
55639                 this.activePanel.getEl().clip();
55640                 
55641                 if(this.activePanel.beforeSlide){
55642                     this.activePanel.beforeSlide();
55643                 }
55644             }
55645         }
55646     },
55647     
55648     afterSlide : function(){
55649         if(Roo.isGecko){// firefox overflow auto bug workaround
55650             this.bodyEl.unclip();
55651             if(this.tabs) {
55652                 this.tabs.bodyEl.unclip();
55653             }
55654             if(this.activePanel){
55655                 this.activePanel.getEl().unclip();
55656                 if(this.activePanel.afterSlide){
55657                     this.activePanel.afterSlide();
55658                 }
55659             }
55660         }
55661     },
55662
55663     initAutoHide : function(){
55664         if(this.autoHide !== false){
55665             if(!this.autoHideHd){
55666                 var st = new Roo.util.DelayedTask(this.slideIn, this);
55667                 this.autoHideHd = {
55668                     "mouseout": function(e){
55669                         if(!e.within(this.el, true)){
55670                             st.delay(500);
55671                         }
55672                     },
55673                     "mouseover" : function(e){
55674                         st.cancel();
55675                     },
55676                     scope : this
55677                 };
55678             }
55679             this.el.on(this.autoHideHd);
55680         }
55681     },
55682
55683     clearAutoHide : function(){
55684         if(this.autoHide !== false){
55685             this.el.un("mouseout", this.autoHideHd.mouseout);
55686             this.el.un("mouseover", this.autoHideHd.mouseover);
55687         }
55688     },
55689
55690     clearMonitor : function(){
55691         Roo.get(document).un("click", this.slideInIf, this);
55692     },
55693
55694     // these names are backwards but not changed for compat
55695     slideOut : function(){
55696         if(this.isSlid || this.el.hasActiveFx()){
55697             return;
55698         }
55699         this.isSlid = true;
55700         if(this.collapseBtn){
55701             this.collapseBtn.hide();
55702         }
55703         this.closeBtnState = this.closeBtn.getStyle('display');
55704         this.closeBtn.hide();
55705         if(this.stickBtn){
55706             this.stickBtn.show();
55707         }
55708         this.el.show();
55709         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
55710         this.beforeSlide();
55711         this.el.setStyle("z-index", 10001);
55712         this.el.slideIn(this.getSlideAnchor(), {
55713             callback: function(){
55714                 this.afterSlide();
55715                 this.initAutoHide();
55716                 Roo.get(document).on("click", this.slideInIf, this);
55717                 this.fireEvent("slideshow", this);
55718             },
55719             scope: this,
55720             block: true
55721         });
55722     },
55723
55724     afterSlideIn : function(){
55725         this.clearAutoHide();
55726         this.isSlid = false;
55727         this.clearMonitor();
55728         this.el.setStyle("z-index", "");
55729         if(this.collapseBtn){
55730             this.collapseBtn.show();
55731         }
55732         this.closeBtn.setStyle('display', this.closeBtnState);
55733         if(this.stickBtn){
55734             this.stickBtn.hide();
55735         }
55736         this.fireEvent("slidehide", this);
55737     },
55738
55739     slideIn : function(cb){
55740         if(!this.isSlid || this.el.hasActiveFx()){
55741             Roo.callback(cb);
55742             return;
55743         }
55744         this.isSlid = false;
55745         this.beforeSlide();
55746         this.el.slideOut(this.getSlideAnchor(), {
55747             callback: function(){
55748                 this.el.setLeftTop(-10000, -10000);
55749                 this.afterSlide();
55750                 this.afterSlideIn();
55751                 Roo.callback(cb);
55752             },
55753             scope: this,
55754             block: true
55755         });
55756     },
55757     
55758     slideInIf : function(e){
55759         if(!e.within(this.el)){
55760             this.slideIn();
55761         }
55762     },
55763
55764     animateCollapse : function(){
55765         this.beforeSlide();
55766         this.el.setStyle("z-index", 20000);
55767         var anchor = this.getSlideAnchor();
55768         this.el.slideOut(anchor, {
55769             callback : function(){
55770                 this.el.setStyle("z-index", "");
55771                 this.collapsedEl.slideIn(anchor, {duration:.3});
55772                 this.afterSlide();
55773                 this.el.setLocation(-10000,-10000);
55774                 this.el.hide();
55775                 this.fireEvent("collapsed", this);
55776             },
55777             scope: this,
55778             block: true
55779         });
55780     },
55781
55782     animateExpand : function(){
55783         this.beforeSlide();
55784         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55785         this.el.setStyle("z-index", 20000);
55786         this.collapsedEl.hide({
55787             duration:.1
55788         });
55789         this.el.slideIn(this.getSlideAnchor(), {
55790             callback : function(){
55791                 this.el.setStyle("z-index", "");
55792                 this.afterSlide();
55793                 if(this.split){
55794                     this.split.el.show();
55795                 }
55796                 this.fireEvent("invalidated", this);
55797                 this.fireEvent("expanded", this);
55798             },
55799             scope: this,
55800             block: true
55801         });
55802     },
55803
55804     anchors : {
55805         "west" : "left",
55806         "east" : "right",
55807         "north" : "top",
55808         "south" : "bottom"
55809     },
55810
55811     sanchors : {
55812         "west" : "l",
55813         "east" : "r",
55814         "north" : "t",
55815         "south" : "b"
55816     },
55817
55818     canchors : {
55819         "west" : "tl-tr",
55820         "east" : "tr-tl",
55821         "north" : "tl-bl",
55822         "south" : "bl-tl"
55823     },
55824
55825     getAnchor : function(){
55826         return this.anchors[this.position];
55827     },
55828
55829     getCollapseAnchor : function(){
55830         return this.canchors[this.position];
55831     },
55832
55833     getSlideAnchor : function(){
55834         return this.sanchors[this.position];
55835     },
55836
55837     getAlignAdj : function(){
55838         var cm = this.cmargins;
55839         switch(this.position){
55840             case "west":
55841                 return [0, 0];
55842             break;
55843             case "east":
55844                 return [0, 0];
55845             break;
55846             case "north":
55847                 return [0, 0];
55848             break;
55849             case "south":
55850                 return [0, 0];
55851             break;
55852         }
55853     },
55854
55855     getExpandAdj : function(){
55856         var c = this.collapsedEl, cm = this.cmargins;
55857         switch(this.position){
55858             case "west":
55859                 return [-(cm.right+c.getWidth()+cm.left), 0];
55860             break;
55861             case "east":
55862                 return [cm.right+c.getWidth()+cm.left, 0];
55863             break;
55864             case "north":
55865                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55866             break;
55867             case "south":
55868                 return [0, cm.top+cm.bottom+c.getHeight()];
55869             break;
55870         }
55871     }
55872 });/*
55873  * Based on:
55874  * Ext JS Library 1.1.1
55875  * Copyright(c) 2006-2007, Ext JS, LLC.
55876  *
55877  * Originally Released Under LGPL - original licence link has changed is not relivant.
55878  *
55879  * Fork - LGPL
55880  * <script type="text/javascript">
55881  */
55882 /*
55883  * These classes are private internal classes
55884  */
55885 Roo.CenterLayoutRegion = function(mgr, config){
55886     Roo.LayoutRegion.call(this, mgr, config, "center");
55887     this.visible = true;
55888     this.minWidth = config.minWidth || 20;
55889     this.minHeight = config.minHeight || 20;
55890 };
55891
55892 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55893     hide : function(){
55894         // center panel can't be hidden
55895     },
55896     
55897     show : function(){
55898         // center panel can't be hidden
55899     },
55900     
55901     getMinWidth: function(){
55902         return this.minWidth;
55903     },
55904     
55905     getMinHeight: function(){
55906         return this.minHeight;
55907     }
55908 });
55909
55910
55911 Roo.NorthLayoutRegion = function(mgr, config){
55912     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55913     if(this.split){
55914         this.split.placement = Roo.SplitBar.TOP;
55915         this.split.orientation = Roo.SplitBar.VERTICAL;
55916         this.split.el.addClass("x-layout-split-v");
55917     }
55918     var size = config.initialSize || config.height;
55919     if(typeof size != "undefined"){
55920         this.el.setHeight(size);
55921     }
55922 };
55923 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55924     orientation: Roo.SplitBar.VERTICAL,
55925     getBox : function(){
55926         if(this.collapsed){
55927             return this.collapsedEl.getBox();
55928         }
55929         var box = this.el.getBox();
55930         if(this.split){
55931             box.height += this.split.el.getHeight();
55932         }
55933         return box;
55934     },
55935     
55936     updateBox : function(box){
55937         if(this.split && !this.collapsed){
55938             box.height -= this.split.el.getHeight();
55939             this.split.el.setLeft(box.x);
55940             this.split.el.setTop(box.y+box.height);
55941             this.split.el.setWidth(box.width);
55942         }
55943         if(this.collapsed){
55944             this.updateBody(box.width, null);
55945         }
55946         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55947     }
55948 });
55949
55950 Roo.SouthLayoutRegion = function(mgr, config){
55951     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55952     if(this.split){
55953         this.split.placement = Roo.SplitBar.BOTTOM;
55954         this.split.orientation = Roo.SplitBar.VERTICAL;
55955         this.split.el.addClass("x-layout-split-v");
55956     }
55957     var size = config.initialSize || config.height;
55958     if(typeof size != "undefined"){
55959         this.el.setHeight(size);
55960     }
55961 };
55962 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55963     orientation: Roo.SplitBar.VERTICAL,
55964     getBox : function(){
55965         if(this.collapsed){
55966             return this.collapsedEl.getBox();
55967         }
55968         var box = this.el.getBox();
55969         if(this.split){
55970             var sh = this.split.el.getHeight();
55971             box.height += sh;
55972             box.y -= sh;
55973         }
55974         return box;
55975     },
55976     
55977     updateBox : function(box){
55978         if(this.split && !this.collapsed){
55979             var sh = this.split.el.getHeight();
55980             box.height -= sh;
55981             box.y += sh;
55982             this.split.el.setLeft(box.x);
55983             this.split.el.setTop(box.y-sh);
55984             this.split.el.setWidth(box.width);
55985         }
55986         if(this.collapsed){
55987             this.updateBody(box.width, null);
55988         }
55989         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55990     }
55991 });
55992
55993 Roo.EastLayoutRegion = function(mgr, config){
55994     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55995     if(this.split){
55996         this.split.placement = Roo.SplitBar.RIGHT;
55997         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55998         this.split.el.addClass("x-layout-split-h");
55999     }
56000     var size = config.initialSize || config.width;
56001     if(typeof size != "undefined"){
56002         this.el.setWidth(size);
56003     }
56004 };
56005 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56006     orientation: Roo.SplitBar.HORIZONTAL,
56007     getBox : function(){
56008         if(this.collapsed){
56009             return this.collapsedEl.getBox();
56010         }
56011         var box = this.el.getBox();
56012         if(this.split){
56013             var sw = this.split.el.getWidth();
56014             box.width += sw;
56015             box.x -= sw;
56016         }
56017         return box;
56018     },
56019
56020     updateBox : function(box){
56021         if(this.split && !this.collapsed){
56022             var sw = this.split.el.getWidth();
56023             box.width -= sw;
56024             this.split.el.setLeft(box.x);
56025             this.split.el.setTop(box.y);
56026             this.split.el.setHeight(box.height);
56027             box.x += sw;
56028         }
56029         if(this.collapsed){
56030             this.updateBody(null, box.height);
56031         }
56032         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56033     }
56034 });
56035
56036 Roo.WestLayoutRegion = function(mgr, config){
56037     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56038     if(this.split){
56039         this.split.placement = Roo.SplitBar.LEFT;
56040         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56041         this.split.el.addClass("x-layout-split-h");
56042     }
56043     var size = config.initialSize || config.width;
56044     if(typeof size != "undefined"){
56045         this.el.setWidth(size);
56046     }
56047 };
56048 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56049     orientation: Roo.SplitBar.HORIZONTAL,
56050     getBox : function(){
56051         if(this.collapsed){
56052             return this.collapsedEl.getBox();
56053         }
56054         var box = this.el.getBox();
56055         if(this.split){
56056             box.width += this.split.el.getWidth();
56057         }
56058         return box;
56059     },
56060     
56061     updateBox : function(box){
56062         if(this.split && !this.collapsed){
56063             var sw = this.split.el.getWidth();
56064             box.width -= sw;
56065             this.split.el.setLeft(box.x+box.width);
56066             this.split.el.setTop(box.y);
56067             this.split.el.setHeight(box.height);
56068         }
56069         if(this.collapsed){
56070             this.updateBody(null, box.height);
56071         }
56072         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56073     }
56074 });
56075 /*
56076  * Based on:
56077  * Ext JS Library 1.1.1
56078  * Copyright(c) 2006-2007, Ext JS, LLC.
56079  *
56080  * Originally Released Under LGPL - original licence link has changed is not relivant.
56081  *
56082  * Fork - LGPL
56083  * <script type="text/javascript">
56084  */
56085  
56086  
56087 /*
56088  * Private internal class for reading and applying state
56089  */
56090 Roo.LayoutStateManager = function(layout){
56091      // default empty state
56092      this.state = {
56093         north: {},
56094         south: {},
56095         east: {},
56096         west: {}       
56097     };
56098 };
56099
56100 Roo.LayoutStateManager.prototype = {
56101     init : function(layout, provider){
56102         this.provider = provider;
56103         var state = provider.get(layout.id+"-layout-state");
56104         if(state){
56105             var wasUpdating = layout.isUpdating();
56106             if(!wasUpdating){
56107                 layout.beginUpdate();
56108             }
56109             for(var key in state){
56110                 if(typeof state[key] != "function"){
56111                     var rstate = state[key];
56112                     var r = layout.getRegion(key);
56113                     if(r && rstate){
56114                         if(rstate.size){
56115                             r.resizeTo(rstate.size);
56116                         }
56117                         if(rstate.collapsed == true){
56118                             r.collapse(true);
56119                         }else{
56120                             r.expand(null, true);
56121                         }
56122                     }
56123                 }
56124             }
56125             if(!wasUpdating){
56126                 layout.endUpdate();
56127             }
56128             this.state = state; 
56129         }
56130         this.layout = layout;
56131         layout.on("regionresized", this.onRegionResized, this);
56132         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56133         layout.on("regionexpanded", this.onRegionExpanded, this);
56134     },
56135     
56136     storeState : function(){
56137         this.provider.set(this.layout.id+"-layout-state", this.state);
56138     },
56139     
56140     onRegionResized : function(region, newSize){
56141         this.state[region.getPosition()].size = newSize;
56142         this.storeState();
56143     },
56144     
56145     onRegionCollapsed : function(region){
56146         this.state[region.getPosition()].collapsed = true;
56147         this.storeState();
56148     },
56149     
56150     onRegionExpanded : function(region){
56151         this.state[region.getPosition()].collapsed = false;
56152         this.storeState();
56153     }
56154 };/*
56155  * Based on:
56156  * Ext JS Library 1.1.1
56157  * Copyright(c) 2006-2007, Ext JS, LLC.
56158  *
56159  * Originally Released Under LGPL - original licence link has changed is not relivant.
56160  *
56161  * Fork - LGPL
56162  * <script type="text/javascript">
56163  */
56164 /**
56165  * @class Roo.ContentPanel
56166  * @extends Roo.util.Observable
56167  * @children Roo.form.Form Roo.JsonView Roo.View
56168  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
56169  * A basic ContentPanel element.
56170  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56171  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56172  * @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
56173  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56174  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56175  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56176  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56177  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56178  * @cfg {String} title          The title for this panel
56179  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56180  * @cfg {String} url            Calls {@link #setUrl} with this value
56181  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
56182  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56183  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56184  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56185  * @cfg {String}    style  Extra style to add to the content panel
56186  * @cfg {Roo.menu.Menu} menu  popup menu
56187
56188  * @constructor
56189  * Create a new ContentPanel.
56190  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56191  * @param {String/Object} config A string to set only the title or a config object
56192  * @param {String} content (optional) Set the HTML content for this panel
56193  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56194  */
56195 Roo.ContentPanel = function(el, config, content){
56196     
56197      
56198     /*
56199     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56200         config = el;
56201         el = Roo.id();
56202     }
56203     if (config && config.parentLayout) { 
56204         el = config.parentLayout.el.createChild(); 
56205     }
56206     */
56207     if(el.autoCreate){ // xtype is available if this is called from factory
56208         config = el;
56209         el = Roo.id();
56210     }
56211     this.el = Roo.get(el);
56212     if(!this.el && config && config.autoCreate){
56213         if(typeof config.autoCreate == "object"){
56214             if(!config.autoCreate.id){
56215                 config.autoCreate.id = config.id||el;
56216             }
56217             this.el = Roo.DomHelper.append(document.body,
56218                         config.autoCreate, true);
56219         }else{
56220             this.el = Roo.DomHelper.append(document.body,
56221                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56222         }
56223     }
56224     
56225     
56226     this.closable = false;
56227     this.loaded = false;
56228     this.active = false;
56229     if(typeof config == "string"){
56230         this.title = config;
56231     }else{
56232         Roo.apply(this, config);
56233     }
56234     
56235     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56236         this.wrapEl = this.el.wrap();
56237         this.toolbar.container = this.el.insertSibling(false, 'before');
56238         this.toolbar = new Roo.Toolbar(this.toolbar);
56239     }
56240     
56241     // xtype created footer. - not sure if will work as we normally have to render first..
56242     if (this.footer && !this.footer.el && this.footer.xtype) {
56243         if (!this.wrapEl) {
56244             this.wrapEl = this.el.wrap();
56245         }
56246     
56247         this.footer.container = this.wrapEl.createChild();
56248          
56249         this.footer = Roo.factory(this.footer, Roo);
56250         
56251     }
56252     
56253     if(this.resizeEl){
56254         this.resizeEl = Roo.get(this.resizeEl, true);
56255     }else{
56256         this.resizeEl = this.el;
56257     }
56258     // handle view.xtype
56259     
56260  
56261     
56262     
56263     this.addEvents({
56264         /**
56265          * @event activate
56266          * Fires when this panel is activated. 
56267          * @param {Roo.ContentPanel} this
56268          */
56269         "activate" : true,
56270         /**
56271          * @event deactivate
56272          * Fires when this panel is activated. 
56273          * @param {Roo.ContentPanel} this
56274          */
56275         "deactivate" : true,
56276
56277         /**
56278          * @event resize
56279          * Fires when this panel is resized if fitToFrame is true.
56280          * @param {Roo.ContentPanel} this
56281          * @param {Number} width The width after any component adjustments
56282          * @param {Number} height The height after any component adjustments
56283          */
56284         "resize" : true,
56285         
56286          /**
56287          * @event render
56288          * Fires when this tab is created
56289          * @param {Roo.ContentPanel} this
56290          */
56291         "render" : true
56292          
56293         
56294     });
56295     
56296
56297     
56298     
56299     if(this.autoScroll){
56300         this.resizeEl.setStyle("overflow", "auto");
56301     } else {
56302         // fix randome scrolling
56303         this.el.on('scroll', function() {
56304             Roo.log('fix random scolling');
56305             this.scrollTo('top',0); 
56306         });
56307     }
56308     content = content || this.content;
56309     if(content){
56310         this.setContent(content);
56311     }
56312     if(config && config.url){
56313         this.setUrl(this.url, this.params, this.loadOnce);
56314     }
56315     
56316     
56317     
56318     Roo.ContentPanel.superclass.constructor.call(this);
56319     
56320     if (this.view && typeof(this.view.xtype) != 'undefined') {
56321         this.view.el = this.el.appendChild(document.createElement("div"));
56322         this.view = Roo.factory(this.view); 
56323         this.view.render  &&  this.view.render(false, '');  
56324     }
56325     
56326     
56327     this.fireEvent('render', this);
56328 };
56329
56330 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56331     tabTip:'',
56332     setRegion : function(region){
56333         this.region = region;
56334         if(region){
56335            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56336         }else{
56337            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56338         } 
56339     },
56340     
56341     /**
56342      * Returns the toolbar for this Panel if one was configured. 
56343      * @return {Roo.Toolbar} 
56344      */
56345     getToolbar : function(){
56346         return this.toolbar;
56347     },
56348     
56349     setActiveState : function(active){
56350         this.active = active;
56351         if(!active){
56352             this.fireEvent("deactivate", this);
56353         }else{
56354             this.fireEvent("activate", this);
56355         }
56356     },
56357     /**
56358      * Updates this panel's element
56359      * @param {String} content The new content
56360      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56361     */
56362     setContent : function(content, loadScripts){
56363         this.el.update(content, loadScripts);
56364     },
56365
56366     ignoreResize : function(w, h){
56367         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56368             return true;
56369         }else{
56370             this.lastSize = {width: w, height: h};
56371             return false;
56372         }
56373     },
56374     /**
56375      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56376      * @return {Roo.UpdateManager} The UpdateManager
56377      */
56378     getUpdateManager : function(){
56379         return this.el.getUpdateManager();
56380     },
56381      /**
56382      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56383      * @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:
56384 <pre><code>
56385 panel.load({
56386     url: "your-url.php",
56387     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56388     callback: yourFunction,
56389     scope: yourObject, //(optional scope)
56390     discardUrl: false,
56391     nocache: false,
56392     text: "Loading...",
56393     timeout: 30,
56394     scripts: false
56395 });
56396 </code></pre>
56397      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56398      * 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.
56399      * @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}
56400      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56401      * @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.
56402      * @return {Roo.ContentPanel} this
56403      */
56404     load : function(){
56405         var um = this.el.getUpdateManager();
56406         um.update.apply(um, arguments);
56407         return this;
56408     },
56409
56410
56411     /**
56412      * 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.
56413      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56414      * @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)
56415      * @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)
56416      * @return {Roo.UpdateManager} The UpdateManager
56417      */
56418     setUrl : function(url, params, loadOnce){
56419         if(this.refreshDelegate){
56420             this.removeListener("activate", this.refreshDelegate);
56421         }
56422         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
56423         this.on("activate", this.refreshDelegate);
56424         return this.el.getUpdateManager();
56425     },
56426     
56427     _handleRefresh : function(url, params, loadOnce){
56428         if(!loadOnce || !this.loaded){
56429             var updater = this.el.getUpdateManager();
56430             updater.update(url, params, this._setLoaded.createDelegate(this));
56431         }
56432     },
56433     
56434     _setLoaded : function(){
56435         this.loaded = true;
56436     }, 
56437     
56438     /**
56439      * Returns this panel's id
56440      * @return {String} 
56441      */
56442     getId : function(){
56443         return this.el.id;
56444     },
56445     
56446     /** 
56447      * Returns this panel's element - used by regiosn to add.
56448      * @return {Roo.Element} 
56449      */
56450     getEl : function(){
56451         return this.wrapEl || this.el;
56452     },
56453     
56454     adjustForComponents : function(width, height)
56455     {
56456         //Roo.log('adjustForComponents ');
56457         if(this.resizeEl != this.el){
56458             width -= this.el.getFrameWidth('lr');
56459             height -= this.el.getFrameWidth('tb');
56460         }
56461         if(this.toolbar){
56462             var te = this.toolbar.getEl();
56463             height -= te.getHeight();
56464             te.setWidth(width);
56465         }
56466         if(this.footer){
56467             var te = this.footer.getEl();
56468             //Roo.log("footer:" + te.getHeight());
56469             
56470             height -= te.getHeight();
56471             te.setWidth(width);
56472         }
56473         
56474         
56475         if(this.adjustments){
56476             width += this.adjustments[0];
56477             height += this.adjustments[1];
56478         }
56479         return {"width": width, "height": height};
56480     },
56481     
56482     setSize : function(width, height){
56483         if(this.fitToFrame && !this.ignoreResize(width, height)){
56484             if(this.fitContainer && this.resizeEl != this.el){
56485                 this.el.setSize(width, height);
56486             }
56487             var size = this.adjustForComponents(width, height);
56488             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
56489             this.fireEvent('resize', this, size.width, size.height);
56490         }
56491     },
56492     
56493     /**
56494      * Returns this panel's title
56495      * @return {String} 
56496      */
56497     getTitle : function(){
56498         return this.title;
56499     },
56500     
56501     /**
56502      * Set this panel's title
56503      * @param {String} title
56504      */
56505     setTitle : function(title){
56506         this.title = title;
56507         if(this.region){
56508             this.region.updatePanelTitle(this, title);
56509         }
56510     },
56511     
56512     /**
56513      * Returns true is this panel was configured to be closable
56514      * @return {Boolean} 
56515      */
56516     isClosable : function(){
56517         return this.closable;
56518     },
56519     
56520     beforeSlide : function(){
56521         this.el.clip();
56522         this.resizeEl.clip();
56523     },
56524     
56525     afterSlide : function(){
56526         this.el.unclip();
56527         this.resizeEl.unclip();
56528     },
56529     
56530     /**
56531      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
56532      *   Will fail silently if the {@link #setUrl} method has not been called.
56533      *   This does not activate the panel, just updates its content.
56534      */
56535     refresh : function(){
56536         if(this.refreshDelegate){
56537            this.loaded = false;
56538            this.refreshDelegate();
56539         }
56540     },
56541     
56542     /**
56543      * Destroys this panel
56544      */
56545     destroy : function(){
56546         this.el.removeAllListeners();
56547         var tempEl = document.createElement("span");
56548         tempEl.appendChild(this.el.dom);
56549         tempEl.innerHTML = "";
56550         this.el.remove();
56551         this.el = null;
56552     },
56553     
56554     /**
56555      * form - if the content panel contains a form - this is a reference to it.
56556      * @type {Roo.form.Form}
56557      */
56558     form : false,
56559     /**
56560      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
56561      *    This contains a reference to it.
56562      * @type {Roo.View}
56563      */
56564     view : false,
56565     
56566       /**
56567      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
56568      * <pre><code>
56569
56570 layout.addxtype({
56571        xtype : 'Form',
56572        items: [ .... ]
56573    }
56574 );
56575
56576 </code></pre>
56577      * @param {Object} cfg Xtype definition of item to add.
56578      */
56579     
56580     addxtype : function(cfg) {
56581         // add form..
56582         if (cfg.xtype.match(/^Form$/)) {
56583             
56584             var el;
56585             //if (this.footer) {
56586             //    el = this.footer.container.insertSibling(false, 'before');
56587             //} else {
56588                 el = this.el.createChild();
56589             //}
56590
56591             this.form = new  Roo.form.Form(cfg);
56592             
56593             
56594             if ( this.form.allItems.length) {
56595                 this.form.render(el.dom);
56596             }
56597             return this.form;
56598         }
56599         // should only have one of theses..
56600         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
56601             // views.. should not be just added - used named prop 'view''
56602             
56603             cfg.el = this.el.appendChild(document.createElement("div"));
56604             // factory?
56605             
56606             var ret = new Roo.factory(cfg);
56607              
56608              ret.render && ret.render(false, ''); // render blank..
56609             this.view = ret;
56610             return ret;
56611         }
56612         return false;
56613     }
56614 });
56615
56616 /**
56617  * @class Roo.GridPanel
56618  * @extends Roo.ContentPanel
56619  * @constructor
56620  * Create a new GridPanel.
56621  * @param {Roo.grid.Grid} grid The grid for this panel
56622  * @param {String/Object} config A string to set only the panel's title, or a config object
56623  */
56624 Roo.GridPanel = function(grid, config){
56625     
56626   
56627     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
56628         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
56629         
56630     this.wrapper.dom.appendChild(grid.getGridEl().dom);
56631     
56632     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
56633     
56634     if(this.toolbar){
56635         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
56636     }
56637     // xtype created footer. - not sure if will work as we normally have to render first..
56638     if (this.footer && !this.footer.el && this.footer.xtype) {
56639         
56640         this.footer.container = this.grid.getView().getFooterPanel(true);
56641         this.footer.dataSource = this.grid.dataSource;
56642         this.footer = Roo.factory(this.footer, Roo);
56643         
56644     }
56645     
56646     grid.monitorWindowResize = false; // turn off autosizing
56647     grid.autoHeight = false;
56648     grid.autoWidth = false;
56649     this.grid = grid;
56650     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
56651 };
56652
56653 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
56654     getId : function(){
56655         return this.grid.id;
56656     },
56657     
56658     /**
56659      * Returns the grid for this panel
56660      * @return {Roo.grid.Grid} 
56661      */
56662     getGrid : function(){
56663         return this.grid;    
56664     },
56665     
56666     setSize : function(width, height){
56667         if(!this.ignoreResize(width, height)){
56668             var grid = this.grid;
56669             var size = this.adjustForComponents(width, height);
56670             grid.getGridEl().setSize(size.width, size.height);
56671             grid.autoSize();
56672         }
56673     },
56674     
56675     beforeSlide : function(){
56676         this.grid.getView().scroller.clip();
56677     },
56678     
56679     afterSlide : function(){
56680         this.grid.getView().scroller.unclip();
56681     },
56682     
56683     destroy : function(){
56684         this.grid.destroy();
56685         delete this.grid;
56686         Roo.GridPanel.superclass.destroy.call(this); 
56687     }
56688 });
56689
56690
56691 /**
56692  * @class Roo.NestedLayoutPanel
56693  * @extends Roo.ContentPanel
56694  * @constructor
56695  * Create a new NestedLayoutPanel.
56696  * 
56697  * 
56698  * @param {Roo.BorderLayout} layout [required] The layout for this panel
56699  * @param {String/Object} config A string to set only the title or a config object
56700  */
56701 Roo.NestedLayoutPanel = function(layout, config)
56702 {
56703     // construct with only one argument..
56704     /* FIXME - implement nicer consturctors
56705     if (layout.layout) {
56706         config = layout;
56707         layout = config.layout;
56708         delete config.layout;
56709     }
56710     if (layout.xtype && !layout.getEl) {
56711         // then layout needs constructing..
56712         layout = Roo.factory(layout, Roo);
56713     }
56714     */
56715     
56716     
56717     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
56718     
56719     layout.monitorWindowResize = false; // turn off autosizing
56720     this.layout = layout;
56721     this.layout.getEl().addClass("x-layout-nested-layout");
56722     
56723     
56724     
56725     
56726 };
56727
56728 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
56729
56730     setSize : function(width, height){
56731         if(!this.ignoreResize(width, height)){
56732             var size = this.adjustForComponents(width, height);
56733             var el = this.layout.getEl();
56734             el.setSize(size.width, size.height);
56735             var touch = el.dom.offsetWidth;
56736             this.layout.layout();
56737             // ie requires a double layout on the first pass
56738             if(Roo.isIE && !this.initialized){
56739                 this.initialized = true;
56740                 this.layout.layout();
56741             }
56742         }
56743     },
56744     
56745     // activate all subpanels if not currently active..
56746     
56747     setActiveState : function(active){
56748         this.active = active;
56749         if(!active){
56750             this.fireEvent("deactivate", this);
56751             return;
56752         }
56753         
56754         this.fireEvent("activate", this);
56755         // not sure if this should happen before or after..
56756         if (!this.layout) {
56757             return; // should not happen..
56758         }
56759         var reg = false;
56760         for (var r in this.layout.regions) {
56761             reg = this.layout.getRegion(r);
56762             if (reg.getActivePanel()) {
56763                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56764                 reg.setActivePanel(reg.getActivePanel());
56765                 continue;
56766             }
56767             if (!reg.panels.length) {
56768                 continue;
56769             }
56770             reg.showPanel(reg.getPanel(0));
56771         }
56772         
56773         
56774         
56775         
56776     },
56777     
56778     /**
56779      * Returns the nested BorderLayout for this panel
56780      * @return {Roo.BorderLayout} 
56781      */
56782     getLayout : function(){
56783         return this.layout;
56784     },
56785     
56786      /**
56787      * Adds a xtype elements to the layout of the nested panel
56788      * <pre><code>
56789
56790 panel.addxtype({
56791        xtype : 'ContentPanel',
56792        region: 'west',
56793        items: [ .... ]
56794    }
56795 );
56796
56797 panel.addxtype({
56798         xtype : 'NestedLayoutPanel',
56799         region: 'west',
56800         layout: {
56801            center: { },
56802            west: { }   
56803         },
56804         items : [ ... list of content panels or nested layout panels.. ]
56805    }
56806 );
56807 </code></pre>
56808      * @param {Object} cfg Xtype definition of item to add.
56809      */
56810     addxtype : function(cfg) {
56811         return this.layout.addxtype(cfg);
56812     
56813     }
56814 });
56815
56816 Roo.ScrollPanel = function(el, config, content){
56817     config = config || {};
56818     config.fitToFrame = true;
56819     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56820     
56821     this.el.dom.style.overflow = "hidden";
56822     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56823     this.el.removeClass("x-layout-inactive-content");
56824     this.el.on("mousewheel", this.onWheel, this);
56825
56826     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56827     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56828     up.unselectable(); down.unselectable();
56829     up.on("click", this.scrollUp, this);
56830     down.on("click", this.scrollDown, this);
56831     up.addClassOnOver("x-scroller-btn-over");
56832     down.addClassOnOver("x-scroller-btn-over");
56833     up.addClassOnClick("x-scroller-btn-click");
56834     down.addClassOnClick("x-scroller-btn-click");
56835     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56836
56837     this.resizeEl = this.el;
56838     this.el = wrap; this.up = up; this.down = down;
56839 };
56840
56841 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56842     increment : 100,
56843     wheelIncrement : 5,
56844     scrollUp : function(){
56845         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56846     },
56847
56848     scrollDown : function(){
56849         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56850     },
56851
56852     afterScroll : function(){
56853         var el = this.resizeEl;
56854         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56855         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56856         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56857     },
56858
56859     setSize : function(){
56860         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56861         this.afterScroll();
56862     },
56863
56864     onWheel : function(e){
56865         var d = e.getWheelDelta();
56866         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56867         this.afterScroll();
56868         e.stopEvent();
56869     },
56870
56871     setContent : function(content, loadScripts){
56872         this.resizeEl.update(content, loadScripts);
56873     }
56874
56875 });
56876
56877
56878
56879 /**
56880  * @class Roo.TreePanel
56881  * @extends Roo.ContentPanel
56882  * Treepanel component
56883  * 
56884  * @constructor
56885  * Create a new TreePanel. - defaults to fit/scoll contents.
56886  * @param {String/Object} config A string to set only the panel's title, or a config object
56887  */
56888 Roo.TreePanel = function(config){
56889     var el = config.el;
56890     var tree = config.tree;
56891     delete config.tree; 
56892     delete config.el; // hopefull!
56893     
56894     // wrapper for IE7 strict & safari scroll issue
56895     
56896     var treeEl = el.createChild();
56897     config.resizeEl = treeEl;
56898     
56899     
56900     
56901     Roo.TreePanel.superclass.constructor.call(this, el, config);
56902  
56903  
56904     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56905     //console.log(tree);
56906     this.on('activate', function()
56907     {
56908         if (this.tree.rendered) {
56909             return;
56910         }
56911         //console.log('render tree');
56912         this.tree.render();
56913     });
56914     // this should not be needed.. - it's actually the 'el' that resizes?
56915     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56916     
56917     //this.on('resize',  function (cp, w, h) {
56918     //        this.tree.innerCt.setWidth(w);
56919     //        this.tree.innerCt.setHeight(h);
56920     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56921     //});
56922
56923         
56924     
56925 };
56926
56927 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56928     fitToFrame : true,
56929     autoScroll : true,
56930     /*
56931      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56932      */
56933     tree : false
56934
56935 });
56936
56937
56938
56939
56940
56941
56942
56943
56944
56945
56946
56947 /*
56948  * Based on:
56949  * Ext JS Library 1.1.1
56950  * Copyright(c) 2006-2007, Ext JS, LLC.
56951  *
56952  * Originally Released Under LGPL - original licence link has changed is not relivant.
56953  *
56954  * Fork - LGPL
56955  * <script type="text/javascript">
56956  */
56957  
56958
56959 /**
56960  * @class Roo.ReaderLayout
56961  * @extends Roo.BorderLayout
56962  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56963  * center region containing two nested regions (a top one for a list view and one for item preview below),
56964  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56965  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56966  * expedites the setup of the overall layout and regions for this common application style.
56967  * Example:
56968  <pre><code>
56969 var reader = new Roo.ReaderLayout();
56970 var CP = Roo.ContentPanel;  // shortcut for adding
56971
56972 reader.beginUpdate();
56973 reader.add("north", new CP("north", "North"));
56974 reader.add("west", new CP("west", {title: "West"}));
56975 reader.add("east", new CP("east", {title: "East"}));
56976
56977 reader.regions.listView.add(new CP("listView", "List"));
56978 reader.regions.preview.add(new CP("preview", "Preview"));
56979 reader.endUpdate();
56980 </code></pre>
56981 * @constructor
56982 * Create a new ReaderLayout
56983 * @param {Object} config Configuration options
56984 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56985 * document.body if omitted)
56986 */
56987 Roo.ReaderLayout = function(config, renderTo){
56988     var c = config || {size:{}};
56989     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56990         north: c.north !== false ? Roo.apply({
56991             split:false,
56992             initialSize: 32,
56993             titlebar: false
56994         }, c.north) : false,
56995         west: c.west !== false ? Roo.apply({
56996             split:true,
56997             initialSize: 200,
56998             minSize: 175,
56999             maxSize: 400,
57000             titlebar: true,
57001             collapsible: true,
57002             animate: true,
57003             margins:{left:5,right:0,bottom:5,top:5},
57004             cmargins:{left:5,right:5,bottom:5,top:5}
57005         }, c.west) : false,
57006         east: c.east !== false ? Roo.apply({
57007             split:true,
57008             initialSize: 200,
57009             minSize: 175,
57010             maxSize: 400,
57011             titlebar: true,
57012             collapsible: true,
57013             animate: true,
57014             margins:{left:0,right:5,bottom:5,top:5},
57015             cmargins:{left:5,right:5,bottom:5,top:5}
57016         }, c.east) : false,
57017         center: Roo.apply({
57018             tabPosition: 'top',
57019             autoScroll:false,
57020             closeOnTab: true,
57021             titlebar:false,
57022             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57023         }, c.center)
57024     });
57025
57026     this.el.addClass('x-reader');
57027
57028     this.beginUpdate();
57029
57030     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57031         south: c.preview !== false ? Roo.apply({
57032             split:true,
57033             initialSize: 200,
57034             minSize: 100,
57035             autoScroll:true,
57036             collapsible:true,
57037             titlebar: true,
57038             cmargins:{top:5,left:0, right:0, bottom:0}
57039         }, c.preview) : false,
57040         center: Roo.apply({
57041             autoScroll:false,
57042             titlebar:false,
57043             minHeight:200
57044         }, c.listView)
57045     });
57046     this.add('center', new Roo.NestedLayoutPanel(inner,
57047             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57048
57049     this.endUpdate();
57050
57051     this.regions.preview = inner.getRegion('south');
57052     this.regions.listView = inner.getRegion('center');
57053 };
57054
57055 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57056  * Based on:
57057  * Ext JS Library 1.1.1
57058  * Copyright(c) 2006-2007, Ext JS, LLC.
57059  *
57060  * Originally Released Under LGPL - original licence link has changed is not relivant.
57061  *
57062  * Fork - LGPL
57063  * <script type="text/javascript">
57064  */
57065  
57066 /**
57067  * @class Roo.grid.Grid
57068  * @extends Roo.util.Observable
57069  * This class represents the primary interface of a component based grid control.
57070  * <br><br>Usage:<pre><code>
57071  var grid = new Roo.grid.Grid("my-container-id", {
57072      ds: myDataStore,
57073      cm: myColModel,
57074      selModel: mySelectionModel,
57075      autoSizeColumns: true,
57076      monitorWindowResize: false,
57077      trackMouseOver: true
57078  });
57079  // set any options
57080  grid.render();
57081  * </code></pre>
57082  * <b>Common Problems:</b><br/>
57083  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57084  * element will correct this<br/>
57085  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57086  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57087  * are unpredictable.<br/>
57088  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57089  * grid to calculate dimensions/offsets.<br/>
57090   * @constructor
57091  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57092  * The container MUST have some type of size defined for the grid to fill. The container will be
57093  * automatically set to position relative if it isn't already.
57094  * @param {Object} config A config object that sets properties on this grid.
57095  */
57096 Roo.grid.Grid = function(container, config){
57097         // initialize the container
57098         this.container = Roo.get(container);
57099         this.container.update("");
57100         this.container.setStyle("overflow", "hidden");
57101     this.container.addClass('x-grid-container');
57102
57103     this.id = this.container.id;
57104
57105     Roo.apply(this, config);
57106     // check and correct shorthanded configs
57107     if(this.ds){
57108         this.dataSource = this.ds;
57109         delete this.ds;
57110     }
57111     if(this.cm){
57112         this.colModel = this.cm;
57113         delete this.cm;
57114     }
57115     if(this.sm){
57116         this.selModel = this.sm;
57117         delete this.sm;
57118     }
57119
57120     if (this.selModel) {
57121         this.selModel = Roo.factory(this.selModel, Roo.grid);
57122         this.sm = this.selModel;
57123         this.sm.xmodule = this.xmodule || false;
57124     }
57125     if (typeof(this.colModel.config) == 'undefined') {
57126         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57127         this.cm = this.colModel;
57128         this.cm.xmodule = this.xmodule || false;
57129     }
57130     if (this.dataSource) {
57131         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57132         this.ds = this.dataSource;
57133         this.ds.xmodule = this.xmodule || false;
57134          
57135     }
57136     
57137     
57138     
57139     if(this.width){
57140         this.container.setWidth(this.width);
57141     }
57142
57143     if(this.height){
57144         this.container.setHeight(this.height);
57145     }
57146     /** @private */
57147         this.addEvents({
57148         // raw events
57149         /**
57150          * @event click
57151          * The raw click event for the entire grid.
57152          * @param {Roo.EventObject} e
57153          */
57154         "click" : true,
57155         /**
57156          * @event dblclick
57157          * The raw dblclick event for the entire grid.
57158          * @param {Roo.EventObject} e
57159          */
57160         "dblclick" : true,
57161         /**
57162          * @event contextmenu
57163          * The raw contextmenu event for the entire grid.
57164          * @param {Roo.EventObject} e
57165          */
57166         "contextmenu" : true,
57167         /**
57168          * @event mousedown
57169          * The raw mousedown event for the entire grid.
57170          * @param {Roo.EventObject} e
57171          */
57172         "mousedown" : true,
57173         /**
57174          * @event mouseup
57175          * The raw mouseup event for the entire grid.
57176          * @param {Roo.EventObject} e
57177          */
57178         "mouseup" : true,
57179         /**
57180          * @event mouseover
57181          * The raw mouseover event for the entire grid.
57182          * @param {Roo.EventObject} e
57183          */
57184         "mouseover" : true,
57185         /**
57186          * @event mouseout
57187          * The raw mouseout event for the entire grid.
57188          * @param {Roo.EventObject} e
57189          */
57190         "mouseout" : true,
57191         /**
57192          * @event keypress
57193          * The raw keypress event for the entire grid.
57194          * @param {Roo.EventObject} e
57195          */
57196         "keypress" : true,
57197         /**
57198          * @event keydown
57199          * The raw keydown event for the entire grid.
57200          * @param {Roo.EventObject} e
57201          */
57202         "keydown" : true,
57203
57204         // custom events
57205
57206         /**
57207          * @event cellclick
57208          * Fires when a cell is clicked
57209          * @param {Grid} this
57210          * @param {Number} rowIndex
57211          * @param {Number} columnIndex
57212          * @param {Roo.EventObject} e
57213          */
57214         "cellclick" : true,
57215         /**
57216          * @event celldblclick
57217          * Fires when a cell is double clicked
57218          * @param {Grid} this
57219          * @param {Number} rowIndex
57220          * @param {Number} columnIndex
57221          * @param {Roo.EventObject} e
57222          */
57223         "celldblclick" : true,
57224         /**
57225          * @event rowclick
57226          * Fires when a row is clicked
57227          * @param {Grid} this
57228          * @param {Number} rowIndex
57229          * @param {Roo.EventObject} e
57230          */
57231         "rowclick" : true,
57232         /**
57233          * @event rowdblclick
57234          * Fires when a row is double clicked
57235          * @param {Grid} this
57236          * @param {Number} rowIndex
57237          * @param {Roo.EventObject} e
57238          */
57239         "rowdblclick" : true,
57240         /**
57241          * @event headerclick
57242          * Fires when a header is clicked
57243          * @param {Grid} this
57244          * @param {Number} columnIndex
57245          * @param {Roo.EventObject} e
57246          */
57247         "headerclick" : true,
57248         /**
57249          * @event headerdblclick
57250          * Fires when a header cell is double clicked
57251          * @param {Grid} this
57252          * @param {Number} columnIndex
57253          * @param {Roo.EventObject} e
57254          */
57255         "headerdblclick" : true,
57256         /**
57257          * @event rowcontextmenu
57258          * Fires when a row is right clicked
57259          * @param {Grid} this
57260          * @param {Number} rowIndex
57261          * @param {Roo.EventObject} e
57262          */
57263         "rowcontextmenu" : true,
57264         /**
57265          * @event cellcontextmenu
57266          * Fires when a cell is right clicked
57267          * @param {Grid} this
57268          * @param {Number} rowIndex
57269          * @param {Number} cellIndex
57270          * @param {Roo.EventObject} e
57271          */
57272          "cellcontextmenu" : true,
57273         /**
57274          * @event headercontextmenu
57275          * Fires when a header is right clicked
57276          * @param {Grid} this
57277          * @param {Number} columnIndex
57278          * @param {Roo.EventObject} e
57279          */
57280         "headercontextmenu" : true,
57281         /**
57282          * @event bodyscroll
57283          * Fires when the body element is scrolled
57284          * @param {Number} scrollLeft
57285          * @param {Number} scrollTop
57286          */
57287         "bodyscroll" : true,
57288         /**
57289          * @event columnresize
57290          * Fires when the user resizes a column
57291          * @param {Number} columnIndex
57292          * @param {Number} newSize
57293          */
57294         "columnresize" : true,
57295         /**
57296          * @event columnmove
57297          * Fires when the user moves a column
57298          * @param {Number} oldIndex
57299          * @param {Number} newIndex
57300          */
57301         "columnmove" : true,
57302         /**
57303          * @event startdrag
57304          * Fires when row(s) start being dragged
57305          * @param {Grid} this
57306          * @param {Roo.GridDD} dd The drag drop object
57307          * @param {event} e The raw browser event
57308          */
57309         "startdrag" : true,
57310         /**
57311          * @event enddrag
57312          * Fires when a drag operation is complete
57313          * @param {Grid} this
57314          * @param {Roo.GridDD} dd The drag drop object
57315          * @param {event} e The raw browser event
57316          */
57317         "enddrag" : true,
57318         /**
57319          * @event dragdrop
57320          * Fires when dragged row(s) are dropped on a valid DD target
57321          * @param {Grid} this
57322          * @param {Roo.GridDD} dd The drag drop object
57323          * @param {String} targetId The target drag drop object
57324          * @param {event} e The raw browser event
57325          */
57326         "dragdrop" : true,
57327         /**
57328          * @event dragover
57329          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57330          * @param {Grid} this
57331          * @param {Roo.GridDD} dd The drag drop object
57332          * @param {String} targetId The target drag drop object
57333          * @param {event} e The raw browser event
57334          */
57335         "dragover" : true,
57336         /**
57337          * @event dragenter
57338          *  Fires when the dragged row(s) first cross another DD target while being dragged
57339          * @param {Grid} this
57340          * @param {Roo.GridDD} dd The drag drop object
57341          * @param {String} targetId The target drag drop object
57342          * @param {event} e The raw browser event
57343          */
57344         "dragenter" : true,
57345         /**
57346          * @event dragout
57347          * Fires when the dragged row(s) leave another DD target while being dragged
57348          * @param {Grid} this
57349          * @param {Roo.GridDD} dd The drag drop object
57350          * @param {String} targetId The target drag drop object
57351          * @param {event} e The raw browser event
57352          */
57353         "dragout" : true,
57354         /**
57355          * @event rowclass
57356          * Fires when a row is rendered, so you can change add a style to it.
57357          * @param {GridView} gridview   The grid view
57358          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57359          */
57360         'rowclass' : true,
57361
57362         /**
57363          * @event render
57364          * Fires when the grid is rendered
57365          * @param {Grid} grid
57366          */
57367         'render' : true
57368     });
57369
57370     Roo.grid.Grid.superclass.constructor.call(this);
57371 };
57372 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57373     
57374     /**
57375          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57376          */
57377         /**
57378          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57379          */
57380         /**
57381          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57382          */
57383         /**
57384          * @cfg {Roo.grid.Store} ds The data store for the grid
57385          */
57386         /**
57387          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57388          */
57389         /**
57390      * @cfg {String} ddGroup - drag drop group.
57391      */
57392       /**
57393      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57394      */
57395
57396     /**
57397      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57398      */
57399     minColumnWidth : 25,
57400
57401     /**
57402      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57403      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57404      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57405      */
57406     autoSizeColumns : false,
57407
57408     /**
57409      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57410      */
57411     autoSizeHeaders : true,
57412
57413     /**
57414      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
57415      */
57416     monitorWindowResize : true,
57417
57418     /**
57419      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
57420      * rows measured to get a columns size. Default is 0 (all rows).
57421      */
57422     maxRowsToMeasure : 0,
57423
57424     /**
57425      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
57426      */
57427     trackMouseOver : true,
57428
57429     /**
57430     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
57431     */
57432       /**
57433     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
57434     */
57435     
57436     /**
57437     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
57438     */
57439     enableDragDrop : false,
57440     
57441     /**
57442     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
57443     */
57444     enableColumnMove : true,
57445     
57446     /**
57447     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
57448     */
57449     enableColumnHide : true,
57450     
57451     /**
57452     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
57453     */
57454     enableRowHeightSync : false,
57455     
57456     /**
57457     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
57458     */
57459     stripeRows : true,
57460     
57461     /**
57462     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
57463     */
57464     autoHeight : false,
57465
57466     /**
57467      * @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.
57468      */
57469     autoExpandColumn : false,
57470
57471     /**
57472     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
57473     * Default is 50.
57474     */
57475     autoExpandMin : 50,
57476
57477     /**
57478     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
57479     */
57480     autoExpandMax : 1000,
57481
57482     /**
57483     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
57484     */
57485     view : null,
57486
57487     /**
57488     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
57489     */
57490     loadMask : false,
57491     /**
57492     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
57493     */
57494     dropTarget: false,
57495     
57496    
57497     
57498     // private
57499     rendered : false,
57500
57501     /**
57502     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
57503     * of a fixed width. Default is false.
57504     */
57505     /**
57506     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
57507     */
57508     
57509     
57510     /**
57511     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
57512     * %0 is replaced with the number of selected rows.
57513     */
57514     ddText : "{0} selected row{1}",
57515     
57516     
57517     /**
57518      * Called once after all setup has been completed and the grid is ready to be rendered.
57519      * @return {Roo.grid.Grid} this
57520      */
57521     render : function()
57522     {
57523         var c = this.container;
57524         // try to detect autoHeight/width mode
57525         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
57526             this.autoHeight = true;
57527         }
57528         var view = this.getView();
57529         view.init(this);
57530
57531         c.on("click", this.onClick, this);
57532         c.on("dblclick", this.onDblClick, this);
57533         c.on("contextmenu", this.onContextMenu, this);
57534         c.on("keydown", this.onKeyDown, this);
57535         if (Roo.isTouch) {
57536             c.on("touchstart", this.onTouchStart, this);
57537         }
57538
57539         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
57540
57541         this.getSelectionModel().init(this);
57542
57543         view.render();
57544
57545         if(this.loadMask){
57546             this.loadMask = new Roo.LoadMask(this.container,
57547                     Roo.apply({store:this.dataSource}, this.loadMask));
57548         }
57549         
57550         
57551         if (this.toolbar && this.toolbar.xtype) {
57552             this.toolbar.container = this.getView().getHeaderPanel(true);
57553             this.toolbar = new Roo.Toolbar(this.toolbar);
57554         }
57555         if (this.footer && this.footer.xtype) {
57556             this.footer.dataSource = this.getDataSource();
57557             this.footer.container = this.getView().getFooterPanel(true);
57558             this.footer = Roo.factory(this.footer, Roo);
57559         }
57560         if (this.dropTarget && this.dropTarget.xtype) {
57561             delete this.dropTarget.xtype;
57562             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
57563         }
57564         
57565         
57566         this.rendered = true;
57567         this.fireEvent('render', this);
57568         return this;
57569     },
57570
57571     /**
57572      * Reconfigures the grid to use a different Store and Column Model.
57573      * The View will be bound to the new objects and refreshed.
57574      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
57575      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
57576      */
57577     reconfigure : function(dataSource, colModel){
57578         if(this.loadMask){
57579             this.loadMask.destroy();
57580             this.loadMask = new Roo.LoadMask(this.container,
57581                     Roo.apply({store:dataSource}, this.loadMask));
57582         }
57583         this.view.bind(dataSource, colModel);
57584         this.dataSource = dataSource;
57585         this.colModel = colModel;
57586         this.view.refresh(true);
57587     },
57588     /**
57589      * addColumns
57590      * Add's a column, default at the end..
57591      
57592      * @param {int} position to add (default end)
57593      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
57594      */
57595     addColumns : function(pos, ar)
57596     {
57597         
57598         for (var i =0;i< ar.length;i++) {
57599             var cfg = ar[i];
57600             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
57601             this.cm.lookup[cfg.id] = cfg;
57602         }
57603         
57604         
57605         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
57606             pos = this.cm.config.length; //this.cm.config.push(cfg);
57607         } 
57608         pos = Math.max(0,pos);
57609         ar.unshift(0);
57610         ar.unshift(pos);
57611         this.cm.config.splice.apply(this.cm.config, ar);
57612         
57613         
57614         
57615         this.view.generateRules(this.cm);
57616         this.view.refresh(true);
57617         
57618     },
57619     
57620     
57621     
57622     
57623     // private
57624     onKeyDown : function(e){
57625         this.fireEvent("keydown", e);
57626     },
57627
57628     /**
57629      * Destroy this grid.
57630      * @param {Boolean} removeEl True to remove the element
57631      */
57632     destroy : function(removeEl, keepListeners){
57633         if(this.loadMask){
57634             this.loadMask.destroy();
57635         }
57636         var c = this.container;
57637         c.removeAllListeners();
57638         this.view.destroy();
57639         this.colModel.purgeListeners();
57640         if(!keepListeners){
57641             this.purgeListeners();
57642         }
57643         c.update("");
57644         if(removeEl === true){
57645             c.remove();
57646         }
57647     },
57648
57649     // private
57650     processEvent : function(name, e){
57651         // does this fire select???
57652         //Roo.log('grid:processEvent '  + name);
57653         
57654         if (name != 'touchstart' ) {
57655             this.fireEvent(name, e);    
57656         }
57657         
57658         var t = e.getTarget();
57659         var v = this.view;
57660         var header = v.findHeaderIndex(t);
57661         if(header !== false){
57662             var ename = name == 'touchstart' ? 'click' : name;
57663              
57664             this.fireEvent("header" + ename, this, header, e);
57665         }else{
57666             var row = v.findRowIndex(t);
57667             var cell = v.findCellIndex(t);
57668             if (name == 'touchstart') {
57669                 // first touch is always a click.
57670                 // hopefull this happens after selection is updated.?
57671                 name = false;
57672                 
57673                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
57674                     var cs = this.selModel.getSelectedCell();
57675                     if (row == cs[0] && cell == cs[1]){
57676                         name = 'dblclick';
57677                     }
57678                 }
57679                 if (typeof(this.selModel.getSelections) != 'undefined') {
57680                     var cs = this.selModel.getSelections();
57681                     var ds = this.dataSource;
57682                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
57683                         name = 'dblclick';
57684                     }
57685                 }
57686                 if (!name) {
57687                     return;
57688                 }
57689             }
57690             
57691             
57692             if(row !== false){
57693                 this.fireEvent("row" + name, this, row, e);
57694                 if(cell !== false){
57695                     this.fireEvent("cell" + name, this, row, cell, e);
57696                 }
57697             }
57698         }
57699     },
57700
57701     // private
57702     onClick : function(e){
57703         this.processEvent("click", e);
57704     },
57705    // private
57706     onTouchStart : function(e){
57707         this.processEvent("touchstart", e);
57708     },
57709
57710     // private
57711     onContextMenu : function(e, t){
57712         this.processEvent("contextmenu", e);
57713     },
57714
57715     // private
57716     onDblClick : function(e){
57717         this.processEvent("dblclick", e);
57718     },
57719
57720     // private
57721     walkCells : function(row, col, step, fn, scope){
57722         var cm = this.colModel, clen = cm.getColumnCount();
57723         var ds = this.dataSource, rlen = ds.getCount(), first = true;
57724         if(step < 0){
57725             if(col < 0){
57726                 row--;
57727                 first = false;
57728             }
57729             while(row >= 0){
57730                 if(!first){
57731                     col = clen-1;
57732                 }
57733                 first = false;
57734                 while(col >= 0){
57735                     if(fn.call(scope || this, row, col, cm) === true){
57736                         return [row, col];
57737                     }
57738                     col--;
57739                 }
57740                 row--;
57741             }
57742         } else {
57743             if(col >= clen){
57744                 row++;
57745                 first = false;
57746             }
57747             while(row < rlen){
57748                 if(!first){
57749                     col = 0;
57750                 }
57751                 first = false;
57752                 while(col < clen){
57753                     if(fn.call(scope || this, row, col, cm) === true){
57754                         return [row, col];
57755                     }
57756                     col++;
57757                 }
57758                 row++;
57759             }
57760         }
57761         return null;
57762     },
57763
57764     // private
57765     getSelections : function(){
57766         return this.selModel.getSelections();
57767     },
57768
57769     /**
57770      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57771      * but if manual update is required this method will initiate it.
57772      */
57773     autoSize : function(){
57774         if(this.rendered){
57775             this.view.layout();
57776             if(this.view.adjustForScroll){
57777                 this.view.adjustForScroll();
57778             }
57779         }
57780     },
57781
57782     /**
57783      * Returns the grid's underlying element.
57784      * @return {Element} The element
57785      */
57786     getGridEl : function(){
57787         return this.container;
57788     },
57789
57790     // private for compatibility, overridden by editor grid
57791     stopEditing : function(){},
57792
57793     /**
57794      * Returns the grid's SelectionModel.
57795      * @return {SelectionModel}
57796      */
57797     getSelectionModel : function(){
57798         if(!this.selModel){
57799             this.selModel = new Roo.grid.RowSelectionModel();
57800         }
57801         return this.selModel;
57802     },
57803
57804     /**
57805      * Returns the grid's DataSource.
57806      * @return {DataSource}
57807      */
57808     getDataSource : function(){
57809         return this.dataSource;
57810     },
57811
57812     /**
57813      * Returns the grid's ColumnModel.
57814      * @return {ColumnModel}
57815      */
57816     getColumnModel : function(){
57817         return this.colModel;
57818     },
57819
57820     /**
57821      * Returns the grid's GridView object.
57822      * @return {GridView}
57823      */
57824     getView : function(){
57825         if(!this.view){
57826             this.view = new Roo.grid.GridView(this.viewConfig);
57827             this.relayEvents(this.view, [
57828                 "beforerowremoved", "beforerowsinserted",
57829                 "beforerefresh", "rowremoved",
57830                 "rowsinserted", "rowupdated" ,"refresh"
57831             ]);
57832         }
57833         return this.view;
57834     },
57835     /**
57836      * Called to get grid's drag proxy text, by default returns this.ddText.
57837      * Override this to put something different in the dragged text.
57838      * @return {String}
57839      */
57840     getDragDropText : function(){
57841         var count = this.selModel.getCount();
57842         return String.format(this.ddText, count, count == 1 ? '' : 's');
57843     }
57844 });
57845 /*
57846  * Based on:
57847  * Ext JS Library 1.1.1
57848  * Copyright(c) 2006-2007, Ext JS, LLC.
57849  *
57850  * Originally Released Under LGPL - original licence link has changed is not relivant.
57851  *
57852  * Fork - LGPL
57853  * <script type="text/javascript">
57854  */
57855  /**
57856  * @class Roo.grid.AbstractGridView
57857  * @extends Roo.util.Observable
57858  * @abstract
57859  * Abstract base class for grid Views
57860  * @constructor
57861  */
57862 Roo.grid.AbstractGridView = function(){
57863         this.grid = null;
57864         
57865         this.events = {
57866             "beforerowremoved" : true,
57867             "beforerowsinserted" : true,
57868             "beforerefresh" : true,
57869             "rowremoved" : true,
57870             "rowsinserted" : true,
57871             "rowupdated" : true,
57872             "refresh" : true
57873         };
57874     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57875 };
57876
57877 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57878     rowClass : "x-grid-row",
57879     cellClass : "x-grid-cell",
57880     tdClass : "x-grid-td",
57881     hdClass : "x-grid-hd",
57882     splitClass : "x-grid-hd-split",
57883     
57884     init: function(grid){
57885         this.grid = grid;
57886                 var cid = this.grid.getGridEl().id;
57887         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57888         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57889         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57890         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57891         },
57892         
57893     getColumnRenderers : function(){
57894         var renderers = [];
57895         var cm = this.grid.colModel;
57896         var colCount = cm.getColumnCount();
57897         for(var i = 0; i < colCount; i++){
57898             renderers[i] = cm.getRenderer(i);
57899         }
57900         return renderers;
57901     },
57902     
57903     getColumnIds : function(){
57904         var ids = [];
57905         var cm = this.grid.colModel;
57906         var colCount = cm.getColumnCount();
57907         for(var i = 0; i < colCount; i++){
57908             ids[i] = cm.getColumnId(i);
57909         }
57910         return ids;
57911     },
57912     
57913     getDataIndexes : function(){
57914         if(!this.indexMap){
57915             this.indexMap = this.buildIndexMap();
57916         }
57917         return this.indexMap.colToData;
57918     },
57919     
57920     getColumnIndexByDataIndex : function(dataIndex){
57921         if(!this.indexMap){
57922             this.indexMap = this.buildIndexMap();
57923         }
57924         return this.indexMap.dataToCol[dataIndex];
57925     },
57926     
57927     /**
57928      * Set a css style for a column dynamically. 
57929      * @param {Number} colIndex The index of the column
57930      * @param {String} name The css property name
57931      * @param {String} value The css value
57932      */
57933     setCSSStyle : function(colIndex, name, value){
57934         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57935         Roo.util.CSS.updateRule(selector, name, value);
57936     },
57937     
57938     generateRules : function(cm){
57939         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57940         Roo.util.CSS.removeStyleSheet(rulesId);
57941         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57942             var cid = cm.getColumnId(i);
57943             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57944                          this.tdSelector, cid, " {\n}\n",
57945                          this.hdSelector, cid, " {\n}\n",
57946                          this.splitSelector, cid, " {\n}\n");
57947         }
57948         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57949     }
57950 });/*
57951  * Based on:
57952  * Ext JS Library 1.1.1
57953  * Copyright(c) 2006-2007, Ext JS, LLC.
57954  *
57955  * Originally Released Under LGPL - original licence link has changed is not relivant.
57956  *
57957  * Fork - LGPL
57958  * <script type="text/javascript">
57959  */
57960
57961 // private
57962 // This is a support class used internally by the Grid components
57963 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57964     this.grid = grid;
57965     this.view = grid.getView();
57966     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57967     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57968     if(hd2){
57969         this.setHandleElId(Roo.id(hd));
57970         this.setOuterHandleElId(Roo.id(hd2));
57971     }
57972     this.scroll = false;
57973 };
57974 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57975     maxDragWidth: 120,
57976     getDragData : function(e){
57977         var t = Roo.lib.Event.getTarget(e);
57978         var h = this.view.findHeaderCell(t);
57979         if(h){
57980             return {ddel: h.firstChild, header:h};
57981         }
57982         return false;
57983     },
57984
57985     onInitDrag : function(e){
57986         this.view.headersDisabled = true;
57987         var clone = this.dragData.ddel.cloneNode(true);
57988         clone.id = Roo.id();
57989         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57990         this.proxy.update(clone);
57991         return true;
57992     },
57993
57994     afterValidDrop : function(){
57995         var v = this.view;
57996         setTimeout(function(){
57997             v.headersDisabled = false;
57998         }, 50);
57999     },
58000
58001     afterInvalidDrop : function(){
58002         var v = this.view;
58003         setTimeout(function(){
58004             v.headersDisabled = false;
58005         }, 50);
58006     }
58007 });
58008 /*
58009  * Based on:
58010  * Ext JS Library 1.1.1
58011  * Copyright(c) 2006-2007, Ext JS, LLC.
58012  *
58013  * Originally Released Under LGPL - original licence link has changed is not relivant.
58014  *
58015  * Fork - LGPL
58016  * <script type="text/javascript">
58017  */
58018 // private
58019 // This is a support class used internally by the Grid components
58020 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58021     this.grid = grid;
58022     this.view = grid.getView();
58023     // split the proxies so they don't interfere with mouse events
58024     this.proxyTop = Roo.DomHelper.append(document.body, {
58025         cls:"col-move-top", html:"&#160;"
58026     }, true);
58027     this.proxyBottom = Roo.DomHelper.append(document.body, {
58028         cls:"col-move-bottom", html:"&#160;"
58029     }, true);
58030     this.proxyTop.hide = this.proxyBottom.hide = function(){
58031         this.setLeftTop(-100,-100);
58032         this.setStyle("visibility", "hidden");
58033     };
58034     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58035     // temporarily disabled
58036     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58037     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58038 };
58039 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58040     proxyOffsets : [-4, -9],
58041     fly: Roo.Element.fly,
58042
58043     getTargetFromEvent : function(e){
58044         var t = Roo.lib.Event.getTarget(e);
58045         var cindex = this.view.findCellIndex(t);
58046         if(cindex !== false){
58047             return this.view.getHeaderCell(cindex);
58048         }
58049         return null;
58050     },
58051
58052     nextVisible : function(h){
58053         var v = this.view, cm = this.grid.colModel;
58054         h = h.nextSibling;
58055         while(h){
58056             if(!cm.isHidden(v.getCellIndex(h))){
58057                 return h;
58058             }
58059             h = h.nextSibling;
58060         }
58061         return null;
58062     },
58063
58064     prevVisible : function(h){
58065         var v = this.view, cm = this.grid.colModel;
58066         h = h.prevSibling;
58067         while(h){
58068             if(!cm.isHidden(v.getCellIndex(h))){
58069                 return h;
58070             }
58071             h = h.prevSibling;
58072         }
58073         return null;
58074     },
58075
58076     positionIndicator : function(h, n, e){
58077         var x = Roo.lib.Event.getPageX(e);
58078         var r = Roo.lib.Dom.getRegion(n.firstChild);
58079         var px, pt, py = r.top + this.proxyOffsets[1];
58080         if((r.right - x) <= (r.right-r.left)/2){
58081             px = r.right+this.view.borderWidth;
58082             pt = "after";
58083         }else{
58084             px = r.left;
58085             pt = "before";
58086         }
58087         var oldIndex = this.view.getCellIndex(h);
58088         var newIndex = this.view.getCellIndex(n);
58089
58090         if(this.grid.colModel.isFixed(newIndex)){
58091             return false;
58092         }
58093
58094         var locked = this.grid.colModel.isLocked(newIndex);
58095
58096         if(pt == "after"){
58097             newIndex++;
58098         }
58099         if(oldIndex < newIndex){
58100             newIndex--;
58101         }
58102         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58103             return false;
58104         }
58105         px +=  this.proxyOffsets[0];
58106         this.proxyTop.setLeftTop(px, py);
58107         this.proxyTop.show();
58108         if(!this.bottomOffset){
58109             this.bottomOffset = this.view.mainHd.getHeight();
58110         }
58111         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58112         this.proxyBottom.show();
58113         return pt;
58114     },
58115
58116     onNodeEnter : function(n, dd, e, data){
58117         if(data.header != n){
58118             this.positionIndicator(data.header, n, e);
58119         }
58120     },
58121
58122     onNodeOver : function(n, dd, e, data){
58123         var result = false;
58124         if(data.header != n){
58125             result = this.positionIndicator(data.header, n, e);
58126         }
58127         if(!result){
58128             this.proxyTop.hide();
58129             this.proxyBottom.hide();
58130         }
58131         return result ? this.dropAllowed : this.dropNotAllowed;
58132     },
58133
58134     onNodeOut : function(n, dd, e, data){
58135         this.proxyTop.hide();
58136         this.proxyBottom.hide();
58137     },
58138
58139     onNodeDrop : function(n, dd, e, data){
58140         var h = data.header;
58141         if(h != n){
58142             var cm = this.grid.colModel;
58143             var x = Roo.lib.Event.getPageX(e);
58144             var r = Roo.lib.Dom.getRegion(n.firstChild);
58145             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58146             var oldIndex = this.view.getCellIndex(h);
58147             var newIndex = this.view.getCellIndex(n);
58148             var locked = cm.isLocked(newIndex);
58149             if(pt == "after"){
58150                 newIndex++;
58151             }
58152             if(oldIndex < newIndex){
58153                 newIndex--;
58154             }
58155             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58156                 return false;
58157             }
58158             cm.setLocked(oldIndex, locked, true);
58159             cm.moveColumn(oldIndex, newIndex);
58160             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58161             return true;
58162         }
58163         return false;
58164     }
58165 });
58166 /*
58167  * Based on:
58168  * Ext JS Library 1.1.1
58169  * Copyright(c) 2006-2007, Ext JS, LLC.
58170  *
58171  * Originally Released Under LGPL - original licence link has changed is not relivant.
58172  *
58173  * Fork - LGPL
58174  * <script type="text/javascript">
58175  */
58176   
58177 /**
58178  * @class Roo.grid.GridView
58179  * @extends Roo.util.Observable
58180  *
58181  * @constructor
58182  * @param {Object} config
58183  */
58184 Roo.grid.GridView = function(config){
58185     Roo.grid.GridView.superclass.constructor.call(this);
58186     this.el = null;
58187
58188     Roo.apply(this, config);
58189 };
58190
58191 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58192
58193     unselectable :  'unselectable="on"',
58194     unselectableCls :  'x-unselectable',
58195     
58196     
58197     rowClass : "x-grid-row",
58198
58199     cellClass : "x-grid-col",
58200
58201     tdClass : "x-grid-td",
58202
58203     hdClass : "x-grid-hd",
58204
58205     splitClass : "x-grid-split",
58206
58207     sortClasses : ["sort-asc", "sort-desc"],
58208
58209     enableMoveAnim : false,
58210
58211     hlColor: "C3DAF9",
58212
58213     dh : Roo.DomHelper,
58214
58215     fly : Roo.Element.fly,
58216
58217     css : Roo.util.CSS,
58218
58219     borderWidth: 1,
58220
58221     splitOffset: 3,
58222
58223     scrollIncrement : 22,
58224
58225     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58226
58227     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58228
58229     bind : function(ds, cm){
58230         if(this.ds){
58231             this.ds.un("load", this.onLoad, this);
58232             this.ds.un("datachanged", this.onDataChange, this);
58233             this.ds.un("add", this.onAdd, this);
58234             this.ds.un("remove", this.onRemove, this);
58235             this.ds.un("update", this.onUpdate, this);
58236             this.ds.un("clear", this.onClear, this);
58237         }
58238         if(ds){
58239             ds.on("load", this.onLoad, this);
58240             ds.on("datachanged", this.onDataChange, this);
58241             ds.on("add", this.onAdd, this);
58242             ds.on("remove", this.onRemove, this);
58243             ds.on("update", this.onUpdate, this);
58244             ds.on("clear", this.onClear, this);
58245         }
58246         this.ds = ds;
58247
58248         if(this.cm){
58249             this.cm.un("widthchange", this.onColWidthChange, this);
58250             this.cm.un("headerchange", this.onHeaderChange, this);
58251             this.cm.un("hiddenchange", this.onHiddenChange, this);
58252             this.cm.un("columnmoved", this.onColumnMove, this);
58253             this.cm.un("columnlockchange", this.onColumnLock, this);
58254         }
58255         if(cm){
58256             this.generateRules(cm);
58257             cm.on("widthchange", this.onColWidthChange, this);
58258             cm.on("headerchange", this.onHeaderChange, this);
58259             cm.on("hiddenchange", this.onHiddenChange, this);
58260             cm.on("columnmoved", this.onColumnMove, this);
58261             cm.on("columnlockchange", this.onColumnLock, this);
58262         }
58263         this.cm = cm;
58264     },
58265
58266     init: function(grid){
58267         Roo.grid.GridView.superclass.init.call(this, grid);
58268
58269         this.bind(grid.dataSource, grid.colModel);
58270
58271         grid.on("headerclick", this.handleHeaderClick, this);
58272
58273         if(grid.trackMouseOver){
58274             grid.on("mouseover", this.onRowOver, this);
58275             grid.on("mouseout", this.onRowOut, this);
58276         }
58277         grid.cancelTextSelection = function(){};
58278         this.gridId = grid.id;
58279
58280         var tpls = this.templates || {};
58281
58282         if(!tpls.master){
58283             tpls.master = new Roo.Template(
58284                '<div class="x-grid" hidefocus="true">',
58285                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58286                   '<div class="x-grid-topbar"></div>',
58287                   '<div class="x-grid-scroller"><div></div></div>',
58288                   '<div class="x-grid-locked">',
58289                       '<div class="x-grid-header">{lockedHeader}</div>',
58290                       '<div class="x-grid-body">{lockedBody}</div>',
58291                   "</div>",
58292                   '<div class="x-grid-viewport">',
58293                       '<div class="x-grid-header">{header}</div>',
58294                       '<div class="x-grid-body">{body}</div>',
58295                   "</div>",
58296                   '<div class="x-grid-bottombar"></div>',
58297                  
58298                   '<div class="x-grid-resize-proxy">&#160;</div>',
58299                "</div>"
58300             );
58301             tpls.master.disableformats = true;
58302         }
58303
58304         if(!tpls.header){
58305             tpls.header = new Roo.Template(
58306                '<table border="0" cellspacing="0" cellpadding="0">',
58307                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58308                "</table>{splits}"
58309             );
58310             tpls.header.disableformats = true;
58311         }
58312         tpls.header.compile();
58313
58314         if(!tpls.hcell){
58315             tpls.hcell = new Roo.Template(
58316                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58317                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58318                 "</div></td>"
58319              );
58320              tpls.hcell.disableFormats = true;
58321         }
58322         tpls.hcell.compile();
58323
58324         if(!tpls.hsplit){
58325             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58326                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58327             tpls.hsplit.disableFormats = true;
58328         }
58329         tpls.hsplit.compile();
58330
58331         if(!tpls.body){
58332             tpls.body = new Roo.Template(
58333                '<table border="0" cellspacing="0" cellpadding="0">',
58334                "<tbody>{rows}</tbody>",
58335                "</table>"
58336             );
58337             tpls.body.disableFormats = true;
58338         }
58339         tpls.body.compile();
58340
58341         if(!tpls.row){
58342             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58343             tpls.row.disableFormats = true;
58344         }
58345         tpls.row.compile();
58346
58347         if(!tpls.cell){
58348             tpls.cell = new Roo.Template(
58349                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58350                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58351                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58352                 "</td>"
58353             );
58354             tpls.cell.disableFormats = true;
58355         }
58356         tpls.cell.compile();
58357
58358         this.templates = tpls;
58359     },
58360
58361     // remap these for backwards compat
58362     onColWidthChange : function(){
58363         this.updateColumns.apply(this, arguments);
58364     },
58365     onHeaderChange : function(){
58366         this.updateHeaders.apply(this, arguments);
58367     }, 
58368     onHiddenChange : function(){
58369         this.handleHiddenChange.apply(this, arguments);
58370     },
58371     onColumnMove : function(){
58372         this.handleColumnMove.apply(this, arguments);
58373     },
58374     onColumnLock : function(){
58375         this.handleLockChange.apply(this, arguments);
58376     },
58377
58378     onDataChange : function(){
58379         this.refresh();
58380         this.updateHeaderSortState();
58381     },
58382
58383     onClear : function(){
58384         this.refresh();
58385     },
58386
58387     onUpdate : function(ds, record){
58388         this.refreshRow(record);
58389     },
58390
58391     refreshRow : function(record){
58392         var ds = this.ds, index;
58393         if(typeof record == 'number'){
58394             index = record;
58395             record = ds.getAt(index);
58396         }else{
58397             index = ds.indexOf(record);
58398         }
58399         this.insertRows(ds, index, index, true);
58400         this.onRemove(ds, record, index+1, true);
58401         this.syncRowHeights(index, index);
58402         this.layout();
58403         this.fireEvent("rowupdated", this, index, record);
58404     },
58405
58406     onAdd : function(ds, records, index){
58407         this.insertRows(ds, index, index + (records.length-1));
58408     },
58409
58410     onRemove : function(ds, record, index, isUpdate){
58411         if(isUpdate !== true){
58412             this.fireEvent("beforerowremoved", this, index, record);
58413         }
58414         var bt = this.getBodyTable(), lt = this.getLockedTable();
58415         if(bt.rows[index]){
58416             bt.firstChild.removeChild(bt.rows[index]);
58417         }
58418         if(lt.rows[index]){
58419             lt.firstChild.removeChild(lt.rows[index]);
58420         }
58421         if(isUpdate !== true){
58422             this.stripeRows(index);
58423             this.syncRowHeights(index, index);
58424             this.layout();
58425             this.fireEvent("rowremoved", this, index, record);
58426         }
58427     },
58428
58429     onLoad : function(){
58430         this.scrollToTop();
58431     },
58432
58433     /**
58434      * Scrolls the grid to the top
58435      */
58436     scrollToTop : function(){
58437         if(this.scroller){
58438             this.scroller.dom.scrollTop = 0;
58439             this.syncScroll();
58440         }
58441     },
58442
58443     /**
58444      * Gets a panel in the header of the grid that can be used for toolbars etc.
58445      * After modifying the contents of this panel a call to grid.autoSize() may be
58446      * required to register any changes in size.
58447      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
58448      * @return Roo.Element
58449      */
58450     getHeaderPanel : function(doShow){
58451         if(doShow){
58452             this.headerPanel.show();
58453         }
58454         return this.headerPanel;
58455     },
58456
58457     /**
58458      * Gets a panel in the footer of the grid that can be used for toolbars etc.
58459      * After modifying the contents of this panel a call to grid.autoSize() may be
58460      * required to register any changes in size.
58461      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
58462      * @return Roo.Element
58463      */
58464     getFooterPanel : function(doShow){
58465         if(doShow){
58466             this.footerPanel.show();
58467         }
58468         return this.footerPanel;
58469     },
58470
58471     initElements : function(){
58472         var E = Roo.Element;
58473         var el = this.grid.getGridEl().dom.firstChild;
58474         var cs = el.childNodes;
58475
58476         this.el = new E(el);
58477         
58478          this.focusEl = new E(el.firstChild);
58479         this.focusEl.swallowEvent("click", true);
58480         
58481         this.headerPanel = new E(cs[1]);
58482         this.headerPanel.enableDisplayMode("block");
58483
58484         this.scroller = new E(cs[2]);
58485         this.scrollSizer = new E(this.scroller.dom.firstChild);
58486
58487         this.lockedWrap = new E(cs[3]);
58488         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
58489         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
58490
58491         this.mainWrap = new E(cs[4]);
58492         this.mainHd = new E(this.mainWrap.dom.firstChild);
58493         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
58494
58495         this.footerPanel = new E(cs[5]);
58496         this.footerPanel.enableDisplayMode("block");
58497
58498         this.resizeProxy = new E(cs[6]);
58499
58500         this.headerSelector = String.format(
58501            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
58502            this.lockedHd.id, this.mainHd.id
58503         );
58504
58505         this.splitterSelector = String.format(
58506            '#{0} div.x-grid-split, #{1} div.x-grid-split',
58507            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
58508         );
58509     },
58510     idToCssName : function(s)
58511     {
58512         return s.replace(/[^a-z0-9]+/ig, '-');
58513     },
58514
58515     getHeaderCell : function(index){
58516         return Roo.DomQuery.select(this.headerSelector)[index];
58517     },
58518
58519     getHeaderCellMeasure : function(index){
58520         return this.getHeaderCell(index).firstChild;
58521     },
58522
58523     getHeaderCellText : function(index){
58524         return this.getHeaderCell(index).firstChild.firstChild;
58525     },
58526
58527     getLockedTable : function(){
58528         return this.lockedBody.dom.firstChild;
58529     },
58530
58531     getBodyTable : function(){
58532         return this.mainBody.dom.firstChild;
58533     },
58534
58535     getLockedRow : function(index){
58536         return this.getLockedTable().rows[index];
58537     },
58538
58539     getRow : function(index){
58540         return this.getBodyTable().rows[index];
58541     },
58542
58543     getRowComposite : function(index){
58544         if(!this.rowEl){
58545             this.rowEl = new Roo.CompositeElementLite();
58546         }
58547         var els = [], lrow, mrow;
58548         if(lrow = this.getLockedRow(index)){
58549             els.push(lrow);
58550         }
58551         if(mrow = this.getRow(index)){
58552             els.push(mrow);
58553         }
58554         this.rowEl.elements = els;
58555         return this.rowEl;
58556     },
58557     /**
58558      * Gets the 'td' of the cell
58559      * 
58560      * @param {Integer} rowIndex row to select
58561      * @param {Integer} colIndex column to select
58562      * 
58563      * @return {Object} 
58564      */
58565     getCell : function(rowIndex, colIndex){
58566         var locked = this.cm.getLockedCount();
58567         var source;
58568         if(colIndex < locked){
58569             source = this.lockedBody.dom.firstChild;
58570         }else{
58571             source = this.mainBody.dom.firstChild;
58572             colIndex -= locked;
58573         }
58574         return source.rows[rowIndex].childNodes[colIndex];
58575     },
58576
58577     getCellText : function(rowIndex, colIndex){
58578         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
58579     },
58580
58581     getCellBox : function(cell){
58582         var b = this.fly(cell).getBox();
58583         if(Roo.isOpera){ // opera fails to report the Y
58584             b.y = cell.offsetTop + this.mainBody.getY();
58585         }
58586         return b;
58587     },
58588
58589     getCellIndex : function(cell){
58590         var id = String(cell.className).match(this.cellRE);
58591         if(id){
58592             return parseInt(id[1], 10);
58593         }
58594         return 0;
58595     },
58596
58597     findHeaderIndex : function(n){
58598         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58599         return r ? this.getCellIndex(r) : false;
58600     },
58601
58602     findHeaderCell : function(n){
58603         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58604         return r ? r : false;
58605     },
58606
58607     findRowIndex : function(n){
58608         if(!n){
58609             return false;
58610         }
58611         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
58612         return r ? r.rowIndex : false;
58613     },
58614
58615     findCellIndex : function(node){
58616         var stop = this.el.dom;
58617         while(node && node != stop){
58618             if(this.findRE.test(node.className)){
58619                 return this.getCellIndex(node);
58620             }
58621             node = node.parentNode;
58622         }
58623         return false;
58624     },
58625
58626     getColumnId : function(index){
58627         return this.cm.getColumnId(index);
58628     },
58629
58630     getSplitters : function()
58631     {
58632         if(this.splitterSelector){
58633            return Roo.DomQuery.select(this.splitterSelector);
58634         }else{
58635             return null;
58636       }
58637     },
58638
58639     getSplitter : function(index){
58640         return this.getSplitters()[index];
58641     },
58642
58643     onRowOver : function(e, t){
58644         var row;
58645         if((row = this.findRowIndex(t)) !== false){
58646             this.getRowComposite(row).addClass("x-grid-row-over");
58647         }
58648     },
58649
58650     onRowOut : function(e, t){
58651         var row;
58652         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
58653             this.getRowComposite(row).removeClass("x-grid-row-over");
58654         }
58655     },
58656
58657     renderHeaders : function(){
58658         var cm = this.cm;
58659         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
58660         var cb = [], lb = [], sb = [], lsb = [], p = {};
58661         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58662             p.cellId = "x-grid-hd-0-" + i;
58663             p.splitId = "x-grid-csplit-0-" + i;
58664             p.id = cm.getColumnId(i);
58665             p.value = cm.getColumnHeader(i) || "";
58666             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
58667             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
58668             if(!cm.isLocked(i)){
58669                 cb[cb.length] = ct.apply(p);
58670                 sb[sb.length] = st.apply(p);
58671             }else{
58672                 lb[lb.length] = ct.apply(p);
58673                 lsb[lsb.length] = st.apply(p);
58674             }
58675         }
58676         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
58677                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
58678     },
58679
58680     updateHeaders : function(){
58681         var html = this.renderHeaders();
58682         this.lockedHd.update(html[0]);
58683         this.mainHd.update(html[1]);
58684     },
58685
58686     /**
58687      * Focuses the specified row.
58688      * @param {Number} row The row index
58689      */
58690     focusRow : function(row)
58691     {
58692         //Roo.log('GridView.focusRow');
58693         var x = this.scroller.dom.scrollLeft;
58694         this.focusCell(row, 0, false);
58695         this.scroller.dom.scrollLeft = x;
58696     },
58697
58698     /**
58699      * Focuses the specified cell.
58700      * @param {Number} row The row index
58701      * @param {Number} col The column index
58702      * @param {Boolean} hscroll false to disable horizontal scrolling
58703      */
58704     focusCell : function(row, col, hscroll)
58705     {
58706         //Roo.log('GridView.focusCell');
58707         var el = this.ensureVisible(row, col, hscroll);
58708         this.focusEl.alignTo(el, "tl-tl");
58709         if(Roo.isGecko){
58710             this.focusEl.focus();
58711         }else{
58712             this.focusEl.focus.defer(1, this.focusEl);
58713         }
58714     },
58715
58716     /**
58717      * Scrolls the specified cell into view
58718      * @param {Number} row The row index
58719      * @param {Number} col The column index
58720      * @param {Boolean} hscroll false to disable horizontal scrolling
58721      */
58722     ensureVisible : function(row, col, hscroll)
58723     {
58724         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
58725         //return null; //disable for testing.
58726         if(typeof row != "number"){
58727             row = row.rowIndex;
58728         }
58729         if(row < 0 && row >= this.ds.getCount()){
58730             return  null;
58731         }
58732         col = (col !== undefined ? col : 0);
58733         var cm = this.grid.colModel;
58734         while(cm.isHidden(col)){
58735             col++;
58736         }
58737
58738         var el = this.getCell(row, col);
58739         if(!el){
58740             return null;
58741         }
58742         var c = this.scroller.dom;
58743
58744         var ctop = parseInt(el.offsetTop, 10);
58745         var cleft = parseInt(el.offsetLeft, 10);
58746         var cbot = ctop + el.offsetHeight;
58747         var cright = cleft + el.offsetWidth;
58748         
58749         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58750         var stop = parseInt(c.scrollTop, 10);
58751         var sleft = parseInt(c.scrollLeft, 10);
58752         var sbot = stop + ch;
58753         var sright = sleft + c.clientWidth;
58754         /*
58755         Roo.log('GridView.ensureVisible:' +
58756                 ' ctop:' + ctop +
58757                 ' c.clientHeight:' + c.clientHeight +
58758                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58759                 ' stop:' + stop +
58760                 ' cbot:' + cbot +
58761                 ' sbot:' + sbot +
58762                 ' ch:' + ch  
58763                 );
58764         */
58765         if(ctop < stop){
58766             c.scrollTop = ctop;
58767             //Roo.log("set scrolltop to ctop DISABLE?");
58768         }else if(cbot > sbot){
58769             //Roo.log("set scrolltop to cbot-ch");
58770             c.scrollTop = cbot-ch;
58771         }
58772         
58773         if(hscroll !== false){
58774             if(cleft < sleft){
58775                 c.scrollLeft = cleft;
58776             }else if(cright > sright){
58777                 c.scrollLeft = cright-c.clientWidth;
58778             }
58779         }
58780          
58781         return el;
58782     },
58783
58784     updateColumns : function(){
58785         this.grid.stopEditing();
58786         var cm = this.grid.colModel, colIds = this.getColumnIds();
58787         //var totalWidth = cm.getTotalWidth();
58788         var pos = 0;
58789         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58790             //if(cm.isHidden(i)) continue;
58791             var w = cm.getColumnWidth(i);
58792             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58793             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58794         }
58795         this.updateSplitters();
58796     },
58797
58798     generateRules : function(cm){
58799         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58800         Roo.util.CSS.removeStyleSheet(rulesId);
58801         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58802             var cid = cm.getColumnId(i);
58803             var align = '';
58804             if(cm.config[i].align){
58805                 align = 'text-align:'+cm.config[i].align+';';
58806             }
58807             var hidden = '';
58808             if(cm.isHidden(i)){
58809                 hidden = 'display:none;';
58810             }
58811             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58812             ruleBuf.push(
58813                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58814                     this.hdSelector, cid, " {\n", align, width, "}\n",
58815                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58816                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58817         }
58818         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58819     },
58820
58821     updateSplitters : function(){
58822         var cm = this.cm, s = this.getSplitters();
58823         if(s){ // splitters not created yet
58824             var pos = 0, locked = true;
58825             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58826                 if(cm.isHidden(i)) {
58827                     continue;
58828                 }
58829                 var w = cm.getColumnWidth(i); // make sure it's a number
58830                 if(!cm.isLocked(i) && locked){
58831                     pos = 0;
58832                     locked = false;
58833                 }
58834                 pos += w;
58835                 s[i].style.left = (pos-this.splitOffset) + "px";
58836             }
58837         }
58838     },
58839
58840     handleHiddenChange : function(colModel, colIndex, hidden){
58841         if(hidden){
58842             this.hideColumn(colIndex);
58843         }else{
58844             this.unhideColumn(colIndex);
58845         }
58846     },
58847
58848     hideColumn : function(colIndex){
58849         var cid = this.getColumnId(colIndex);
58850         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58851         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58852         if(Roo.isSafari){
58853             this.updateHeaders();
58854         }
58855         this.updateSplitters();
58856         this.layout();
58857     },
58858
58859     unhideColumn : function(colIndex){
58860         var cid = this.getColumnId(colIndex);
58861         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58862         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58863
58864         if(Roo.isSafari){
58865             this.updateHeaders();
58866         }
58867         this.updateSplitters();
58868         this.layout();
58869     },
58870
58871     insertRows : function(dm, firstRow, lastRow, isUpdate){
58872         if(firstRow == 0 && lastRow == dm.getCount()-1){
58873             this.refresh();
58874         }else{
58875             if(!isUpdate){
58876                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58877             }
58878             var s = this.getScrollState();
58879             var markup = this.renderRows(firstRow, lastRow);
58880             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58881             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58882             this.restoreScroll(s);
58883             if(!isUpdate){
58884                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58885                 this.syncRowHeights(firstRow, lastRow);
58886                 this.stripeRows(firstRow);
58887                 this.layout();
58888             }
58889         }
58890     },
58891
58892     bufferRows : function(markup, target, index){
58893         var before = null, trows = target.rows, tbody = target.tBodies[0];
58894         if(index < trows.length){
58895             before = trows[index];
58896         }
58897         var b = document.createElement("div");
58898         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58899         var rows = b.firstChild.rows;
58900         for(var i = 0, len = rows.length; i < len; i++){
58901             if(before){
58902                 tbody.insertBefore(rows[0], before);
58903             }else{
58904                 tbody.appendChild(rows[0]);
58905             }
58906         }
58907         b.innerHTML = "";
58908         b = null;
58909     },
58910
58911     deleteRows : function(dm, firstRow, lastRow){
58912         if(dm.getRowCount()<1){
58913             this.fireEvent("beforerefresh", this);
58914             this.mainBody.update("");
58915             this.lockedBody.update("");
58916             this.fireEvent("refresh", this);
58917         }else{
58918             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58919             var bt = this.getBodyTable();
58920             var tbody = bt.firstChild;
58921             var rows = bt.rows;
58922             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58923                 tbody.removeChild(rows[firstRow]);
58924             }
58925             this.stripeRows(firstRow);
58926             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58927         }
58928     },
58929
58930     updateRows : function(dataSource, firstRow, lastRow){
58931         var s = this.getScrollState();
58932         this.refresh();
58933         this.restoreScroll(s);
58934     },
58935
58936     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58937         if(!noRefresh){
58938            this.refresh();
58939         }
58940         this.updateHeaderSortState();
58941     },
58942
58943     getScrollState : function(){
58944         
58945         var sb = this.scroller.dom;
58946         return {left: sb.scrollLeft, top: sb.scrollTop};
58947     },
58948
58949     stripeRows : function(startRow){
58950         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58951             return;
58952         }
58953         startRow = startRow || 0;
58954         var rows = this.getBodyTable().rows;
58955         var lrows = this.getLockedTable().rows;
58956         var cls = ' x-grid-row-alt ';
58957         for(var i = startRow, len = rows.length; i < len; i++){
58958             var row = rows[i], lrow = lrows[i];
58959             var isAlt = ((i+1) % 2 == 0);
58960             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58961             if(isAlt == hasAlt){
58962                 continue;
58963             }
58964             if(isAlt){
58965                 row.className += " x-grid-row-alt";
58966             }else{
58967                 row.className = row.className.replace("x-grid-row-alt", "");
58968             }
58969             if(lrow){
58970                 lrow.className = row.className;
58971             }
58972         }
58973     },
58974
58975     restoreScroll : function(state){
58976         //Roo.log('GridView.restoreScroll');
58977         var sb = this.scroller.dom;
58978         sb.scrollLeft = state.left;
58979         sb.scrollTop = state.top;
58980         this.syncScroll();
58981     },
58982
58983     syncScroll : function(){
58984         //Roo.log('GridView.syncScroll');
58985         var sb = this.scroller.dom;
58986         var sh = this.mainHd.dom;
58987         var bs = this.mainBody.dom;
58988         var lv = this.lockedBody.dom;
58989         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58990         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58991     },
58992
58993     handleScroll : function(e){
58994         this.syncScroll();
58995         var sb = this.scroller.dom;
58996         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58997         e.stopEvent();
58998     },
58999
59000     handleWheel : function(e){
59001         var d = e.getWheelDelta();
59002         this.scroller.dom.scrollTop -= d*22;
59003         // set this here to prevent jumpy scrolling on large tables
59004         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59005         e.stopEvent();
59006     },
59007
59008     renderRows : function(startRow, endRow){
59009         // pull in all the crap needed to render rows
59010         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59011         var colCount = cm.getColumnCount();
59012
59013         if(ds.getCount() < 1){
59014             return ["", ""];
59015         }
59016
59017         // build a map for all the columns
59018         var cs = [];
59019         for(var i = 0; i < colCount; i++){
59020             var name = cm.getDataIndex(i);
59021             cs[i] = {
59022                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59023                 renderer : cm.getRenderer(i),
59024                 id : cm.getColumnId(i),
59025                 locked : cm.isLocked(i),
59026                 has_editor : cm.isCellEditable(i)
59027             };
59028         }
59029
59030         startRow = startRow || 0;
59031         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59032
59033         // records to render
59034         var rs = ds.getRange(startRow, endRow);
59035
59036         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59037     },
59038
59039     // As much as I hate to duplicate code, this was branched because FireFox really hates
59040     // [].join("") on strings. The performance difference was substantial enough to
59041     // branch this function
59042     doRender : Roo.isGecko ?
59043             function(cs, rs, ds, startRow, colCount, stripe){
59044                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59045                 // buffers
59046                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59047                 
59048                 var hasListener = this.grid.hasListener('rowclass');
59049                 var rowcfg = {};
59050                 for(var j = 0, len = rs.length; j < len; j++){
59051                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59052                     for(var i = 0; i < colCount; i++){
59053                         c = cs[i];
59054                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59055                         p.id = c.id;
59056                         p.css = p.attr = "";
59057                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59058                         if(p.value == undefined || p.value === "") {
59059                             p.value = "&#160;";
59060                         }
59061                         if(c.has_editor){
59062                             p.css += ' x-grid-editable-cell';
59063                         }
59064                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59065                             p.css +=  ' x-grid-dirty-cell';
59066                         }
59067                         var markup = ct.apply(p);
59068                         if(!c.locked){
59069                             cb+= markup;
59070                         }else{
59071                             lcb+= markup;
59072                         }
59073                     }
59074                     var alt = [];
59075                     if(stripe && ((rowIndex+1) % 2 == 0)){
59076                         alt.push("x-grid-row-alt")
59077                     }
59078                     if(r.dirty){
59079                         alt.push(  " x-grid-dirty-row");
59080                     }
59081                     rp.cells = lcb;
59082                     if(this.getRowClass){
59083                         alt.push(this.getRowClass(r, rowIndex));
59084                     }
59085                     if (hasListener) {
59086                         rowcfg = {
59087                              
59088                             record: r,
59089                             rowIndex : rowIndex,
59090                             rowClass : ''
59091                         };
59092                         this.grid.fireEvent('rowclass', this, rowcfg);
59093                         alt.push(rowcfg.rowClass);
59094                     }
59095                     rp.alt = alt.join(" ");
59096                     lbuf+= rt.apply(rp);
59097                     rp.cells = cb;
59098                     buf+=  rt.apply(rp);
59099                 }
59100                 return [lbuf, buf];
59101             } :
59102             function(cs, rs, ds, startRow, colCount, stripe){
59103                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59104                 // buffers
59105                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59106                 var hasListener = this.grid.hasListener('rowclass');
59107  
59108                 var rowcfg = {};
59109                 for(var j = 0, len = rs.length; j < len; j++){
59110                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59111                     for(var i = 0; i < colCount; i++){
59112                         c = cs[i];
59113                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59114                         p.id = c.id;
59115                         p.css = p.attr = "";
59116                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59117                         if(p.value == undefined || p.value === "") {
59118                             p.value = "&#160;";
59119                         }
59120                         //Roo.log(c);
59121                          if(c.has_editor){
59122                             p.css += ' x-grid-editable-cell';
59123                         }
59124                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59125                             p.css += ' x-grid-dirty-cell' 
59126                         }
59127                         
59128                         var markup = ct.apply(p);
59129                         if(!c.locked){
59130                             cb[cb.length] = markup;
59131                         }else{
59132                             lcb[lcb.length] = markup;
59133                         }
59134                     }
59135                     var alt = [];
59136                     if(stripe && ((rowIndex+1) % 2 == 0)){
59137                         alt.push( "x-grid-row-alt");
59138                     }
59139                     if(r.dirty){
59140                         alt.push(" x-grid-dirty-row");
59141                     }
59142                     rp.cells = lcb;
59143                     if(this.getRowClass){
59144                         alt.push( this.getRowClass(r, rowIndex));
59145                     }
59146                     if (hasListener) {
59147                         rowcfg = {
59148                              
59149                             record: r,
59150                             rowIndex : rowIndex,
59151                             rowClass : ''
59152                         };
59153                         this.grid.fireEvent('rowclass', this, rowcfg);
59154                         alt.push(rowcfg.rowClass);
59155                     }
59156                     
59157                     rp.alt = alt.join(" ");
59158                     rp.cells = lcb.join("");
59159                     lbuf[lbuf.length] = rt.apply(rp);
59160                     rp.cells = cb.join("");
59161                     buf[buf.length] =  rt.apply(rp);
59162                 }
59163                 return [lbuf.join(""), buf.join("")];
59164             },
59165
59166     renderBody : function(){
59167         var markup = this.renderRows();
59168         var bt = this.templates.body;
59169         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59170     },
59171
59172     /**
59173      * Refreshes the grid
59174      * @param {Boolean} headersToo
59175      */
59176     refresh : function(headersToo){
59177         this.fireEvent("beforerefresh", this);
59178         this.grid.stopEditing();
59179         var result = this.renderBody();
59180         this.lockedBody.update(result[0]);
59181         this.mainBody.update(result[1]);
59182         if(headersToo === true){
59183             this.updateHeaders();
59184             this.updateColumns();
59185             this.updateSplitters();
59186             this.updateHeaderSortState();
59187         }
59188         this.syncRowHeights();
59189         this.layout();
59190         this.fireEvent("refresh", this);
59191     },
59192
59193     handleColumnMove : function(cm, oldIndex, newIndex){
59194         this.indexMap = null;
59195         var s = this.getScrollState();
59196         this.refresh(true);
59197         this.restoreScroll(s);
59198         this.afterMove(newIndex);
59199     },
59200
59201     afterMove : function(colIndex){
59202         if(this.enableMoveAnim && Roo.enableFx){
59203             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59204         }
59205         // if multisort - fix sortOrder, and reload..
59206         if (this.grid.dataSource.multiSort) {
59207             // the we can call sort again..
59208             var dm = this.grid.dataSource;
59209             var cm = this.grid.colModel;
59210             var so = [];
59211             for(var i = 0; i < cm.config.length; i++ ) {
59212                 
59213                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59214                     continue; // dont' bother, it's not in sort list or being set.
59215                 }
59216                 
59217                 so.push(cm.config[i].dataIndex);
59218             };
59219             dm.sortOrder = so;
59220             dm.load(dm.lastOptions);
59221             
59222             
59223         }
59224         
59225     },
59226
59227     updateCell : function(dm, rowIndex, dataIndex){
59228         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59229         if(typeof colIndex == "undefined"){ // not present in grid
59230             return;
59231         }
59232         var cm = this.grid.colModel;
59233         var cell = this.getCell(rowIndex, colIndex);
59234         var cellText = this.getCellText(rowIndex, colIndex);
59235
59236         var p = {
59237             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59238             id : cm.getColumnId(colIndex),
59239             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59240         };
59241         var renderer = cm.getRenderer(colIndex);
59242         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59243         if(typeof val == "undefined" || val === "") {
59244             val = "&#160;";
59245         }
59246         cellText.innerHTML = val;
59247         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59248         this.syncRowHeights(rowIndex, rowIndex);
59249     },
59250
59251     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59252         var maxWidth = 0;
59253         if(this.grid.autoSizeHeaders){
59254             var h = this.getHeaderCellMeasure(colIndex);
59255             maxWidth = Math.max(maxWidth, h.scrollWidth);
59256         }
59257         var tb, index;
59258         if(this.cm.isLocked(colIndex)){
59259             tb = this.getLockedTable();
59260             index = colIndex;
59261         }else{
59262             tb = this.getBodyTable();
59263             index = colIndex - this.cm.getLockedCount();
59264         }
59265         if(tb && tb.rows){
59266             var rows = tb.rows;
59267             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59268             for(var i = 0; i < stopIndex; i++){
59269                 var cell = rows[i].childNodes[index].firstChild;
59270                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59271             }
59272         }
59273         return maxWidth + /*margin for error in IE*/ 5;
59274     },
59275     /**
59276      * Autofit a column to its content.
59277      * @param {Number} colIndex
59278      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59279      */
59280      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59281          if(this.cm.isHidden(colIndex)){
59282              return; // can't calc a hidden column
59283          }
59284         if(forceMinSize){
59285             var cid = this.cm.getColumnId(colIndex);
59286             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59287            if(this.grid.autoSizeHeaders){
59288                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59289            }
59290         }
59291         var newWidth = this.calcColumnWidth(colIndex);
59292         this.cm.setColumnWidth(colIndex,
59293             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59294         if(!suppressEvent){
59295             this.grid.fireEvent("columnresize", colIndex, newWidth);
59296         }
59297     },
59298
59299     /**
59300      * Autofits all columns to their content and then expands to fit any extra space in the grid
59301      */
59302      autoSizeColumns : function(){
59303         var cm = this.grid.colModel;
59304         var colCount = cm.getColumnCount();
59305         for(var i = 0; i < colCount; i++){
59306             this.autoSizeColumn(i, true, true);
59307         }
59308         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59309             this.fitColumns();
59310         }else{
59311             this.updateColumns();
59312             this.layout();
59313         }
59314     },
59315
59316     /**
59317      * Autofits all columns to the grid's width proportionate with their current size
59318      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59319      */
59320     fitColumns : function(reserveScrollSpace){
59321         var cm = this.grid.colModel;
59322         var colCount = cm.getColumnCount();
59323         var cols = [];
59324         var width = 0;
59325         var i, w;
59326         for (i = 0; i < colCount; i++){
59327             if(!cm.isHidden(i) && !cm.isFixed(i)){
59328                 w = cm.getColumnWidth(i);
59329                 cols.push(i);
59330                 cols.push(w);
59331                 width += w;
59332             }
59333         }
59334         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59335         if(reserveScrollSpace){
59336             avail -= 17;
59337         }
59338         var frac = (avail - cm.getTotalWidth())/width;
59339         while (cols.length){
59340             w = cols.pop();
59341             i = cols.pop();
59342             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59343         }
59344         this.updateColumns();
59345         this.layout();
59346     },
59347
59348     onRowSelect : function(rowIndex){
59349         var row = this.getRowComposite(rowIndex);
59350         row.addClass("x-grid-row-selected");
59351     },
59352
59353     onRowDeselect : function(rowIndex){
59354         var row = this.getRowComposite(rowIndex);
59355         row.removeClass("x-grid-row-selected");
59356     },
59357
59358     onCellSelect : function(row, col){
59359         var cell = this.getCell(row, col);
59360         if(cell){
59361             Roo.fly(cell).addClass("x-grid-cell-selected");
59362         }
59363     },
59364
59365     onCellDeselect : function(row, col){
59366         var cell = this.getCell(row, col);
59367         if(cell){
59368             Roo.fly(cell).removeClass("x-grid-cell-selected");
59369         }
59370     },
59371
59372     updateHeaderSortState : function(){
59373         
59374         // sort state can be single { field: xxx, direction : yyy}
59375         // or   { xxx=>ASC , yyy : DESC ..... }
59376         
59377         var mstate = {};
59378         if (!this.ds.multiSort) { 
59379             var state = this.ds.getSortState();
59380             if(!state){
59381                 return;
59382             }
59383             mstate[state.field] = state.direction;
59384             // FIXME... - this is not used here.. but might be elsewhere..
59385             this.sortState = state;
59386             
59387         } else {
59388             mstate = this.ds.sortToggle;
59389         }
59390         //remove existing sort classes..
59391         
59392         var sc = this.sortClasses;
59393         var hds = this.el.select(this.headerSelector).removeClass(sc);
59394         
59395         for(var f in mstate) {
59396         
59397             var sortColumn = this.cm.findColumnIndex(f);
59398             
59399             if(sortColumn != -1){
59400                 var sortDir = mstate[f];        
59401                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59402             }
59403         }
59404         
59405          
59406         
59407     },
59408
59409
59410     handleHeaderClick : function(g, index,e){
59411         
59412         Roo.log("header click");
59413         
59414         if (Roo.isTouch) {
59415             // touch events on header are handled by context
59416             this.handleHdCtx(g,index,e);
59417             return;
59418         }
59419         
59420         
59421         if(this.headersDisabled){
59422             return;
59423         }
59424         var dm = g.dataSource, cm = g.colModel;
59425         if(!cm.isSortable(index)){
59426             return;
59427         }
59428         g.stopEditing();
59429         
59430         if (dm.multiSort) {
59431             // update the sortOrder
59432             var so = [];
59433             for(var i = 0; i < cm.config.length; i++ ) {
59434                 
59435                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
59436                     continue; // dont' bother, it's not in sort list or being set.
59437                 }
59438                 
59439                 so.push(cm.config[i].dataIndex);
59440             };
59441             dm.sortOrder = so;
59442         }
59443         
59444         
59445         dm.sort(cm.getDataIndex(index));
59446     },
59447
59448
59449     destroy : function(){
59450         if(this.colMenu){
59451             this.colMenu.removeAll();
59452             Roo.menu.MenuMgr.unregister(this.colMenu);
59453             this.colMenu.getEl().remove();
59454             delete this.colMenu;
59455         }
59456         if(this.hmenu){
59457             this.hmenu.removeAll();
59458             Roo.menu.MenuMgr.unregister(this.hmenu);
59459             this.hmenu.getEl().remove();
59460             delete this.hmenu;
59461         }
59462         if(this.grid.enableColumnMove){
59463             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59464             if(dds){
59465                 for(var dd in dds){
59466                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
59467                         var elid = dds[dd].dragElId;
59468                         dds[dd].unreg();
59469                         Roo.get(elid).remove();
59470                     } else if(dds[dd].config.isTarget){
59471                         dds[dd].proxyTop.remove();
59472                         dds[dd].proxyBottom.remove();
59473                         dds[dd].unreg();
59474                     }
59475                     if(Roo.dd.DDM.locationCache[dd]){
59476                         delete Roo.dd.DDM.locationCache[dd];
59477                     }
59478                 }
59479                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59480             }
59481         }
59482         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
59483         this.bind(null, null);
59484         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
59485     },
59486
59487     handleLockChange : function(){
59488         this.refresh(true);
59489     },
59490
59491     onDenyColumnLock : function(){
59492
59493     },
59494
59495     onDenyColumnHide : function(){
59496
59497     },
59498
59499     handleHdMenuClick : function(item){
59500         var index = this.hdCtxIndex;
59501         var cm = this.cm, ds = this.ds;
59502         switch(item.id){
59503             case "asc":
59504                 ds.sort(cm.getDataIndex(index), "ASC");
59505                 break;
59506             case "desc":
59507                 ds.sort(cm.getDataIndex(index), "DESC");
59508                 break;
59509             case "lock":
59510                 var lc = cm.getLockedCount();
59511                 if(cm.getColumnCount(true) <= lc+1){
59512                     this.onDenyColumnLock();
59513                     return;
59514                 }
59515                 if(lc != index){
59516                     cm.setLocked(index, true, true);
59517                     cm.moveColumn(index, lc);
59518                     this.grid.fireEvent("columnmove", index, lc);
59519                 }else{
59520                     cm.setLocked(index, true);
59521                 }
59522             break;
59523             case "unlock":
59524                 var lc = cm.getLockedCount();
59525                 if((lc-1) != index){
59526                     cm.setLocked(index, false, true);
59527                     cm.moveColumn(index, lc-1);
59528                     this.grid.fireEvent("columnmove", index, lc-1);
59529                 }else{
59530                     cm.setLocked(index, false);
59531                 }
59532             break;
59533             case 'wider': // used to expand cols on touch..
59534             case 'narrow':
59535                 var cw = cm.getColumnWidth(index);
59536                 cw += (item.id == 'wider' ? 1 : -1) * 50;
59537                 cw = Math.max(0, cw);
59538                 cw = Math.min(cw,4000);
59539                 cm.setColumnWidth(index, cw);
59540                 break;
59541                 
59542             default:
59543                 index = cm.getIndexById(item.id.substr(4));
59544                 if(index != -1){
59545                     if(item.checked && cm.getColumnCount(true) <= 1){
59546                         this.onDenyColumnHide();
59547                         return false;
59548                     }
59549                     cm.setHidden(index, item.checked);
59550                 }
59551         }
59552         return true;
59553     },
59554
59555     beforeColMenuShow : function(){
59556         var cm = this.cm,  colCount = cm.getColumnCount();
59557         this.colMenu.removeAll();
59558         for(var i = 0; i < colCount; i++){
59559             this.colMenu.add(new Roo.menu.CheckItem({
59560                 id: "col-"+cm.getColumnId(i),
59561                 text: cm.getColumnHeader(i),
59562                 checked: !cm.isHidden(i),
59563                 hideOnClick:false
59564             }));
59565         }
59566     },
59567
59568     handleHdCtx : function(g, index, e){
59569         e.stopEvent();
59570         var hd = this.getHeaderCell(index);
59571         this.hdCtxIndex = index;
59572         var ms = this.hmenu.items, cm = this.cm;
59573         ms.get("asc").setDisabled(!cm.isSortable(index));
59574         ms.get("desc").setDisabled(!cm.isSortable(index));
59575         if(this.grid.enableColLock !== false){
59576             ms.get("lock").setDisabled(cm.isLocked(index));
59577             ms.get("unlock").setDisabled(!cm.isLocked(index));
59578         }
59579         this.hmenu.show(hd, "tl-bl");
59580     },
59581
59582     handleHdOver : function(e){
59583         var hd = this.findHeaderCell(e.getTarget());
59584         if(hd && !this.headersDisabled){
59585             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
59586                this.fly(hd).addClass("x-grid-hd-over");
59587             }
59588         }
59589     },
59590
59591     handleHdOut : function(e){
59592         var hd = this.findHeaderCell(e.getTarget());
59593         if(hd){
59594             this.fly(hd).removeClass("x-grid-hd-over");
59595         }
59596     },
59597
59598     handleSplitDblClick : function(e, t){
59599         var i = this.getCellIndex(t);
59600         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
59601             this.autoSizeColumn(i, true);
59602             this.layout();
59603         }
59604     },
59605
59606     render : function(){
59607
59608         var cm = this.cm;
59609         var colCount = cm.getColumnCount();
59610
59611         if(this.grid.monitorWindowResize === true){
59612             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
59613         }
59614         var header = this.renderHeaders();
59615         var body = this.templates.body.apply({rows:""});
59616         var html = this.templates.master.apply({
59617             lockedBody: body,
59618             body: body,
59619             lockedHeader: header[0],
59620             header: header[1]
59621         });
59622
59623         //this.updateColumns();
59624
59625         this.grid.getGridEl().dom.innerHTML = html;
59626
59627         this.initElements();
59628         
59629         // a kludge to fix the random scolling effect in webkit
59630         this.el.on("scroll", function() {
59631             this.el.dom.scrollTop=0; // hopefully not recursive..
59632         },this);
59633
59634         this.scroller.on("scroll", this.handleScroll, this);
59635         this.lockedBody.on("mousewheel", this.handleWheel, this);
59636         this.mainBody.on("mousewheel", this.handleWheel, this);
59637
59638         this.mainHd.on("mouseover", this.handleHdOver, this);
59639         this.mainHd.on("mouseout", this.handleHdOut, this);
59640         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
59641                 {delegate: "."+this.splitClass});
59642
59643         this.lockedHd.on("mouseover", this.handleHdOver, this);
59644         this.lockedHd.on("mouseout", this.handleHdOut, this);
59645         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
59646                 {delegate: "."+this.splitClass});
59647
59648         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
59649             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59650         }
59651
59652         this.updateSplitters();
59653
59654         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
59655             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59656             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59657         }
59658
59659         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
59660             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
59661             this.hmenu.add(
59662                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
59663                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
59664             );
59665             if(this.grid.enableColLock !== false){
59666                 this.hmenu.add('-',
59667                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
59668                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
59669                 );
59670             }
59671             if (Roo.isTouch) {
59672                  this.hmenu.add('-',
59673                     {id:"wider", text: this.columnsWiderText},
59674                     {id:"narrow", text: this.columnsNarrowText }
59675                 );
59676                 
59677                  
59678             }
59679             
59680             if(this.grid.enableColumnHide !== false){
59681
59682                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
59683                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
59684                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
59685
59686                 this.hmenu.add('-',
59687                     {id:"columns", text: this.columnsText, menu: this.colMenu}
59688                 );
59689             }
59690             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
59691
59692             this.grid.on("headercontextmenu", this.handleHdCtx, this);
59693         }
59694
59695         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
59696             this.dd = new Roo.grid.GridDragZone(this.grid, {
59697                 ddGroup : this.grid.ddGroup || 'GridDD'
59698             });
59699             
59700         }
59701
59702         /*
59703         for(var i = 0; i < colCount; i++){
59704             if(cm.isHidden(i)){
59705                 this.hideColumn(i);
59706             }
59707             if(cm.config[i].align){
59708                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
59709                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
59710             }
59711         }*/
59712         
59713         this.updateHeaderSortState();
59714
59715         this.beforeInitialResize();
59716         this.layout(true);
59717
59718         // two part rendering gives faster view to the user
59719         this.renderPhase2.defer(1, this);
59720     },
59721
59722     renderPhase2 : function(){
59723         // render the rows now
59724         this.refresh();
59725         if(this.grid.autoSizeColumns){
59726             this.autoSizeColumns();
59727         }
59728     },
59729
59730     beforeInitialResize : function(){
59731
59732     },
59733
59734     onColumnSplitterMoved : function(i, w){
59735         this.userResized = true;
59736         var cm = this.grid.colModel;
59737         cm.setColumnWidth(i, w, true);
59738         var cid = cm.getColumnId(i);
59739         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59740         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59741         this.updateSplitters();
59742         this.layout();
59743         this.grid.fireEvent("columnresize", i, w);
59744     },
59745
59746     syncRowHeights : function(startIndex, endIndex){
59747         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59748             startIndex = startIndex || 0;
59749             var mrows = this.getBodyTable().rows;
59750             var lrows = this.getLockedTable().rows;
59751             var len = mrows.length-1;
59752             endIndex = Math.min(endIndex || len, len);
59753             for(var i = startIndex; i <= endIndex; i++){
59754                 var m = mrows[i], l = lrows[i];
59755                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59756                 m.style.height = l.style.height = h + "px";
59757             }
59758         }
59759     },
59760
59761     layout : function(initialRender, is2ndPass)
59762     {
59763         var g = this.grid;
59764         var auto = g.autoHeight;
59765         var scrollOffset = 16;
59766         var c = g.getGridEl(), cm = this.cm,
59767                 expandCol = g.autoExpandColumn,
59768                 gv = this;
59769         //c.beginMeasure();
59770
59771         if(!c.dom.offsetWidth){ // display:none?
59772             if(initialRender){
59773                 this.lockedWrap.show();
59774                 this.mainWrap.show();
59775             }
59776             return;
59777         }
59778
59779         var hasLock = this.cm.isLocked(0);
59780
59781         var tbh = this.headerPanel.getHeight();
59782         var bbh = this.footerPanel.getHeight();
59783
59784         if(auto){
59785             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59786             var newHeight = ch + c.getBorderWidth("tb");
59787             if(g.maxHeight){
59788                 newHeight = Math.min(g.maxHeight, newHeight);
59789             }
59790             c.setHeight(newHeight);
59791         }
59792
59793         if(g.autoWidth){
59794             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59795         }
59796
59797         var s = this.scroller;
59798
59799         var csize = c.getSize(true);
59800
59801         this.el.setSize(csize.width, csize.height);
59802
59803         this.headerPanel.setWidth(csize.width);
59804         this.footerPanel.setWidth(csize.width);
59805
59806         var hdHeight = this.mainHd.getHeight();
59807         var vw = csize.width;
59808         var vh = csize.height - (tbh + bbh);
59809
59810         s.setSize(vw, vh);
59811
59812         var bt = this.getBodyTable();
59813         
59814         if(cm.getLockedCount() == cm.config.length){
59815             bt = this.getLockedTable();
59816         }
59817         
59818         var ltWidth = hasLock ?
59819                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59820
59821         var scrollHeight = bt.offsetHeight;
59822         var scrollWidth = ltWidth + bt.offsetWidth;
59823         var vscroll = false, hscroll = false;
59824
59825         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59826
59827         var lw = this.lockedWrap, mw = this.mainWrap;
59828         var lb = this.lockedBody, mb = this.mainBody;
59829
59830         setTimeout(function(){
59831             var t = s.dom.offsetTop;
59832             var w = s.dom.clientWidth,
59833                 h = s.dom.clientHeight;
59834
59835             lw.setTop(t);
59836             lw.setSize(ltWidth, h);
59837
59838             mw.setLeftTop(ltWidth, t);
59839             mw.setSize(w-ltWidth, h);
59840
59841             lb.setHeight(h-hdHeight);
59842             mb.setHeight(h-hdHeight);
59843
59844             if(is2ndPass !== true && !gv.userResized && expandCol){
59845                 // high speed resize without full column calculation
59846                 
59847                 var ci = cm.getIndexById(expandCol);
59848                 if (ci < 0) {
59849                     ci = cm.findColumnIndex(expandCol);
59850                 }
59851                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59852                 var expandId = cm.getColumnId(ci);
59853                 var  tw = cm.getTotalWidth(false);
59854                 var currentWidth = cm.getColumnWidth(ci);
59855                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59856                 if(currentWidth != cw){
59857                     cm.setColumnWidth(ci, cw, true);
59858                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59859                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59860                     gv.updateSplitters();
59861                     gv.layout(false, true);
59862                 }
59863             }
59864
59865             if(initialRender){
59866                 lw.show();
59867                 mw.show();
59868             }
59869             //c.endMeasure();
59870         }, 10);
59871     },
59872
59873     onWindowResize : function(){
59874         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59875             return;
59876         }
59877         this.layout();
59878     },
59879
59880     appendFooter : function(parentEl){
59881         return null;
59882     },
59883
59884     sortAscText : "Sort Ascending",
59885     sortDescText : "Sort Descending",
59886     lockText : "Lock Column",
59887     unlockText : "Unlock Column",
59888     columnsText : "Columns",
59889  
59890     columnsWiderText : "Wider",
59891     columnsNarrowText : "Thinner"
59892 });
59893
59894
59895 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59896     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59897     this.proxy.el.addClass('x-grid3-col-dd');
59898 };
59899
59900 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59901     handleMouseDown : function(e){
59902
59903     },
59904
59905     callHandleMouseDown : function(e){
59906         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59907     }
59908 });
59909 /*
59910  * Based on:
59911  * Ext JS Library 1.1.1
59912  * Copyright(c) 2006-2007, Ext JS, LLC.
59913  *
59914  * Originally Released Under LGPL - original licence link has changed is not relivant.
59915  *
59916  * Fork - LGPL
59917  * <script type="text/javascript">
59918  */
59919  /**
59920  * @extends Roo.dd.DDProxy
59921  * @class Roo.grid.SplitDragZone
59922  * Support for Column Header resizing
59923  * @constructor
59924  * @param {Object} config
59925  */
59926 // private
59927 // This is a support class used internally by the Grid components
59928 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59929     this.grid = grid;
59930     this.view = grid.getView();
59931     this.proxy = this.view.resizeProxy;
59932     Roo.grid.SplitDragZone.superclass.constructor.call(
59933         this,
59934         hd, // ID
59935         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59936         {  // CONFIG
59937             dragElId : Roo.id(this.proxy.dom),
59938             resizeFrame:false
59939         }
59940     );
59941     
59942     this.setHandleElId(Roo.id(hd));
59943     if (hd2 !== false) {
59944         this.setOuterHandleElId(Roo.id(hd2));
59945     }
59946     
59947     this.scroll = false;
59948 };
59949 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59950     fly: Roo.Element.fly,
59951
59952     b4StartDrag : function(x, y){
59953         this.view.headersDisabled = true;
59954         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59955                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59956         );
59957         this.proxy.setHeight(h);
59958         
59959         // for old system colWidth really stored the actual width?
59960         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59961         // which in reality did not work.. - it worked only for fixed sizes
59962         // for resizable we need to use actual sizes.
59963         var w = this.cm.getColumnWidth(this.cellIndex);
59964         if (!this.view.mainWrap) {
59965             // bootstrap.
59966             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59967         }
59968         
59969         
59970         
59971         // this was w-this.grid.minColumnWidth;
59972         // doesnt really make sense? - w = thie curren width or the rendered one?
59973         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59974         this.resetConstraints();
59975         this.setXConstraint(minw, 1000);
59976         this.setYConstraint(0, 0);
59977         this.minX = x - minw;
59978         this.maxX = x + 1000;
59979         this.startPos = x;
59980         if (!this.view.mainWrap) { // this is Bootstrap code..
59981             this.getDragEl().style.display='block';
59982         }
59983         
59984         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59985     },
59986
59987
59988     handleMouseDown : function(e){
59989         ev = Roo.EventObject.setEvent(e);
59990         var t = this.fly(ev.getTarget());
59991         if(t.hasClass("x-grid-split")){
59992             this.cellIndex = this.view.getCellIndex(t.dom);
59993             this.split = t.dom;
59994             this.cm = this.grid.colModel;
59995             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59996                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59997             }
59998         }
59999     },
60000
60001     endDrag : function(e){
60002         this.view.headersDisabled = false;
60003         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60004         var diff = endX - this.startPos;
60005         // 
60006         var w = this.cm.getColumnWidth(this.cellIndex);
60007         if (!this.view.mainWrap) {
60008             w = 0;
60009         }
60010         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60011     },
60012
60013     autoOffset : function(){
60014         this.setDelta(0,0);
60015     }
60016 });/*
60017  * Based on:
60018  * Ext JS Library 1.1.1
60019  * Copyright(c) 2006-2007, Ext JS, LLC.
60020  *
60021  * Originally Released Under LGPL - original licence link has changed is not relivant.
60022  *
60023  * Fork - LGPL
60024  * <script type="text/javascript">
60025  */
60026  
60027 // private
60028 // This is a support class used internally by the Grid components
60029 Roo.grid.GridDragZone = function(grid, config){
60030     this.view = grid.getView();
60031     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60032     if(this.view.lockedBody){
60033         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60034         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60035     }
60036     this.scroll = false;
60037     this.grid = grid;
60038     this.ddel = document.createElement('div');
60039     this.ddel.className = 'x-grid-dd-wrap';
60040 };
60041
60042 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60043     ddGroup : "GridDD",
60044
60045     getDragData : function(e){
60046         var t = Roo.lib.Event.getTarget(e);
60047         var rowIndex = this.view.findRowIndex(t);
60048         var sm = this.grid.selModel;
60049             
60050         //Roo.log(rowIndex);
60051         
60052         if (sm.getSelectedCell) {
60053             // cell selection..
60054             if (!sm.getSelectedCell()) {
60055                 return false;
60056             }
60057             if (rowIndex != sm.getSelectedCell()[0]) {
60058                 return false;
60059             }
60060         
60061         }
60062         if (sm.getSelections && sm.getSelections().length < 1) {
60063             return false;
60064         }
60065         
60066         
60067         // before it used to all dragging of unseleted... - now we dont do that.
60068         if(rowIndex !== false){
60069             
60070             // if editorgrid.. 
60071             
60072             
60073             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60074                
60075             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60076               //  
60077             //}
60078             if (e.hasModifier()){
60079                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60080             }
60081             
60082             Roo.log("getDragData");
60083             
60084             return {
60085                 grid: this.grid,
60086                 ddel: this.ddel,
60087                 rowIndex: rowIndex,
60088                 selections: sm.getSelections ? sm.getSelections() : (
60089                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60090             };
60091         }
60092         return false;
60093     },
60094     
60095     
60096     onInitDrag : function(e){
60097         var data = this.dragData;
60098         this.ddel.innerHTML = this.grid.getDragDropText();
60099         this.proxy.update(this.ddel);
60100         // fire start drag?
60101     },
60102
60103     afterRepair : function(){
60104         this.dragging = false;
60105     },
60106
60107     getRepairXY : function(e, data){
60108         return false;
60109     },
60110
60111     onEndDrag : function(data, e){
60112         // fire end drag?
60113     },
60114
60115     onValidDrop : function(dd, e, id){
60116         // fire drag drop?
60117         this.hideProxy();
60118     },
60119
60120     beforeInvalidDrop : function(e, id){
60121
60122     }
60123 });/*
60124  * Based on:
60125  * Ext JS Library 1.1.1
60126  * Copyright(c) 2006-2007, Ext JS, LLC.
60127  *
60128  * Originally Released Under LGPL - original licence link has changed is not relivant.
60129  *
60130  * Fork - LGPL
60131  * <script type="text/javascript">
60132  */
60133  
60134
60135 /**
60136  * @class Roo.grid.ColumnModel
60137  * @extends Roo.util.Observable
60138  * This is the default implementation of a ColumnModel used by the Grid. It defines
60139  * the columns in the grid.
60140  * <br>Usage:<br>
60141  <pre><code>
60142  var colModel = new Roo.grid.ColumnModel([
60143         {header: "Ticker", width: 60, sortable: true, locked: true},
60144         {header: "Company Name", width: 150, sortable: true},
60145         {header: "Market Cap.", width: 100, sortable: true},
60146         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60147         {header: "Employees", width: 100, sortable: true, resizable: false}
60148  ]);
60149  </code></pre>
60150  * <p>
60151  
60152  * The config options listed for this class are options which may appear in each
60153  * individual column definition.
60154  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60155  * @constructor
60156  * @param {Object} config An Array of column config objects. See this class's
60157  * config objects for details.
60158 */
60159 Roo.grid.ColumnModel = function(config){
60160         /**
60161      * The config passed into the constructor
60162      */
60163     this.config = []; //config;
60164     this.lookup = {};
60165
60166     // if no id, create one
60167     // if the column does not have a dataIndex mapping,
60168     // map it to the order it is in the config
60169     for(var i = 0, len = config.length; i < len; i++){
60170         this.addColumn(config[i]);
60171         
60172     }
60173
60174     /**
60175      * The width of columns which have no width specified (defaults to 100)
60176      * @type Number
60177      */
60178     this.defaultWidth = 100;
60179
60180     /**
60181      * Default sortable of columns which have no sortable specified (defaults to false)
60182      * @type Boolean
60183      */
60184     this.defaultSortable = false;
60185
60186     this.addEvents({
60187         /**
60188              * @event widthchange
60189              * Fires when the width of a column changes.
60190              * @param {ColumnModel} this
60191              * @param {Number} columnIndex The column index
60192              * @param {Number} newWidth The new width
60193              */
60194             "widthchange": true,
60195         /**
60196              * @event headerchange
60197              * Fires when the text of a header changes.
60198              * @param {ColumnModel} this
60199              * @param {Number} columnIndex The column index
60200              * @param {Number} newText The new header text
60201              */
60202             "headerchange": true,
60203         /**
60204              * @event hiddenchange
60205              * Fires when a column is hidden or "unhidden".
60206              * @param {ColumnModel} this
60207              * @param {Number} columnIndex The column index
60208              * @param {Boolean} hidden true if hidden, false otherwise
60209              */
60210             "hiddenchange": true,
60211             /**
60212          * @event columnmoved
60213          * Fires when a column is moved.
60214          * @param {ColumnModel} this
60215          * @param {Number} oldIndex
60216          * @param {Number} newIndex
60217          */
60218         "columnmoved" : true,
60219         /**
60220          * @event columlockchange
60221          * Fires when a column's locked state is changed
60222          * @param {ColumnModel} this
60223          * @param {Number} colIndex
60224          * @param {Boolean} locked true if locked
60225          */
60226         "columnlockchange" : true
60227     });
60228     Roo.grid.ColumnModel.superclass.constructor.call(this);
60229 };
60230 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60231     /**
60232      * @cfg {String} header The header text to display in the Grid view.
60233      */
60234         /**
60235      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60236      */
60237         /**
60238      * @cfg {String} smHeader Header at Bootsrap Small width
60239      */
60240         /**
60241      * @cfg {String} mdHeader Header at Bootsrap Medium width
60242      */
60243         /**
60244      * @cfg {String} lgHeader Header at Bootsrap Large width
60245      */
60246         /**
60247      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60248      */
60249     /**
60250      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60251      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60252      * specified, the column's index is used as an index into the Record's data Array.
60253      */
60254     /**
60255      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60256      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60257      */
60258     /**
60259      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60260      * Defaults to the value of the {@link #defaultSortable} property.
60261      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60262      */
60263     /**
60264      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60265      */
60266     /**
60267      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60268      */
60269     /**
60270      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60271      */
60272     /**
60273      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60274      */
60275     /**
60276      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60277      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60278      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60279      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60280      */
60281        /**
60282      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60283      */
60284     /**
60285      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60286      */
60287     /**
60288      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60289      */
60290     /**
60291      * @cfg {String} cursor (Optional)
60292      */
60293     /**
60294      * @cfg {String} tooltip (Optional)
60295      */
60296     /**
60297      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60298      */
60299     /**
60300      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60301      */
60302     /**
60303      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60304      */
60305     /**
60306      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60307      */
60308         /**
60309      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60310      */
60311     /**
60312      * Returns the id of the column at the specified index.
60313      * @param {Number} index The column index
60314      * @return {String} the id
60315      */
60316     getColumnId : function(index){
60317         return this.config[index].id;
60318     },
60319
60320     /**
60321      * Returns the column for a specified id.
60322      * @param {String} id The column id
60323      * @return {Object} the column
60324      */
60325     getColumnById : function(id){
60326         return this.lookup[id];
60327     },
60328
60329     
60330     /**
60331      * Returns the column Object for a specified dataIndex.
60332      * @param {String} dataIndex The column dataIndex
60333      * @return {Object|Boolean} the column or false if not found
60334      */
60335     getColumnByDataIndex: function(dataIndex){
60336         var index = this.findColumnIndex(dataIndex);
60337         return index > -1 ? this.config[index] : false;
60338     },
60339     
60340     /**
60341      * Returns the index for a specified column id.
60342      * @param {String} id The column id
60343      * @return {Number} the index, or -1 if not found
60344      */
60345     getIndexById : function(id){
60346         for(var i = 0, len = this.config.length; i < len; i++){
60347             if(this.config[i].id == id){
60348                 return i;
60349             }
60350         }
60351         return -1;
60352     },
60353     
60354     /**
60355      * Returns the index for a specified column dataIndex.
60356      * @param {String} dataIndex The column dataIndex
60357      * @return {Number} the index, or -1 if not found
60358      */
60359     
60360     findColumnIndex : function(dataIndex){
60361         for(var i = 0, len = this.config.length; i < len; i++){
60362             if(this.config[i].dataIndex == dataIndex){
60363                 return i;
60364             }
60365         }
60366         return -1;
60367     },
60368     
60369     
60370     moveColumn : function(oldIndex, newIndex){
60371         var c = this.config[oldIndex];
60372         this.config.splice(oldIndex, 1);
60373         this.config.splice(newIndex, 0, c);
60374         this.dataMap = null;
60375         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60376     },
60377
60378     isLocked : function(colIndex){
60379         return this.config[colIndex].locked === true;
60380     },
60381
60382     setLocked : function(colIndex, value, suppressEvent){
60383         if(this.isLocked(colIndex) == value){
60384             return;
60385         }
60386         this.config[colIndex].locked = value;
60387         if(!suppressEvent){
60388             this.fireEvent("columnlockchange", this, colIndex, value);
60389         }
60390     },
60391
60392     getTotalLockedWidth : function(){
60393         var totalWidth = 0;
60394         for(var i = 0; i < this.config.length; i++){
60395             if(this.isLocked(i) && !this.isHidden(i)){
60396                 this.totalWidth += this.getColumnWidth(i);
60397             }
60398         }
60399         return totalWidth;
60400     },
60401
60402     getLockedCount : function(){
60403         for(var i = 0, len = this.config.length; i < len; i++){
60404             if(!this.isLocked(i)){
60405                 return i;
60406             }
60407         }
60408         
60409         return this.config.length;
60410     },
60411
60412     /**
60413      * Returns the number of columns.
60414      * @return {Number}
60415      */
60416     getColumnCount : function(visibleOnly){
60417         if(visibleOnly === true){
60418             var c = 0;
60419             for(var i = 0, len = this.config.length; i < len; i++){
60420                 if(!this.isHidden(i)){
60421                     c++;
60422                 }
60423             }
60424             return c;
60425         }
60426         return this.config.length;
60427     },
60428
60429     /**
60430      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
60431      * @param {Function} fn
60432      * @param {Object} scope (optional)
60433      * @return {Array} result
60434      */
60435     getColumnsBy : function(fn, scope){
60436         var r = [];
60437         for(var i = 0, len = this.config.length; i < len; i++){
60438             var c = this.config[i];
60439             if(fn.call(scope||this, c, i) === true){
60440                 r[r.length] = c;
60441             }
60442         }
60443         return r;
60444     },
60445
60446     /**
60447      * Returns true if the specified column is sortable.
60448      * @param {Number} col The column index
60449      * @return {Boolean}
60450      */
60451     isSortable : function(col){
60452         if(typeof this.config[col].sortable == "undefined"){
60453             return this.defaultSortable;
60454         }
60455         return this.config[col].sortable;
60456     },
60457
60458     /**
60459      * Returns the rendering (formatting) function defined for the column.
60460      * @param {Number} col The column index.
60461      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
60462      */
60463     getRenderer : function(col){
60464         if(!this.config[col].renderer){
60465             return Roo.grid.ColumnModel.defaultRenderer;
60466         }
60467         return this.config[col].renderer;
60468     },
60469
60470     /**
60471      * Sets the rendering (formatting) function for a column.
60472      * @param {Number} col The column index
60473      * @param {Function} fn The function to use to process the cell's raw data
60474      * to return HTML markup for the grid view. The render function is called with
60475      * the following parameters:<ul>
60476      * <li>Data value.</li>
60477      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
60478      * <li>css A CSS style string to apply to the table cell.</li>
60479      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
60480      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
60481      * <li>Row index</li>
60482      * <li>Column index</li>
60483      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
60484      */
60485     setRenderer : function(col, fn){
60486         this.config[col].renderer = fn;
60487     },
60488
60489     /**
60490      * Returns the width for the specified column.
60491      * @param {Number} col The column index
60492      * @param (optional) {String} gridSize bootstrap width size.
60493      * @return {Number}
60494      */
60495     getColumnWidth : function(col, gridSize)
60496         {
60497                 var cfg = this.config[col];
60498                 
60499                 if (typeof(gridSize) == 'undefined') {
60500                         return cfg.width * 1 || this.defaultWidth;
60501                 }
60502                 if (gridSize === false) { // if we set it..
60503                         return cfg.width || false;
60504                 }
60505                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
60506                 
60507                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
60508                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
60509                                 continue;
60510                         }
60511                         return cfg[ sizes[i] ];
60512                 }
60513                 return 1;
60514                 
60515     },
60516
60517     /**
60518      * Sets the width for a column.
60519      * @param {Number} col The column index
60520      * @param {Number} width The new width
60521      */
60522     setColumnWidth : function(col, width, suppressEvent){
60523         this.config[col].width = width;
60524         this.totalWidth = null;
60525         if(!suppressEvent){
60526              this.fireEvent("widthchange", this, col, width);
60527         }
60528     },
60529
60530     /**
60531      * Returns the total width of all columns.
60532      * @param {Boolean} includeHidden True to include hidden column widths
60533      * @return {Number}
60534      */
60535     getTotalWidth : function(includeHidden){
60536         if(!this.totalWidth){
60537             this.totalWidth = 0;
60538             for(var i = 0, len = this.config.length; i < len; i++){
60539                 if(includeHidden || !this.isHidden(i)){
60540                     this.totalWidth += this.getColumnWidth(i);
60541                 }
60542             }
60543         }
60544         return this.totalWidth;
60545     },
60546
60547     /**
60548      * Returns the header for the specified column.
60549      * @param {Number} col The column index
60550      * @return {String}
60551      */
60552     getColumnHeader : function(col){
60553         return this.config[col].header;
60554     },
60555
60556     /**
60557      * Sets the header for a column.
60558      * @param {Number} col The column index
60559      * @param {String} header The new header
60560      */
60561     setColumnHeader : function(col, header){
60562         this.config[col].header = header;
60563         this.fireEvent("headerchange", this, col, header);
60564     },
60565
60566     /**
60567      * Returns the tooltip for the specified column.
60568      * @param {Number} col The column index
60569      * @return {String}
60570      */
60571     getColumnTooltip : function(col){
60572             return this.config[col].tooltip;
60573     },
60574     /**
60575      * Sets the tooltip for a column.
60576      * @param {Number} col The column index
60577      * @param {String} tooltip The new tooltip
60578      */
60579     setColumnTooltip : function(col, tooltip){
60580             this.config[col].tooltip = tooltip;
60581     },
60582
60583     /**
60584      * Returns the dataIndex for the specified column.
60585      * @param {Number} col The column index
60586      * @return {Number}
60587      */
60588     getDataIndex : function(col){
60589         return this.config[col].dataIndex;
60590     },
60591
60592     /**
60593      * Sets the dataIndex for a column.
60594      * @param {Number} col The column index
60595      * @param {Number} dataIndex The new dataIndex
60596      */
60597     setDataIndex : function(col, dataIndex){
60598         this.config[col].dataIndex = dataIndex;
60599     },
60600
60601     
60602     
60603     /**
60604      * Returns true if the cell is editable.
60605      * @param {Number} colIndex The column index
60606      * @param {Number} rowIndex The row index - this is nto actually used..?
60607      * @return {Boolean}
60608      */
60609     isCellEditable : function(colIndex, rowIndex){
60610         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
60611     },
60612
60613     /**
60614      * Returns the editor defined for the cell/column.
60615      * return false or null to disable editing.
60616      * @param {Number} colIndex The column index
60617      * @param {Number} rowIndex The row index
60618      * @return {Object}
60619      */
60620     getCellEditor : function(colIndex, rowIndex){
60621         return this.config[colIndex].editor;
60622     },
60623
60624     /**
60625      * Sets if a column is editable.
60626      * @param {Number} col The column index
60627      * @param {Boolean} editable True if the column is editable
60628      */
60629     setEditable : function(col, editable){
60630         this.config[col].editable = editable;
60631     },
60632
60633
60634     /**
60635      * Returns true if the column is hidden.
60636      * @param {Number} colIndex The column index
60637      * @return {Boolean}
60638      */
60639     isHidden : function(colIndex){
60640         return this.config[colIndex].hidden;
60641     },
60642
60643
60644     /**
60645      * Returns true if the column width cannot be changed
60646      */
60647     isFixed : function(colIndex){
60648         return this.config[colIndex].fixed;
60649     },
60650
60651     /**
60652      * Returns true if the column can be resized
60653      * @return {Boolean}
60654      */
60655     isResizable : function(colIndex){
60656         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
60657     },
60658     /**
60659      * Sets if a column is hidden.
60660      * @param {Number} colIndex The column index
60661      * @param {Boolean} hidden True if the column is hidden
60662      */
60663     setHidden : function(colIndex, hidden){
60664         this.config[colIndex].hidden = hidden;
60665         this.totalWidth = null;
60666         this.fireEvent("hiddenchange", this, colIndex, hidden);
60667     },
60668
60669     /**
60670      * Sets the editor for a column.
60671      * @param {Number} col The column index
60672      * @param {Object} editor The editor object
60673      */
60674     setEditor : function(col, editor){
60675         this.config[col].editor = editor;
60676     },
60677     /**
60678      * Add a column (experimental...) - defaults to adding to the end..
60679      * @param {Object} config 
60680     */
60681     addColumn : function(c)
60682     {
60683     
60684         var i = this.config.length;
60685         this.config[i] = c;
60686         
60687         if(typeof c.dataIndex == "undefined"){
60688             c.dataIndex = i;
60689         }
60690         if(typeof c.renderer == "string"){
60691             c.renderer = Roo.util.Format[c.renderer];
60692         }
60693         if(typeof c.id == "undefined"){
60694             c.id = Roo.id();
60695         }
60696         if(c.editor && c.editor.xtype){
60697             c.editor  = Roo.factory(c.editor, Roo.grid);
60698         }
60699         if(c.editor && c.editor.isFormField){
60700             c.editor = new Roo.grid.GridEditor(c.editor);
60701         }
60702         this.lookup[c.id] = c;
60703     }
60704     
60705 });
60706
60707 Roo.grid.ColumnModel.defaultRenderer = function(value)
60708 {
60709     if(typeof value == "object") {
60710         return value;
60711     }
60712         if(typeof value == "string" && value.length < 1){
60713             return "&#160;";
60714         }
60715     
60716         return String.format("{0}", value);
60717 };
60718
60719 // Alias for backwards compatibility
60720 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
60721 /*
60722  * Based on:
60723  * Ext JS Library 1.1.1
60724  * Copyright(c) 2006-2007, Ext JS, LLC.
60725  *
60726  * Originally Released Under LGPL - original licence link has changed is not relivant.
60727  *
60728  * Fork - LGPL
60729  * <script type="text/javascript">
60730  */
60731
60732 /**
60733  * @class Roo.grid.AbstractSelectionModel
60734  * @extends Roo.util.Observable
60735  * @abstract
60736  * Abstract base class for grid SelectionModels.  It provides the interface that should be
60737  * implemented by descendant classes.  This class should not be directly instantiated.
60738  * @constructor
60739  */
60740 Roo.grid.AbstractSelectionModel = function(){
60741     this.locked = false;
60742     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
60743 };
60744
60745 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
60746     /** @ignore Called by the grid automatically. Do not call directly. */
60747     init : function(grid){
60748         this.grid = grid;
60749         this.initEvents();
60750     },
60751
60752     /**
60753      * Locks the selections.
60754      */
60755     lock : function(){
60756         this.locked = true;
60757     },
60758
60759     /**
60760      * Unlocks the selections.
60761      */
60762     unlock : function(){
60763         this.locked = false;
60764     },
60765
60766     /**
60767      * Returns true if the selections are locked.
60768      * @return {Boolean}
60769      */
60770     isLocked : function(){
60771         return this.locked;
60772     }
60773 });/*
60774  * Based on:
60775  * Ext JS Library 1.1.1
60776  * Copyright(c) 2006-2007, Ext JS, LLC.
60777  *
60778  * Originally Released Under LGPL - original licence link has changed is not relivant.
60779  *
60780  * Fork - LGPL
60781  * <script type="text/javascript">
60782  */
60783 /**
60784  * @extends Roo.grid.AbstractSelectionModel
60785  * @class Roo.grid.RowSelectionModel
60786  * The default SelectionModel used by {@link Roo.grid.Grid}.
60787  * It supports multiple selections and keyboard selection/navigation. 
60788  * @constructor
60789  * @param {Object} config
60790  */
60791 Roo.grid.RowSelectionModel = function(config){
60792     Roo.apply(this, config);
60793     this.selections = new Roo.util.MixedCollection(false, function(o){
60794         return o.id;
60795     });
60796
60797     this.last = false;
60798     this.lastActive = false;
60799
60800     this.addEvents({
60801         /**
60802         * @event selectionchange
60803         * Fires when the selection changes
60804         * @param {SelectionModel} this
60805         */
60806        "selectionchange" : true,
60807        /**
60808         * @event afterselectionchange
60809         * Fires after the selection changes (eg. by key press or clicking)
60810         * @param {SelectionModel} this
60811         */
60812        "afterselectionchange" : true,
60813        /**
60814         * @event beforerowselect
60815         * Fires when a row is selected being selected, return false to cancel.
60816         * @param {SelectionModel} this
60817         * @param {Number} rowIndex The selected index
60818         * @param {Boolean} keepExisting False if other selections will be cleared
60819         */
60820        "beforerowselect" : true,
60821        /**
60822         * @event rowselect
60823         * Fires when a row is selected.
60824         * @param {SelectionModel} this
60825         * @param {Number} rowIndex The selected index
60826         * @param {Roo.data.Record} r The record
60827         */
60828        "rowselect" : true,
60829        /**
60830         * @event rowdeselect
60831         * Fires when a row is deselected.
60832         * @param {SelectionModel} this
60833         * @param {Number} rowIndex The selected index
60834         */
60835         "rowdeselect" : true
60836     });
60837     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60838     this.locked = false;
60839 };
60840
60841 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60842     /**
60843      * @cfg {Boolean} singleSelect
60844      * True to allow selection of only one row at a time (defaults to false)
60845      */
60846     singleSelect : false,
60847
60848     // private
60849     initEvents : function(){
60850
60851         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60852             this.grid.on("mousedown", this.handleMouseDown, this);
60853         }else{ // allow click to work like normal
60854             this.grid.on("rowclick", this.handleDragableRowClick, this);
60855         }
60856         // bootstrap does not have a view..
60857         var view = this.grid.view ? this.grid.view : this.grid;
60858         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60859             "up" : function(e){
60860                 if(!e.shiftKey){
60861                     this.selectPrevious(e.shiftKey);
60862                 }else if(this.last !== false && this.lastActive !== false){
60863                     var last = this.last;
60864                     this.selectRange(this.last,  this.lastActive-1);
60865                     view.focusRow(this.lastActive);
60866                     if(last !== false){
60867                         this.last = last;
60868                     }
60869                 }else{
60870                     this.selectFirstRow();
60871                 }
60872                 this.fireEvent("afterselectionchange", this);
60873             },
60874             "down" : function(e){
60875                 if(!e.shiftKey){
60876                     this.selectNext(e.shiftKey);
60877                 }else if(this.last !== false && this.lastActive !== false){
60878                     var last = this.last;
60879                     this.selectRange(this.last,  this.lastActive+1);
60880                     view.focusRow(this.lastActive);
60881                     if(last !== false){
60882                         this.last = last;
60883                     }
60884                 }else{
60885                     this.selectFirstRow();
60886                 }
60887                 this.fireEvent("afterselectionchange", this);
60888             },
60889             scope: this
60890         });
60891
60892          
60893         view.on("refresh", this.onRefresh, this);
60894         view.on("rowupdated", this.onRowUpdated, this);
60895         view.on("rowremoved", this.onRemove, this);
60896     },
60897
60898     // private
60899     onRefresh : function(){
60900         var ds = this.grid.ds, i, v = this.grid.view;
60901         var s = this.selections;
60902         s.each(function(r){
60903             if((i = ds.indexOfId(r.id)) != -1){
60904                 v.onRowSelect(i);
60905                 s.add(ds.getAt(i)); // updating the selection relate data
60906             }else{
60907                 s.remove(r);
60908             }
60909         });
60910     },
60911
60912     // private
60913     onRemove : function(v, index, r){
60914         this.selections.remove(r);
60915     },
60916
60917     // private
60918     onRowUpdated : function(v, index, r){
60919         if(this.isSelected(r)){
60920             v.onRowSelect(index);
60921         }
60922     },
60923
60924     /**
60925      * Select records.
60926      * @param {Array} records The records to select
60927      * @param {Boolean} keepExisting (optional) True to keep existing selections
60928      */
60929     selectRecords : function(records, keepExisting){
60930         if(!keepExisting){
60931             this.clearSelections();
60932         }
60933         var ds = this.grid.ds;
60934         for(var i = 0, len = records.length; i < len; i++){
60935             this.selectRow(ds.indexOf(records[i]), true);
60936         }
60937     },
60938
60939     /**
60940      * Gets the number of selected rows.
60941      * @return {Number}
60942      */
60943     getCount : function(){
60944         return this.selections.length;
60945     },
60946
60947     /**
60948      * Selects the first row in the grid.
60949      */
60950     selectFirstRow : function(){
60951         this.selectRow(0);
60952     },
60953
60954     /**
60955      * Select the last row.
60956      * @param {Boolean} keepExisting (optional) True to keep existing selections
60957      */
60958     selectLastRow : function(keepExisting){
60959         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60960     },
60961
60962     /**
60963      * Selects the row immediately following the last selected row.
60964      * @param {Boolean} keepExisting (optional) True to keep existing selections
60965      */
60966     selectNext : function(keepExisting){
60967         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60968             this.selectRow(this.last+1, keepExisting);
60969             var view = this.grid.view ? this.grid.view : this.grid;
60970             view.focusRow(this.last);
60971         }
60972     },
60973
60974     /**
60975      * Selects the row that precedes the last selected row.
60976      * @param {Boolean} keepExisting (optional) True to keep existing selections
60977      */
60978     selectPrevious : function(keepExisting){
60979         if(this.last){
60980             this.selectRow(this.last-1, keepExisting);
60981             var view = this.grid.view ? this.grid.view : this.grid;
60982             view.focusRow(this.last);
60983         }
60984     },
60985
60986     /**
60987      * Returns the selected records
60988      * @return {Array} Array of selected records
60989      */
60990     getSelections : function(){
60991         return [].concat(this.selections.items);
60992     },
60993
60994     /**
60995      * Returns the first selected record.
60996      * @return {Record}
60997      */
60998     getSelected : function(){
60999         return this.selections.itemAt(0);
61000     },
61001
61002
61003     /**
61004      * Clears all selections.
61005      */
61006     clearSelections : function(fast){
61007         if(this.locked) {
61008             return;
61009         }
61010         if(fast !== true){
61011             var ds = this.grid.ds;
61012             var s = this.selections;
61013             s.each(function(r){
61014                 this.deselectRow(ds.indexOfId(r.id));
61015             }, this);
61016             s.clear();
61017         }else{
61018             this.selections.clear();
61019         }
61020         this.last = false;
61021     },
61022
61023
61024     /**
61025      * Selects all rows.
61026      */
61027     selectAll : function(){
61028         if(this.locked) {
61029             return;
61030         }
61031         this.selections.clear();
61032         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61033             this.selectRow(i, true);
61034         }
61035     },
61036
61037     /**
61038      * Returns True if there is a selection.
61039      * @return {Boolean}
61040      */
61041     hasSelection : function(){
61042         return this.selections.length > 0;
61043     },
61044
61045     /**
61046      * Returns True if the specified row is selected.
61047      * @param {Number/Record} record The record or index of the record to check
61048      * @return {Boolean}
61049      */
61050     isSelected : function(index){
61051         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61052         return (r && this.selections.key(r.id) ? true : false);
61053     },
61054
61055     /**
61056      * Returns True if the specified record id is selected.
61057      * @param {String} id The id of record to check
61058      * @return {Boolean}
61059      */
61060     isIdSelected : function(id){
61061         return (this.selections.key(id) ? true : false);
61062     },
61063
61064     // private
61065     handleMouseDown : function(e, t)
61066     {
61067         var view = this.grid.view ? this.grid.view : this.grid;
61068         var rowIndex;
61069         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61070             return;
61071         };
61072         if(e.shiftKey && this.last !== false){
61073             var last = this.last;
61074             this.selectRange(last, rowIndex, e.ctrlKey);
61075             this.last = last; // reset the last
61076             view.focusRow(rowIndex);
61077         }else{
61078             var isSelected = this.isSelected(rowIndex);
61079             if(e.button !== 0 && isSelected){
61080                 view.focusRow(rowIndex);
61081             }else if(e.ctrlKey && isSelected){
61082                 this.deselectRow(rowIndex);
61083             }else if(!isSelected){
61084                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61085                 view.focusRow(rowIndex);
61086             }
61087         }
61088         this.fireEvent("afterselectionchange", this);
61089     },
61090     // private
61091     handleDragableRowClick :  function(grid, rowIndex, e) 
61092     {
61093         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61094             this.selectRow(rowIndex, false);
61095             var view = this.grid.view ? this.grid.view : this.grid;
61096             view.focusRow(rowIndex);
61097              this.fireEvent("afterselectionchange", this);
61098         }
61099     },
61100     
61101     /**
61102      * Selects multiple rows.
61103      * @param {Array} rows Array of the indexes of the row to select
61104      * @param {Boolean} keepExisting (optional) True to keep existing selections
61105      */
61106     selectRows : function(rows, keepExisting){
61107         if(!keepExisting){
61108             this.clearSelections();
61109         }
61110         for(var i = 0, len = rows.length; i < len; i++){
61111             this.selectRow(rows[i], true);
61112         }
61113     },
61114
61115     /**
61116      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61117      * @param {Number} startRow The index of the first row in the range
61118      * @param {Number} endRow The index of the last row in the range
61119      * @param {Boolean} keepExisting (optional) True to retain existing selections
61120      */
61121     selectRange : function(startRow, endRow, keepExisting){
61122         if(this.locked) {
61123             return;
61124         }
61125         if(!keepExisting){
61126             this.clearSelections();
61127         }
61128         if(startRow <= endRow){
61129             for(var i = startRow; i <= endRow; i++){
61130                 this.selectRow(i, true);
61131             }
61132         }else{
61133             for(var i = startRow; i >= endRow; i--){
61134                 this.selectRow(i, true);
61135             }
61136         }
61137     },
61138
61139     /**
61140      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61141      * @param {Number} startRow The index of the first row in the range
61142      * @param {Number} endRow The index of the last row in the range
61143      */
61144     deselectRange : function(startRow, endRow, preventViewNotify){
61145         if(this.locked) {
61146             return;
61147         }
61148         for(var i = startRow; i <= endRow; i++){
61149             this.deselectRow(i, preventViewNotify);
61150         }
61151     },
61152
61153     /**
61154      * Selects a row.
61155      * @param {Number} row The index of the row to select
61156      * @param {Boolean} keepExisting (optional) True to keep existing selections
61157      */
61158     selectRow : function(index, keepExisting, preventViewNotify){
61159         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61160             return;
61161         }
61162         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61163             if(!keepExisting || this.singleSelect){
61164                 this.clearSelections();
61165             }
61166             var r = this.grid.ds.getAt(index);
61167             this.selections.add(r);
61168             this.last = this.lastActive = index;
61169             if(!preventViewNotify){
61170                 var view = this.grid.view ? this.grid.view : this.grid;
61171                 view.onRowSelect(index);
61172             }
61173             this.fireEvent("rowselect", this, index, r);
61174             this.fireEvent("selectionchange", this);
61175         }
61176     },
61177
61178     /**
61179      * Deselects a row.
61180      * @param {Number} row The index of the row to deselect
61181      */
61182     deselectRow : function(index, preventViewNotify){
61183         if(this.locked) {
61184             return;
61185         }
61186         if(this.last == index){
61187             this.last = false;
61188         }
61189         if(this.lastActive == index){
61190             this.lastActive = false;
61191         }
61192         var r = this.grid.ds.getAt(index);
61193         this.selections.remove(r);
61194         if(!preventViewNotify){
61195             var view = this.grid.view ? this.grid.view : this.grid;
61196             view.onRowDeselect(index);
61197         }
61198         this.fireEvent("rowdeselect", this, index);
61199         this.fireEvent("selectionchange", this);
61200     },
61201
61202     // private
61203     restoreLast : function(){
61204         if(this._last){
61205             this.last = this._last;
61206         }
61207     },
61208
61209     // private
61210     acceptsNav : function(row, col, cm){
61211         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61212     },
61213
61214     // private
61215     onEditorKey : function(field, e){
61216         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61217         if(k == e.TAB){
61218             e.stopEvent();
61219             ed.completeEdit();
61220             if(e.shiftKey){
61221                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61222             }else{
61223                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61224             }
61225         }else if(k == e.ENTER && !e.ctrlKey){
61226             e.stopEvent();
61227             ed.completeEdit();
61228             if(e.shiftKey){
61229                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61230             }else{
61231                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61232             }
61233         }else if(k == e.ESC){
61234             ed.cancelEdit();
61235         }
61236         if(newCell){
61237             g.startEditing(newCell[0], newCell[1]);
61238         }
61239     }
61240 });/*
61241  * Based on:
61242  * Ext JS Library 1.1.1
61243  * Copyright(c) 2006-2007, Ext JS, LLC.
61244  *
61245  * Originally Released Under LGPL - original licence link has changed is not relivant.
61246  *
61247  * Fork - LGPL
61248  * <script type="text/javascript">
61249  */
61250 /**
61251  * @class Roo.grid.CellSelectionModel
61252  * @extends Roo.grid.AbstractSelectionModel
61253  * This class provides the basic implementation for cell selection in a grid.
61254  * @constructor
61255  * @param {Object} config The object containing the configuration of this model.
61256  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61257  */
61258 Roo.grid.CellSelectionModel = function(config){
61259     Roo.apply(this, config);
61260
61261     this.selection = null;
61262
61263     this.addEvents({
61264         /**
61265              * @event beforerowselect
61266              * Fires before a cell is selected.
61267              * @param {SelectionModel} this
61268              * @param {Number} rowIndex The selected row index
61269              * @param {Number} colIndex The selected cell index
61270              */
61271             "beforecellselect" : true,
61272         /**
61273              * @event cellselect
61274              * Fires when a cell is selected.
61275              * @param {SelectionModel} this
61276              * @param {Number} rowIndex The selected row index
61277              * @param {Number} colIndex The selected cell index
61278              */
61279             "cellselect" : true,
61280         /**
61281              * @event selectionchange
61282              * Fires when the active selection changes.
61283              * @param {SelectionModel} this
61284              * @param {Object} selection null for no selection or an object (o) with two properties
61285                 <ul>
61286                 <li>o.record: the record object for the row the selection is in</li>
61287                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61288                 </ul>
61289              */
61290             "selectionchange" : true,
61291         /**
61292              * @event tabend
61293              * Fires when the tab (or enter) was pressed on the last editable cell
61294              * You can use this to trigger add new row.
61295              * @param {SelectionModel} this
61296              */
61297             "tabend" : true,
61298          /**
61299              * @event beforeeditnext
61300              * Fires before the next editable sell is made active
61301              * You can use this to skip to another cell or fire the tabend
61302              *    if you set cell to false
61303              * @param {Object} eventdata object : { cell : [ row, col ] } 
61304              */
61305             "beforeeditnext" : true
61306     });
61307     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61308 };
61309
61310 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61311     
61312     enter_is_tab: false,
61313
61314     /** @ignore */
61315     initEvents : function(){
61316         this.grid.on("mousedown", this.handleMouseDown, this);
61317         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61318         var view = this.grid.view;
61319         view.on("refresh", this.onViewChange, this);
61320         view.on("rowupdated", this.onRowUpdated, this);
61321         view.on("beforerowremoved", this.clearSelections, this);
61322         view.on("beforerowsinserted", this.clearSelections, this);
61323         if(this.grid.isEditor){
61324             this.grid.on("beforeedit", this.beforeEdit,  this);
61325         }
61326     },
61327
61328         //private
61329     beforeEdit : function(e){
61330         this.select(e.row, e.column, false, true, e.record);
61331     },
61332
61333         //private
61334     onRowUpdated : function(v, index, r){
61335         if(this.selection && this.selection.record == r){
61336             v.onCellSelect(index, this.selection.cell[1]);
61337         }
61338     },
61339
61340         //private
61341     onViewChange : function(){
61342         this.clearSelections(true);
61343     },
61344
61345         /**
61346          * Returns the currently selected cell,.
61347          * @return {Array} The selected cell (row, column) or null if none selected.
61348          */
61349     getSelectedCell : function(){
61350         return this.selection ? this.selection.cell : null;
61351     },
61352
61353     /**
61354      * Clears all selections.
61355      * @param {Boolean} true to prevent the gridview from being notified about the change.
61356      */
61357     clearSelections : function(preventNotify){
61358         var s = this.selection;
61359         if(s){
61360             if(preventNotify !== true){
61361                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61362             }
61363             this.selection = null;
61364             this.fireEvent("selectionchange", this, null);
61365         }
61366     },
61367
61368     /**
61369      * Returns true if there is a selection.
61370      * @return {Boolean}
61371      */
61372     hasSelection : function(){
61373         return this.selection ? true : false;
61374     },
61375
61376     /** @ignore */
61377     handleMouseDown : function(e, t){
61378         var v = this.grid.getView();
61379         if(this.isLocked()){
61380             return;
61381         };
61382         var row = v.findRowIndex(t);
61383         var cell = v.findCellIndex(t);
61384         if(row !== false && cell !== false){
61385             this.select(row, cell);
61386         }
61387     },
61388
61389     /**
61390      * Selects a cell.
61391      * @param {Number} rowIndex
61392      * @param {Number} collIndex
61393      */
61394     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61395         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61396             this.clearSelections();
61397             r = r || this.grid.dataSource.getAt(rowIndex);
61398             this.selection = {
61399                 record : r,
61400                 cell : [rowIndex, colIndex]
61401             };
61402             if(!preventViewNotify){
61403                 var v = this.grid.getView();
61404                 v.onCellSelect(rowIndex, colIndex);
61405                 if(preventFocus !== true){
61406                     v.focusCell(rowIndex, colIndex);
61407                 }
61408             }
61409             this.fireEvent("cellselect", this, rowIndex, colIndex);
61410             this.fireEvent("selectionchange", this, this.selection);
61411         }
61412     },
61413
61414         //private
61415     isSelectable : function(rowIndex, colIndex, cm){
61416         return !cm.isHidden(colIndex);
61417     },
61418
61419     /** @ignore */
61420     handleKeyDown : function(e){
61421         //Roo.log('Cell Sel Model handleKeyDown');
61422         if(!e.isNavKeyPress()){
61423             return;
61424         }
61425         var g = this.grid, s = this.selection;
61426         if(!s){
61427             e.stopEvent();
61428             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
61429             if(cell){
61430                 this.select(cell[0], cell[1]);
61431             }
61432             return;
61433         }
61434         var sm = this;
61435         var walk = function(row, col, step){
61436             return g.walkCells(row, col, step, sm.isSelectable,  sm);
61437         };
61438         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
61439         var newCell;
61440
61441       
61442
61443         switch(k){
61444             case e.TAB:
61445                 // handled by onEditorKey
61446                 if (g.isEditor && g.editing) {
61447                     return;
61448                 }
61449                 if(e.shiftKey) {
61450                     newCell = walk(r, c-1, -1);
61451                 } else {
61452                     newCell = walk(r, c+1, 1);
61453                 }
61454                 break;
61455             
61456             case e.DOWN:
61457                newCell = walk(r+1, c, 1);
61458                 break;
61459             
61460             case e.UP:
61461                 newCell = walk(r-1, c, -1);
61462                 break;
61463             
61464             case e.RIGHT:
61465                 newCell = walk(r, c+1, 1);
61466                 break;
61467             
61468             case e.LEFT:
61469                 newCell = walk(r, c-1, -1);
61470                 break;
61471             
61472             case e.ENTER:
61473                 
61474                 if(g.isEditor && !g.editing){
61475                    g.startEditing(r, c);
61476                    e.stopEvent();
61477                    return;
61478                 }
61479                 
61480                 
61481              break;
61482         };
61483         if(newCell){
61484             this.select(newCell[0], newCell[1]);
61485             e.stopEvent();
61486             
61487         }
61488     },
61489
61490     acceptsNav : function(row, col, cm){
61491         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61492     },
61493     /**
61494      * Selects a cell.
61495      * @param {Number} field (not used) - as it's normally used as a listener
61496      * @param {Number} e - event - fake it by using
61497      *
61498      * var e = Roo.EventObjectImpl.prototype;
61499      * e.keyCode = e.TAB
61500      *
61501      * 
61502      */
61503     onEditorKey : function(field, e){
61504         
61505         var k = e.getKey(),
61506             newCell,
61507             g = this.grid,
61508             ed = g.activeEditor,
61509             forward = false;
61510         ///Roo.log('onEditorKey' + k);
61511         
61512         
61513         if (this.enter_is_tab && k == e.ENTER) {
61514             k = e.TAB;
61515         }
61516         
61517         if(k == e.TAB){
61518             if(e.shiftKey){
61519                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61520             }else{
61521                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61522                 forward = true;
61523             }
61524             
61525             e.stopEvent();
61526             
61527         } else if(k == e.ENTER &&  !e.ctrlKey){
61528             ed.completeEdit();
61529             e.stopEvent();
61530             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61531         
61532                 } else if(k == e.ESC){
61533             ed.cancelEdit();
61534         }
61535                 
61536         if (newCell) {
61537             var ecall = { cell : newCell, forward : forward };
61538             this.fireEvent('beforeeditnext', ecall );
61539             newCell = ecall.cell;
61540                         forward = ecall.forward;
61541         }
61542                 
61543         if(newCell){
61544             //Roo.log('next cell after edit');
61545             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
61546         } else if (forward) {
61547             // tabbed past last
61548             this.fireEvent.defer(100, this, ['tabend',this]);
61549         }
61550     }
61551 });/*
61552  * Based on:
61553  * Ext JS Library 1.1.1
61554  * Copyright(c) 2006-2007, Ext JS, LLC.
61555  *
61556  * Originally Released Under LGPL - original licence link has changed is not relivant.
61557  *
61558  * Fork - LGPL
61559  * <script type="text/javascript">
61560  */
61561  
61562 /**
61563  * @class Roo.grid.EditorGrid
61564  * @extends Roo.grid.Grid
61565  * Class for creating and editable grid.
61566  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
61567  * The container MUST have some type of size defined for the grid to fill. The container will be 
61568  * automatically set to position relative if it isn't already.
61569  * @param {Object} dataSource The data model to bind to
61570  * @param {Object} colModel The column model with info about this grid's columns
61571  */
61572 Roo.grid.EditorGrid = function(container, config){
61573     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
61574     this.getGridEl().addClass("xedit-grid");
61575
61576     if(!this.selModel){
61577         this.selModel = new Roo.grid.CellSelectionModel();
61578     }
61579
61580     this.activeEditor = null;
61581
61582         this.addEvents({
61583             /**
61584              * @event beforeedit
61585              * Fires before cell editing is triggered. The edit event object has the following properties <br />
61586              * <ul style="padding:5px;padding-left:16px;">
61587              * <li>grid - This grid</li>
61588              * <li>record - The record being edited</li>
61589              * <li>field - The field name being edited</li>
61590              * <li>value - The value for the field being edited.</li>
61591              * <li>row - The grid row index</li>
61592              * <li>column - The grid column index</li>
61593              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61594              * </ul>
61595              * @param {Object} e An edit event (see above for description)
61596              */
61597             "beforeedit" : true,
61598             /**
61599              * @event afteredit
61600              * Fires after a cell is edited. <br />
61601              * <ul style="padding:5px;padding-left:16px;">
61602              * <li>grid - This grid</li>
61603              * <li>record - The record being edited</li>
61604              * <li>field - The field name being edited</li>
61605              * <li>value - The value being set</li>
61606              * <li>originalValue - The original value for the field, before the edit.</li>
61607              * <li>row - The grid row index</li>
61608              * <li>column - The grid column index</li>
61609              * </ul>
61610              * @param {Object} e An edit event (see above for description)
61611              */
61612             "afteredit" : true,
61613             /**
61614              * @event validateedit
61615              * Fires after a cell is edited, but before the value is set in the record. 
61616          * You can use this to modify the value being set in the field, Return false
61617              * to cancel the change. The edit event object has the following properties <br />
61618              * <ul style="padding:5px;padding-left:16px;">
61619          * <li>editor - This editor</li>
61620              * <li>grid - This grid</li>
61621              * <li>record - The record being edited</li>
61622              * <li>field - The field name being edited</li>
61623              * <li>value - The value being set</li>
61624              * <li>originalValue - The original value for the field, before the edit.</li>
61625              * <li>row - The grid row index</li>
61626              * <li>column - The grid column index</li>
61627              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61628              * </ul>
61629              * @param {Object} e An edit event (see above for description)
61630              */
61631             "validateedit" : true
61632         });
61633     this.on("bodyscroll", this.stopEditing,  this);
61634     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
61635 };
61636
61637 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
61638     /**
61639      * @cfg {Number} clicksToEdit
61640      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
61641      */
61642     clicksToEdit: 2,
61643
61644     // private
61645     isEditor : true,
61646     // private
61647     trackMouseOver: false, // causes very odd FF errors
61648
61649     onCellDblClick : function(g, row, col){
61650         this.startEditing(row, col);
61651     },
61652
61653     onEditComplete : function(ed, value, startValue){
61654         this.editing = false;
61655         this.activeEditor = null;
61656         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
61657         var r = ed.record;
61658         var field = this.colModel.getDataIndex(ed.col);
61659         var e = {
61660             grid: this,
61661             record: r,
61662             field: field,
61663             originalValue: startValue,
61664             value: value,
61665             row: ed.row,
61666             column: ed.col,
61667             cancel:false,
61668             editor: ed
61669         };
61670         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
61671         cell.show();
61672           
61673         if(String(value) !== String(startValue)){
61674             
61675             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
61676                 r.set(field, e.value);
61677                 // if we are dealing with a combo box..
61678                 // then we also set the 'name' colum to be the displayField
61679                 if (ed.field.displayField && ed.field.name) {
61680                     r.set(ed.field.name, ed.field.el.dom.value);
61681                 }
61682                 
61683                 delete e.cancel; //?? why!!!
61684                 this.fireEvent("afteredit", e);
61685             }
61686         } else {
61687             this.fireEvent("afteredit", e); // always fire it!
61688         }
61689         this.view.focusCell(ed.row, ed.col);
61690     },
61691
61692     /**
61693      * Starts editing the specified for the specified row/column
61694      * @param {Number} rowIndex
61695      * @param {Number} colIndex
61696      */
61697     startEditing : function(row, col){
61698         this.stopEditing();
61699         if(this.colModel.isCellEditable(col, row)){
61700             this.view.ensureVisible(row, col, true);
61701           
61702             var r = this.dataSource.getAt(row);
61703             var field = this.colModel.getDataIndex(col);
61704             var cell = Roo.get(this.view.getCell(row,col));
61705             var e = {
61706                 grid: this,
61707                 record: r,
61708                 field: field,
61709                 value: r.data[field],
61710                 row: row,
61711                 column: col,
61712                 cancel:false 
61713             };
61714             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
61715                 this.editing = true;
61716                 var ed = this.colModel.getCellEditor(col, row);
61717                 
61718                 if (!ed) {
61719                     return;
61720                 }
61721                 if(!ed.rendered){
61722                     ed.render(ed.parentEl || document.body);
61723                 }
61724                 ed.field.reset();
61725                
61726                 cell.hide();
61727                 
61728                 (function(){ // complex but required for focus issues in safari, ie and opera
61729                     ed.row = row;
61730                     ed.col = col;
61731                     ed.record = r;
61732                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
61733                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
61734                     this.activeEditor = ed;
61735                     var v = r.data[field];
61736                     ed.startEdit(this.view.getCell(row, col), v);
61737                     // combo's with 'displayField and name set
61738                     if (ed.field.displayField && ed.field.name) {
61739                         ed.field.el.dom.value = r.data[ed.field.name];
61740                     }
61741                     
61742                     
61743                 }).defer(50, this);
61744             }
61745         }
61746     },
61747         
61748     /**
61749      * Stops any active editing
61750      */
61751     stopEditing : function(){
61752         if(this.activeEditor){
61753             this.activeEditor.completeEdit();
61754         }
61755         this.activeEditor = null;
61756     },
61757         
61758          /**
61759      * Called to get grid's drag proxy text, by default returns this.ddText.
61760      * @return {String}
61761      */
61762     getDragDropText : function(){
61763         var count = this.selModel.getSelectedCell() ? 1 : 0;
61764         return String.format(this.ddText, count, count == 1 ? '' : 's');
61765     }
61766         
61767 });/*
61768  * Based on:
61769  * Ext JS Library 1.1.1
61770  * Copyright(c) 2006-2007, Ext JS, LLC.
61771  *
61772  * Originally Released Under LGPL - original licence link has changed is not relivant.
61773  *
61774  * Fork - LGPL
61775  * <script type="text/javascript">
61776  */
61777
61778 // private - not really -- you end up using it !
61779 // This is a support class used internally by the Grid components
61780
61781 /**
61782  * @class Roo.grid.GridEditor
61783  * @extends Roo.Editor
61784  * Class for creating and editable grid elements.
61785  * @param {Object} config any settings (must include field)
61786  */
61787 Roo.grid.GridEditor = function(field, config){
61788     if (!config && field.field) {
61789         config = field;
61790         field = Roo.factory(config.field, Roo.form);
61791     }
61792     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61793     field.monitorTab = false;
61794 };
61795
61796 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61797     
61798     /**
61799      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61800      */
61801     
61802     alignment: "tl-tl",
61803     autoSize: "width",
61804     hideEl : false,
61805     cls: "x-small-editor x-grid-editor",
61806     shim:false,
61807     shadow:"frame"
61808 });/*
61809  * Based on:
61810  * Ext JS Library 1.1.1
61811  * Copyright(c) 2006-2007, Ext JS, LLC.
61812  *
61813  * Originally Released Under LGPL - original licence link has changed is not relivant.
61814  *
61815  * Fork - LGPL
61816  * <script type="text/javascript">
61817  */
61818   
61819
61820   
61821 Roo.grid.PropertyRecord = Roo.data.Record.create([
61822     {name:'name',type:'string'},  'value'
61823 ]);
61824
61825
61826 Roo.grid.PropertyStore = function(grid, source){
61827     this.grid = grid;
61828     this.store = new Roo.data.Store({
61829         recordType : Roo.grid.PropertyRecord
61830     });
61831     this.store.on('update', this.onUpdate,  this);
61832     if(source){
61833         this.setSource(source);
61834     }
61835     Roo.grid.PropertyStore.superclass.constructor.call(this);
61836 };
61837
61838
61839
61840 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61841     setSource : function(o){
61842         this.source = o;
61843         this.store.removeAll();
61844         var data = [];
61845         for(var k in o){
61846             if(this.isEditableValue(o[k])){
61847                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61848             }
61849         }
61850         this.store.loadRecords({records: data}, {}, true);
61851     },
61852
61853     onUpdate : function(ds, record, type){
61854         if(type == Roo.data.Record.EDIT){
61855             var v = record.data['value'];
61856             var oldValue = record.modified['value'];
61857             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61858                 this.source[record.id] = v;
61859                 record.commit();
61860                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61861             }else{
61862                 record.reject();
61863             }
61864         }
61865     },
61866
61867     getProperty : function(row){
61868        return this.store.getAt(row);
61869     },
61870
61871     isEditableValue: function(val){
61872         if(val && val instanceof Date){
61873             return true;
61874         }else if(typeof val == 'object' || typeof val == 'function'){
61875             return false;
61876         }
61877         return true;
61878     },
61879
61880     setValue : function(prop, value){
61881         this.source[prop] = value;
61882         this.store.getById(prop).set('value', value);
61883     },
61884
61885     getSource : function(){
61886         return this.source;
61887     }
61888 });
61889
61890 Roo.grid.PropertyColumnModel = function(grid, store){
61891     this.grid = grid;
61892     var g = Roo.grid;
61893     g.PropertyColumnModel.superclass.constructor.call(this, [
61894         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61895         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61896     ]);
61897     this.store = store;
61898     this.bselect = Roo.DomHelper.append(document.body, {
61899         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61900             {tag: 'option', value: 'true', html: 'true'},
61901             {tag: 'option', value: 'false', html: 'false'}
61902         ]
61903     });
61904     Roo.id(this.bselect);
61905     var f = Roo.form;
61906     this.editors = {
61907         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61908         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61909         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61910         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61911         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61912     };
61913     this.renderCellDelegate = this.renderCell.createDelegate(this);
61914     this.renderPropDelegate = this.renderProp.createDelegate(this);
61915 };
61916
61917 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61918     
61919     
61920     nameText : 'Name',
61921     valueText : 'Value',
61922     
61923     dateFormat : 'm/j/Y',
61924     
61925     
61926     renderDate : function(dateVal){
61927         return dateVal.dateFormat(this.dateFormat);
61928     },
61929
61930     renderBool : function(bVal){
61931         return bVal ? 'true' : 'false';
61932     },
61933
61934     isCellEditable : function(colIndex, rowIndex){
61935         return colIndex == 1;
61936     },
61937
61938     getRenderer : function(col){
61939         return col == 1 ?
61940             this.renderCellDelegate : this.renderPropDelegate;
61941     },
61942
61943     renderProp : function(v){
61944         return this.getPropertyName(v);
61945     },
61946
61947     renderCell : function(val){
61948         var rv = val;
61949         if(val instanceof Date){
61950             rv = this.renderDate(val);
61951         }else if(typeof val == 'boolean'){
61952             rv = this.renderBool(val);
61953         }
61954         return Roo.util.Format.htmlEncode(rv);
61955     },
61956
61957     getPropertyName : function(name){
61958         var pn = this.grid.propertyNames;
61959         return pn && pn[name] ? pn[name] : name;
61960     },
61961
61962     getCellEditor : function(colIndex, rowIndex){
61963         var p = this.store.getProperty(rowIndex);
61964         var n = p.data['name'], val = p.data['value'];
61965         
61966         if(typeof(this.grid.customEditors[n]) == 'string'){
61967             return this.editors[this.grid.customEditors[n]];
61968         }
61969         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61970             return this.grid.customEditors[n];
61971         }
61972         if(val instanceof Date){
61973             return this.editors['date'];
61974         }else if(typeof val == 'number'){
61975             return this.editors['number'];
61976         }else if(typeof val == 'boolean'){
61977             return this.editors['boolean'];
61978         }else{
61979             return this.editors['string'];
61980         }
61981     }
61982 });
61983
61984 /**
61985  * @class Roo.grid.PropertyGrid
61986  * @extends Roo.grid.EditorGrid
61987  * This class represents the  interface of a component based property grid control.
61988  * <br><br>Usage:<pre><code>
61989  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61990       
61991  });
61992  // set any options
61993  grid.render();
61994  * </code></pre>
61995   
61996  * @constructor
61997  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61998  * The container MUST have some type of size defined for the grid to fill. The container will be
61999  * automatically set to position relative if it isn't already.
62000  * @param {Object} config A config object that sets properties on this grid.
62001  */
62002 Roo.grid.PropertyGrid = function(container, config){
62003     config = config || {};
62004     var store = new Roo.grid.PropertyStore(this);
62005     this.store = store;
62006     var cm = new Roo.grid.PropertyColumnModel(this, store);
62007     store.store.sort('name', 'ASC');
62008     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62009         ds: store.store,
62010         cm: cm,
62011         enableColLock:false,
62012         enableColumnMove:false,
62013         stripeRows:false,
62014         trackMouseOver: false,
62015         clicksToEdit:1
62016     }, config));
62017     this.getGridEl().addClass('x-props-grid');
62018     this.lastEditRow = null;
62019     this.on('columnresize', this.onColumnResize, this);
62020     this.addEvents({
62021          /**
62022              * @event beforepropertychange
62023              * Fires before a property changes (return false to stop?)
62024              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62025              * @param {String} id Record Id
62026              * @param {String} newval New Value
62027          * @param {String} oldval Old Value
62028              */
62029         "beforepropertychange": true,
62030         /**
62031              * @event propertychange
62032              * Fires after a property changes
62033              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62034              * @param {String} id Record Id
62035              * @param {String} newval New Value
62036          * @param {String} oldval Old Value
62037              */
62038         "propertychange": true
62039     });
62040     this.customEditors = this.customEditors || {};
62041 };
62042 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62043     
62044      /**
62045      * @cfg {Object} customEditors map of colnames=> custom editors.
62046      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62047      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62048      * false disables editing of the field.
62049          */
62050     
62051       /**
62052      * @cfg {Object} propertyNames map of property Names to their displayed value
62053          */
62054     
62055     render : function(){
62056         Roo.grid.PropertyGrid.superclass.render.call(this);
62057         this.autoSize.defer(100, this);
62058     },
62059
62060     autoSize : function(){
62061         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62062         if(this.view){
62063             this.view.fitColumns();
62064         }
62065     },
62066
62067     onColumnResize : function(){
62068         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62069         this.autoSize();
62070     },
62071     /**
62072      * Sets the data for the Grid
62073      * accepts a Key => Value object of all the elements avaiable.
62074      * @param {Object} data  to appear in grid.
62075      */
62076     setSource : function(source){
62077         this.store.setSource(source);
62078         //this.autoSize();
62079     },
62080     /**
62081      * Gets all the data from the grid.
62082      * @return {Object} data  data stored in grid
62083      */
62084     getSource : function(){
62085         return this.store.getSource();
62086     }
62087 });/*
62088   
62089  * Licence LGPL
62090  
62091  */
62092  
62093 /**
62094  * @class Roo.grid.Calendar
62095  * @extends Roo.grid.Grid
62096  * This class extends the Grid to provide a calendar widget
62097  * <br><br>Usage:<pre><code>
62098  var grid = new Roo.grid.Calendar("my-container-id", {
62099      ds: myDataStore,
62100      cm: myColModel,
62101      selModel: mySelectionModel,
62102      autoSizeColumns: true,
62103      monitorWindowResize: false,
62104      trackMouseOver: true
62105      eventstore : real data store..
62106  });
62107  // set any options
62108  grid.render();
62109   
62110   * @constructor
62111  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62112  * The container MUST have some type of size defined for the grid to fill. The container will be
62113  * automatically set to position relative if it isn't already.
62114  * @param {Object} config A config object that sets properties on this grid.
62115  */
62116 Roo.grid.Calendar = function(container, config){
62117         // initialize the container
62118         this.container = Roo.get(container);
62119         this.container.update("");
62120         this.container.setStyle("overflow", "hidden");
62121     this.container.addClass('x-grid-container');
62122
62123     this.id = this.container.id;
62124
62125     Roo.apply(this, config);
62126     // check and correct shorthanded configs
62127     
62128     var rows = [];
62129     var d =1;
62130     for (var r = 0;r < 6;r++) {
62131         
62132         rows[r]=[];
62133         for (var c =0;c < 7;c++) {
62134             rows[r][c]= '';
62135         }
62136     }
62137     if (this.eventStore) {
62138         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62139         this.eventStore.on('load',this.onLoad, this);
62140         this.eventStore.on('beforeload',this.clearEvents, this);
62141          
62142     }
62143     
62144     this.dataSource = new Roo.data.Store({
62145             proxy: new Roo.data.MemoryProxy(rows),
62146             reader: new Roo.data.ArrayReader({}, [
62147                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62148     });
62149
62150     this.dataSource.load();
62151     this.ds = this.dataSource;
62152     this.ds.xmodule = this.xmodule || false;
62153     
62154     
62155     var cellRender = function(v,x,r)
62156     {
62157         return String.format(
62158             '<div class="fc-day  fc-widget-content"><div>' +
62159                 '<div class="fc-event-container"></div>' +
62160                 '<div class="fc-day-number">{0}</div>'+
62161                 
62162                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62163             '</div></div>', v);
62164     
62165     }
62166     
62167     
62168     this.colModel = new Roo.grid.ColumnModel( [
62169         {
62170             xtype: 'ColumnModel',
62171             xns: Roo.grid,
62172             dataIndex : 'weekday0',
62173             header : 'Sunday',
62174             renderer : cellRender
62175         },
62176         {
62177             xtype: 'ColumnModel',
62178             xns: Roo.grid,
62179             dataIndex : 'weekday1',
62180             header : 'Monday',
62181             renderer : cellRender
62182         },
62183         {
62184             xtype: 'ColumnModel',
62185             xns: Roo.grid,
62186             dataIndex : 'weekday2',
62187             header : 'Tuesday',
62188             renderer : cellRender
62189         },
62190         {
62191             xtype: 'ColumnModel',
62192             xns: Roo.grid,
62193             dataIndex : 'weekday3',
62194             header : 'Wednesday',
62195             renderer : cellRender
62196         },
62197         {
62198             xtype: 'ColumnModel',
62199             xns: Roo.grid,
62200             dataIndex : 'weekday4',
62201             header : 'Thursday',
62202             renderer : cellRender
62203         },
62204         {
62205             xtype: 'ColumnModel',
62206             xns: Roo.grid,
62207             dataIndex : 'weekday5',
62208             header : 'Friday',
62209             renderer : cellRender
62210         },
62211         {
62212             xtype: 'ColumnModel',
62213             xns: Roo.grid,
62214             dataIndex : 'weekday6',
62215             header : 'Saturday',
62216             renderer : cellRender
62217         }
62218     ]);
62219     this.cm = this.colModel;
62220     this.cm.xmodule = this.xmodule || false;
62221  
62222         
62223           
62224     //this.selModel = new Roo.grid.CellSelectionModel();
62225     //this.sm = this.selModel;
62226     //this.selModel.init(this);
62227     
62228     
62229     if(this.width){
62230         this.container.setWidth(this.width);
62231     }
62232
62233     if(this.height){
62234         this.container.setHeight(this.height);
62235     }
62236     /** @private */
62237         this.addEvents({
62238         // raw events
62239         /**
62240          * @event click
62241          * The raw click event for the entire grid.
62242          * @param {Roo.EventObject} e
62243          */
62244         "click" : true,
62245         /**
62246          * @event dblclick
62247          * The raw dblclick event for the entire grid.
62248          * @param {Roo.EventObject} e
62249          */
62250         "dblclick" : true,
62251         /**
62252          * @event contextmenu
62253          * The raw contextmenu event for the entire grid.
62254          * @param {Roo.EventObject} e
62255          */
62256         "contextmenu" : true,
62257         /**
62258          * @event mousedown
62259          * The raw mousedown event for the entire grid.
62260          * @param {Roo.EventObject} e
62261          */
62262         "mousedown" : true,
62263         /**
62264          * @event mouseup
62265          * The raw mouseup event for the entire grid.
62266          * @param {Roo.EventObject} e
62267          */
62268         "mouseup" : true,
62269         /**
62270          * @event mouseover
62271          * The raw mouseover event for the entire grid.
62272          * @param {Roo.EventObject} e
62273          */
62274         "mouseover" : true,
62275         /**
62276          * @event mouseout
62277          * The raw mouseout event for the entire grid.
62278          * @param {Roo.EventObject} e
62279          */
62280         "mouseout" : true,
62281         /**
62282          * @event keypress
62283          * The raw keypress event for the entire grid.
62284          * @param {Roo.EventObject} e
62285          */
62286         "keypress" : true,
62287         /**
62288          * @event keydown
62289          * The raw keydown event for the entire grid.
62290          * @param {Roo.EventObject} e
62291          */
62292         "keydown" : true,
62293
62294         // custom events
62295
62296         /**
62297          * @event cellclick
62298          * Fires when a cell is clicked
62299          * @param {Grid} this
62300          * @param {Number} rowIndex
62301          * @param {Number} columnIndex
62302          * @param {Roo.EventObject} e
62303          */
62304         "cellclick" : true,
62305         /**
62306          * @event celldblclick
62307          * Fires when a cell is double clicked
62308          * @param {Grid} this
62309          * @param {Number} rowIndex
62310          * @param {Number} columnIndex
62311          * @param {Roo.EventObject} e
62312          */
62313         "celldblclick" : true,
62314         /**
62315          * @event rowclick
62316          * Fires when a row is clicked
62317          * @param {Grid} this
62318          * @param {Number} rowIndex
62319          * @param {Roo.EventObject} e
62320          */
62321         "rowclick" : true,
62322         /**
62323          * @event rowdblclick
62324          * Fires when a row is double clicked
62325          * @param {Grid} this
62326          * @param {Number} rowIndex
62327          * @param {Roo.EventObject} e
62328          */
62329         "rowdblclick" : true,
62330         /**
62331          * @event headerclick
62332          * Fires when a header is clicked
62333          * @param {Grid} this
62334          * @param {Number} columnIndex
62335          * @param {Roo.EventObject} e
62336          */
62337         "headerclick" : true,
62338         /**
62339          * @event headerdblclick
62340          * Fires when a header cell is double clicked
62341          * @param {Grid} this
62342          * @param {Number} columnIndex
62343          * @param {Roo.EventObject} e
62344          */
62345         "headerdblclick" : true,
62346         /**
62347          * @event rowcontextmenu
62348          * Fires when a row is right clicked
62349          * @param {Grid} this
62350          * @param {Number} rowIndex
62351          * @param {Roo.EventObject} e
62352          */
62353         "rowcontextmenu" : true,
62354         /**
62355          * @event cellcontextmenu
62356          * Fires when a cell is right clicked
62357          * @param {Grid} this
62358          * @param {Number} rowIndex
62359          * @param {Number} cellIndex
62360          * @param {Roo.EventObject} e
62361          */
62362          "cellcontextmenu" : true,
62363         /**
62364          * @event headercontextmenu
62365          * Fires when a header is right clicked
62366          * @param {Grid} this
62367          * @param {Number} columnIndex
62368          * @param {Roo.EventObject} e
62369          */
62370         "headercontextmenu" : true,
62371         /**
62372          * @event bodyscroll
62373          * Fires when the body element is scrolled
62374          * @param {Number} scrollLeft
62375          * @param {Number} scrollTop
62376          */
62377         "bodyscroll" : true,
62378         /**
62379          * @event columnresize
62380          * Fires when the user resizes a column
62381          * @param {Number} columnIndex
62382          * @param {Number} newSize
62383          */
62384         "columnresize" : true,
62385         /**
62386          * @event columnmove
62387          * Fires when the user moves a column
62388          * @param {Number} oldIndex
62389          * @param {Number} newIndex
62390          */
62391         "columnmove" : true,
62392         /**
62393          * @event startdrag
62394          * Fires when row(s) start being dragged
62395          * @param {Grid} this
62396          * @param {Roo.GridDD} dd The drag drop object
62397          * @param {event} e The raw browser event
62398          */
62399         "startdrag" : true,
62400         /**
62401          * @event enddrag
62402          * Fires when a drag operation is complete
62403          * @param {Grid} this
62404          * @param {Roo.GridDD} dd The drag drop object
62405          * @param {event} e The raw browser event
62406          */
62407         "enddrag" : true,
62408         /**
62409          * @event dragdrop
62410          * Fires when dragged row(s) are dropped on a valid DD target
62411          * @param {Grid} this
62412          * @param {Roo.GridDD} dd The drag drop object
62413          * @param {String} targetId The target drag drop object
62414          * @param {event} e The raw browser event
62415          */
62416         "dragdrop" : true,
62417         /**
62418          * @event dragover
62419          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62420          * @param {Grid} this
62421          * @param {Roo.GridDD} dd The drag drop object
62422          * @param {String} targetId The target drag drop object
62423          * @param {event} e The raw browser event
62424          */
62425         "dragover" : true,
62426         /**
62427          * @event dragenter
62428          *  Fires when the dragged row(s) first cross another DD target while being dragged
62429          * @param {Grid} this
62430          * @param {Roo.GridDD} dd The drag drop object
62431          * @param {String} targetId The target drag drop object
62432          * @param {event} e The raw browser event
62433          */
62434         "dragenter" : true,
62435         /**
62436          * @event dragout
62437          * Fires when the dragged row(s) leave another DD target while being dragged
62438          * @param {Grid} this
62439          * @param {Roo.GridDD} dd The drag drop object
62440          * @param {String} targetId The target drag drop object
62441          * @param {event} e The raw browser event
62442          */
62443         "dragout" : true,
62444         /**
62445          * @event rowclass
62446          * Fires when a row is rendered, so you can change add a style to it.
62447          * @param {GridView} gridview   The grid view
62448          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
62449          */
62450         'rowclass' : true,
62451
62452         /**
62453          * @event render
62454          * Fires when the grid is rendered
62455          * @param {Grid} grid
62456          */
62457         'render' : true,
62458             /**
62459              * @event select
62460              * Fires when a date is selected
62461              * @param {DatePicker} this
62462              * @param {Date} date The selected date
62463              */
62464         'select': true,
62465         /**
62466              * @event monthchange
62467              * Fires when the displayed month changes 
62468              * @param {DatePicker} this
62469              * @param {Date} date The selected month
62470              */
62471         'monthchange': true,
62472         /**
62473              * @event evententer
62474              * Fires when mouse over an event
62475              * @param {Calendar} this
62476              * @param {event} Event
62477              */
62478         'evententer': true,
62479         /**
62480              * @event eventleave
62481              * Fires when the mouse leaves an
62482              * @param {Calendar} this
62483              * @param {event}
62484              */
62485         'eventleave': true,
62486         /**
62487              * @event eventclick
62488              * Fires when the mouse click an
62489              * @param {Calendar} this
62490              * @param {event}
62491              */
62492         'eventclick': true,
62493         /**
62494              * @event eventrender
62495              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
62496              * @param {Calendar} this
62497              * @param {data} data to be modified
62498              */
62499         'eventrender': true
62500         
62501     });
62502
62503     Roo.grid.Grid.superclass.constructor.call(this);
62504     this.on('render', function() {
62505         this.view.el.addClass('x-grid-cal'); 
62506         
62507         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
62508
62509     },this);
62510     
62511     if (!Roo.grid.Calendar.style) {
62512         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
62513             
62514             
62515             '.x-grid-cal .x-grid-col' :  {
62516                 height: 'auto !important',
62517                 'vertical-align': 'top'
62518             },
62519             '.x-grid-cal  .fc-event-hori' : {
62520                 height: '14px'
62521             }
62522              
62523             
62524         }, Roo.id());
62525     }
62526
62527     
62528     
62529 };
62530 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
62531     /**
62532      * @cfg {Store} eventStore The store that loads events.
62533      */
62534     eventStore : 25,
62535
62536      
62537     activeDate : false,
62538     startDay : 0,
62539     autoWidth : true,
62540     monitorWindowResize : false,
62541
62542     
62543     resizeColumns : function() {
62544         var col = (this.view.el.getWidth() / 7) - 3;
62545         // loop through cols, and setWidth
62546         for(var i =0 ; i < 7 ; i++){
62547             this.cm.setColumnWidth(i, col);
62548         }
62549     },
62550      setDate :function(date) {
62551         
62552         Roo.log('setDate?');
62553         
62554         this.resizeColumns();
62555         var vd = this.activeDate;
62556         this.activeDate = date;
62557 //        if(vd && this.el){
62558 //            var t = date.getTime();
62559 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
62560 //                Roo.log('using add remove');
62561 //                
62562 //                this.fireEvent('monthchange', this, date);
62563 //                
62564 //                this.cells.removeClass("fc-state-highlight");
62565 //                this.cells.each(function(c){
62566 //                   if(c.dateValue == t){
62567 //                       c.addClass("fc-state-highlight");
62568 //                       setTimeout(function(){
62569 //                            try{c.dom.firstChild.focus();}catch(e){}
62570 //                       }, 50);
62571 //                       return false;
62572 //                   }
62573 //                   return true;
62574 //                });
62575 //                return;
62576 //            }
62577 //        }
62578         
62579         var days = date.getDaysInMonth();
62580         
62581         var firstOfMonth = date.getFirstDateOfMonth();
62582         var startingPos = firstOfMonth.getDay()-this.startDay;
62583         
62584         if(startingPos < this.startDay){
62585             startingPos += 7;
62586         }
62587         
62588         var pm = date.add(Date.MONTH, -1);
62589         var prevStart = pm.getDaysInMonth()-startingPos;
62590 //        
62591         
62592         
62593         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62594         
62595         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
62596         //this.cells.addClassOnOver('fc-state-hover');
62597         
62598         var cells = this.cells.elements;
62599         var textEls = this.textNodes;
62600         
62601         //Roo.each(cells, function(cell){
62602         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
62603         //});
62604         
62605         days += startingPos;
62606
62607         // convert everything to numbers so it's fast
62608         var day = 86400000;
62609         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
62610         //Roo.log(d);
62611         //Roo.log(pm);
62612         //Roo.log(prevStart);
62613         
62614         var today = new Date().clearTime().getTime();
62615         var sel = date.clearTime().getTime();
62616         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
62617         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
62618         var ddMatch = this.disabledDatesRE;
62619         var ddText = this.disabledDatesText;
62620         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
62621         var ddaysText = this.disabledDaysText;
62622         var format = this.format;
62623         
62624         var setCellClass = function(cal, cell){
62625             
62626             //Roo.log('set Cell Class');
62627             cell.title = "";
62628             var t = d.getTime();
62629             
62630             //Roo.log(d);
62631             
62632             
62633             cell.dateValue = t;
62634             if(t == today){
62635                 cell.className += " fc-today";
62636                 cell.className += " fc-state-highlight";
62637                 cell.title = cal.todayText;
62638             }
62639             if(t == sel){
62640                 // disable highlight in other month..
62641                 cell.className += " fc-state-highlight";
62642                 
62643             }
62644             // disabling
62645             if(t < min) {
62646                 //cell.className = " fc-state-disabled";
62647                 cell.title = cal.minText;
62648                 return;
62649             }
62650             if(t > max) {
62651                 //cell.className = " fc-state-disabled";
62652                 cell.title = cal.maxText;
62653                 return;
62654             }
62655             if(ddays){
62656                 if(ddays.indexOf(d.getDay()) != -1){
62657                     // cell.title = ddaysText;
62658                    // cell.className = " fc-state-disabled";
62659                 }
62660             }
62661             if(ddMatch && format){
62662                 var fvalue = d.dateFormat(format);
62663                 if(ddMatch.test(fvalue)){
62664                     cell.title = ddText.replace("%0", fvalue);
62665                    cell.className = " fc-state-disabled";
62666                 }
62667             }
62668             
62669             if (!cell.initialClassName) {
62670                 cell.initialClassName = cell.dom.className;
62671             }
62672             
62673             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
62674         };
62675
62676         var i = 0;
62677         
62678         for(; i < startingPos; i++) {
62679             cells[i].dayName =  (++prevStart);
62680             Roo.log(textEls[i]);
62681             d.setDate(d.getDate()+1);
62682             
62683             //cells[i].className = "fc-past fc-other-month";
62684             setCellClass(this, cells[i]);
62685         }
62686         
62687         var intDay = 0;
62688         
62689         for(; i < days; i++){
62690             intDay = i - startingPos + 1;
62691             cells[i].dayName =  (intDay);
62692             d.setDate(d.getDate()+1);
62693             
62694             cells[i].className = ''; // "x-date-active";
62695             setCellClass(this, cells[i]);
62696         }
62697         var extraDays = 0;
62698         
62699         for(; i < 42; i++) {
62700             //textEls[i].innerHTML = (++extraDays);
62701             
62702             d.setDate(d.getDate()+1);
62703             cells[i].dayName = (++extraDays);
62704             cells[i].className = "fc-future fc-other-month";
62705             setCellClass(this, cells[i]);
62706         }
62707         
62708         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
62709         
62710         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
62711         
62712         // this will cause all the cells to mis
62713         var rows= [];
62714         var i =0;
62715         for (var r = 0;r < 6;r++) {
62716             for (var c =0;c < 7;c++) {
62717                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
62718             }    
62719         }
62720         
62721         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62722         for(i=0;i<cells.length;i++) {
62723             
62724             this.cells.elements[i].dayName = cells[i].dayName ;
62725             this.cells.elements[i].className = cells[i].className;
62726             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
62727             this.cells.elements[i].title = cells[i].title ;
62728             this.cells.elements[i].dateValue = cells[i].dateValue ;
62729         }
62730         
62731         
62732         
62733         
62734         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
62735         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
62736         
62737         ////if(totalRows != 6){
62738             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
62739            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
62740        // }
62741         
62742         this.fireEvent('monthchange', this, date);
62743         
62744         
62745     },
62746  /**
62747      * Returns the grid's SelectionModel.
62748      * @return {SelectionModel}
62749      */
62750     getSelectionModel : function(){
62751         if(!this.selModel){
62752             this.selModel = new Roo.grid.CellSelectionModel();
62753         }
62754         return this.selModel;
62755     },
62756
62757     load: function() {
62758         this.eventStore.load()
62759         
62760         
62761         
62762     },
62763     
62764     findCell : function(dt) {
62765         dt = dt.clearTime().getTime();
62766         var ret = false;
62767         this.cells.each(function(c){
62768             //Roo.log("check " +c.dateValue + '?=' + dt);
62769             if(c.dateValue == dt){
62770                 ret = c;
62771                 return false;
62772             }
62773             return true;
62774         });
62775         
62776         return ret;
62777     },
62778     
62779     findCells : function(rec) {
62780         var s = rec.data.start_dt.clone().clearTime().getTime();
62781        // Roo.log(s);
62782         var e= rec.data.end_dt.clone().clearTime().getTime();
62783        // Roo.log(e);
62784         var ret = [];
62785         this.cells.each(function(c){
62786              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62787             
62788             if(c.dateValue > e){
62789                 return ;
62790             }
62791             if(c.dateValue < s){
62792                 return ;
62793             }
62794             ret.push(c);
62795         });
62796         
62797         return ret;    
62798     },
62799     
62800     findBestRow: function(cells)
62801     {
62802         var ret = 0;
62803         
62804         for (var i =0 ; i < cells.length;i++) {
62805             ret  = Math.max(cells[i].rows || 0,ret);
62806         }
62807         return ret;
62808         
62809     },
62810     
62811     
62812     addItem : function(rec)
62813     {
62814         // look for vertical location slot in
62815         var cells = this.findCells(rec);
62816         
62817         rec.row = this.findBestRow(cells);
62818         
62819         // work out the location.
62820         
62821         var crow = false;
62822         var rows = [];
62823         for(var i =0; i < cells.length; i++) {
62824             if (!crow) {
62825                 crow = {
62826                     start : cells[i],
62827                     end :  cells[i]
62828                 };
62829                 continue;
62830             }
62831             if (crow.start.getY() == cells[i].getY()) {
62832                 // on same row.
62833                 crow.end = cells[i];
62834                 continue;
62835             }
62836             // different row.
62837             rows.push(crow);
62838             crow = {
62839                 start: cells[i],
62840                 end : cells[i]
62841             };
62842             
62843         }
62844         
62845         rows.push(crow);
62846         rec.els = [];
62847         rec.rows = rows;
62848         rec.cells = cells;
62849         for (var i = 0; i < cells.length;i++) {
62850             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62851             
62852         }
62853         
62854         
62855     },
62856     
62857     clearEvents: function() {
62858         
62859         if (!this.eventStore.getCount()) {
62860             return;
62861         }
62862         // reset number of rows in cells.
62863         Roo.each(this.cells.elements, function(c){
62864             c.rows = 0;
62865         });
62866         
62867         this.eventStore.each(function(e) {
62868             this.clearEvent(e);
62869         },this);
62870         
62871     },
62872     
62873     clearEvent : function(ev)
62874     {
62875         if (ev.els) {
62876             Roo.each(ev.els, function(el) {
62877                 el.un('mouseenter' ,this.onEventEnter, this);
62878                 el.un('mouseleave' ,this.onEventLeave, this);
62879                 el.remove();
62880             },this);
62881             ev.els = [];
62882         }
62883     },
62884     
62885     
62886     renderEvent : function(ev,ctr) {
62887         if (!ctr) {
62888              ctr = this.view.el.select('.fc-event-container',true).first();
62889         }
62890         
62891          
62892         this.clearEvent(ev);
62893             //code
62894        
62895         
62896         
62897         ev.els = [];
62898         var cells = ev.cells;
62899         var rows = ev.rows;
62900         this.fireEvent('eventrender', this, ev);
62901         
62902         for(var i =0; i < rows.length; i++) {
62903             
62904             cls = '';
62905             if (i == 0) {
62906                 cls += ' fc-event-start';
62907             }
62908             if ((i+1) == rows.length) {
62909                 cls += ' fc-event-end';
62910             }
62911             
62912             //Roo.log(ev.data);
62913             // how many rows should it span..
62914             var cg = this.eventTmpl.append(ctr,Roo.apply({
62915                 fccls : cls
62916                 
62917             }, ev.data) , true);
62918             
62919             
62920             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62921             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62922             cg.on('click', this.onEventClick, this, ev);
62923             
62924             ev.els.push(cg);
62925             
62926             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62927             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62928             //Roo.log(cg);
62929              
62930             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62931             cg.setWidth(ebox.right - sbox.x -2);
62932         }
62933     },
62934     
62935     renderEvents: function()
62936     {   
62937         // first make sure there is enough space..
62938         
62939         if (!this.eventTmpl) {
62940             this.eventTmpl = new Roo.Template(
62941                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62942                     '<div class="fc-event-inner">' +
62943                         '<span class="fc-event-time">{time}</span>' +
62944                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62945                     '</div>' +
62946                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62947                 '</div>'
62948             );
62949                 
62950         }
62951                
62952         
62953         
62954         this.cells.each(function(c) {
62955             //Roo.log(c.select('.fc-day-content div',true).first());
62956             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62957         });
62958         
62959         var ctr = this.view.el.select('.fc-event-container',true).first();
62960         
62961         var cls;
62962         this.eventStore.each(function(ev){
62963             
62964             this.renderEvent(ev);
62965              
62966              
62967         }, this);
62968         this.view.layout();
62969         
62970     },
62971     
62972     onEventEnter: function (e, el,event,d) {
62973         this.fireEvent('evententer', this, el, event);
62974     },
62975     
62976     onEventLeave: function (e, el,event,d) {
62977         this.fireEvent('eventleave', this, el, event);
62978     },
62979     
62980     onEventClick: function (e, el,event,d) {
62981         this.fireEvent('eventclick', this, el, event);
62982     },
62983     
62984     onMonthChange: function () {
62985         this.store.load();
62986     },
62987     
62988     onLoad: function () {
62989         
62990         //Roo.log('calendar onload');
62991 //         
62992         if(this.eventStore.getCount() > 0){
62993             
62994            
62995             
62996             this.eventStore.each(function(d){
62997                 
62998                 
62999                 // FIXME..
63000                 var add =   d.data;
63001                 if (typeof(add.end_dt) == 'undefined')  {
63002                     Roo.log("Missing End time in calendar data: ");
63003                     Roo.log(d);
63004                     return;
63005                 }
63006                 if (typeof(add.start_dt) == 'undefined')  {
63007                     Roo.log("Missing Start time in calendar data: ");
63008                     Roo.log(d);
63009                     return;
63010                 }
63011                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63012                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63013                 add.id = add.id || d.id;
63014                 add.title = add.title || '??';
63015                 
63016                 this.addItem(d);
63017                 
63018              
63019             },this);
63020         }
63021         
63022         this.renderEvents();
63023     }
63024     
63025
63026 });
63027 /*
63028  grid : {
63029                 xtype: 'Grid',
63030                 xns: Roo.grid,
63031                 listeners : {
63032                     render : function ()
63033                     {
63034                         _this.grid = this;
63035                         
63036                         if (!this.view.el.hasClass('course-timesheet')) {
63037                             this.view.el.addClass('course-timesheet');
63038                         }
63039                         if (this.tsStyle) {
63040                             this.ds.load({});
63041                             return; 
63042                         }
63043                         Roo.log('width');
63044                         Roo.log(_this.grid.view.el.getWidth());
63045                         
63046                         
63047                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63048                             '.course-timesheet .x-grid-row' : {
63049                                 height: '80px'
63050                             },
63051                             '.x-grid-row td' : {
63052                                 'vertical-align' : 0
63053                             },
63054                             '.course-edit-link' : {
63055                                 'color' : 'blue',
63056                                 'text-overflow' : 'ellipsis',
63057                                 'overflow' : 'hidden',
63058                                 'white-space' : 'nowrap',
63059                                 'cursor' : 'pointer'
63060                             },
63061                             '.sub-link' : {
63062                                 'color' : 'green'
63063                             },
63064                             '.de-act-sup-link' : {
63065                                 'color' : 'purple',
63066                                 'text-decoration' : 'line-through'
63067                             },
63068                             '.de-act-link' : {
63069                                 'color' : 'red',
63070                                 'text-decoration' : 'line-through'
63071                             },
63072                             '.course-timesheet .course-highlight' : {
63073                                 'border-top-style': 'dashed !important',
63074                                 'border-bottom-bottom': 'dashed !important'
63075                             },
63076                             '.course-timesheet .course-item' : {
63077                                 'font-family'   : 'tahoma, arial, helvetica',
63078                                 'font-size'     : '11px',
63079                                 'overflow'      : 'hidden',
63080                                 'padding-left'  : '10px',
63081                                 'padding-right' : '10px',
63082                                 'padding-top' : '10px' 
63083                             }
63084                             
63085                         }, Roo.id());
63086                                 this.ds.load({});
63087                     }
63088                 },
63089                 autoWidth : true,
63090                 monitorWindowResize : false,
63091                 cellrenderer : function(v,x,r)
63092                 {
63093                     return v;
63094                 },
63095                 sm : {
63096                     xtype: 'CellSelectionModel',
63097                     xns: Roo.grid
63098                 },
63099                 dataSource : {
63100                     xtype: 'Store',
63101                     xns: Roo.data,
63102                     listeners : {
63103                         beforeload : function (_self, options)
63104                         {
63105                             options.params = options.params || {};
63106                             options.params._month = _this.monthField.getValue();
63107                             options.params.limit = 9999;
63108                             options.params['sort'] = 'when_dt';    
63109                             options.params['dir'] = 'ASC';    
63110                             this.proxy.loadResponse = this.loadResponse;
63111                             Roo.log("load?");
63112                             //this.addColumns();
63113                         },
63114                         load : function (_self, records, options)
63115                         {
63116                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63117                                 // if you click on the translation.. you can edit it...
63118                                 var el = Roo.get(this);
63119                                 var id = el.dom.getAttribute('data-id');
63120                                 var d = el.dom.getAttribute('data-date');
63121                                 var t = el.dom.getAttribute('data-time');
63122                                 //var id = this.child('span').dom.textContent;
63123                                 
63124                                 //Roo.log(this);
63125                                 Pman.Dialog.CourseCalendar.show({
63126                                     id : id,
63127                                     when_d : d,
63128                                     when_t : t,
63129                                     productitem_active : id ? 1 : 0
63130                                 }, function() {
63131                                     _this.grid.ds.load({});
63132                                 });
63133                            
63134                            });
63135                            
63136                            _this.panel.fireEvent('resize', [ '', '' ]);
63137                         }
63138                     },
63139                     loadResponse : function(o, success, response){
63140                             // this is overridden on before load..
63141                             
63142                             Roo.log("our code?");       
63143                             //Roo.log(success);
63144                             //Roo.log(response)
63145                             delete this.activeRequest;
63146                             if(!success){
63147                                 this.fireEvent("loadexception", this, o, response);
63148                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63149                                 return;
63150                             }
63151                             var result;
63152                             try {
63153                                 result = o.reader.read(response);
63154                             }catch(e){
63155                                 Roo.log("load exception?");
63156                                 this.fireEvent("loadexception", this, o, response, e);
63157                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63158                                 return;
63159                             }
63160                             Roo.log("ready...");        
63161                             // loop through result.records;
63162                             // and set this.tdate[date] = [] << array of records..
63163                             _this.tdata  = {};
63164                             Roo.each(result.records, function(r){
63165                                 //Roo.log(r.data);
63166                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63167                                     _this.tdata[r.data.when_dt.format('j')] = [];
63168                                 }
63169                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63170                             });
63171                             
63172                             //Roo.log(_this.tdata);
63173                             
63174                             result.records = [];
63175                             result.totalRecords = 6;
63176                     
63177                             // let's generate some duumy records for the rows.
63178                             //var st = _this.dateField.getValue();
63179                             
63180                             // work out monday..
63181                             //st = st.add(Date.DAY, -1 * st.format('w'));
63182                             
63183                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63184                             
63185                             var firstOfMonth = date.getFirstDayOfMonth();
63186                             var days = date.getDaysInMonth();
63187                             var d = 1;
63188                             var firstAdded = false;
63189                             for (var i = 0; i < result.totalRecords ; i++) {
63190                                 //var d= st.add(Date.DAY, i);
63191                                 var row = {};
63192                                 var added = 0;
63193                                 for(var w = 0 ; w < 7 ; w++){
63194                                     if(!firstAdded && firstOfMonth != w){
63195                                         continue;
63196                                     }
63197                                     if(d > days){
63198                                         continue;
63199                                     }
63200                                     firstAdded = true;
63201                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63202                                     row['weekday'+w] = String.format(
63203                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63204                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63205                                                     d,
63206                                                     date.format('Y-m-')+dd
63207                                                 );
63208                                     added++;
63209                                     if(typeof(_this.tdata[d]) != 'undefined'){
63210                                         Roo.each(_this.tdata[d], function(r){
63211                                             var is_sub = '';
63212                                             var deactive = '';
63213                                             var id = r.id;
63214                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63215                                             if(r.parent_id*1>0){
63216                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63217                                                 id = r.parent_id;
63218                                             }
63219                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63220                                                 deactive = 'de-act-link';
63221                                             }
63222                                             
63223                                             row['weekday'+w] += String.format(
63224                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63225                                                     id, //0
63226                                                     r.product_id_name, //1
63227                                                     r.when_dt.format('h:ia'), //2
63228                                                     is_sub, //3
63229                                                     deactive, //4
63230                                                     desc // 5
63231                                             );
63232                                         });
63233                                     }
63234                                     d++;
63235                                 }
63236                                 
63237                                 // only do this if something added..
63238                                 if(added > 0){ 
63239                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63240                                 }
63241                                 
63242                                 
63243                                 // push it twice. (second one with an hour..
63244                                 
63245                             }
63246                             //Roo.log(result);
63247                             this.fireEvent("load", this, o, o.request.arg);
63248                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63249                         },
63250                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63251                     proxy : {
63252                         xtype: 'HttpProxy',
63253                         xns: Roo.data,
63254                         method : 'GET',
63255                         url : baseURL + '/Roo/Shop_course.php'
63256                     },
63257                     reader : {
63258                         xtype: 'JsonReader',
63259                         xns: Roo.data,
63260                         id : 'id',
63261                         fields : [
63262                             {
63263                                 'name': 'id',
63264                                 'type': 'int'
63265                             },
63266                             {
63267                                 'name': 'when_dt',
63268                                 'type': 'string'
63269                             },
63270                             {
63271                                 'name': 'end_dt',
63272                                 'type': 'string'
63273                             },
63274                             {
63275                                 'name': 'parent_id',
63276                                 'type': 'int'
63277                             },
63278                             {
63279                                 'name': 'product_id',
63280                                 'type': 'int'
63281                             },
63282                             {
63283                                 'name': 'productitem_id',
63284                                 'type': 'int'
63285                             },
63286                             {
63287                                 'name': 'guid',
63288                                 'type': 'int'
63289                             }
63290                         ]
63291                     }
63292                 },
63293                 toolbar : {
63294                     xtype: 'Toolbar',
63295                     xns: Roo,
63296                     items : [
63297                         {
63298                             xtype: 'Button',
63299                             xns: Roo.Toolbar,
63300                             listeners : {
63301                                 click : function (_self, e)
63302                                 {
63303                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63304                                     sd.setMonth(sd.getMonth()-1);
63305                                     _this.monthField.setValue(sd.format('Y-m-d'));
63306                                     _this.grid.ds.load({});
63307                                 }
63308                             },
63309                             text : "Back"
63310                         },
63311                         {
63312                             xtype: 'Separator',
63313                             xns: Roo.Toolbar
63314                         },
63315                         {
63316                             xtype: 'MonthField',
63317                             xns: Roo.form,
63318                             listeners : {
63319                                 render : function (_self)
63320                                 {
63321                                     _this.monthField = _self;
63322                                    // _this.monthField.set  today
63323                                 },
63324                                 select : function (combo, date)
63325                                 {
63326                                     _this.grid.ds.load({});
63327                                 }
63328                             },
63329                             value : (function() { return new Date(); })()
63330                         },
63331                         {
63332                             xtype: 'Separator',
63333                             xns: Roo.Toolbar
63334                         },
63335                         {
63336                             xtype: 'TextItem',
63337                             xns: Roo.Toolbar,
63338                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63339                         },
63340                         {
63341                             xtype: 'Fill',
63342                             xns: Roo.Toolbar
63343                         },
63344                         {
63345                             xtype: 'Button',
63346                             xns: Roo.Toolbar,
63347                             listeners : {
63348                                 click : function (_self, e)
63349                                 {
63350                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63351                                     sd.setMonth(sd.getMonth()+1);
63352                                     _this.monthField.setValue(sd.format('Y-m-d'));
63353                                     _this.grid.ds.load({});
63354                                 }
63355                             },
63356                             text : "Next"
63357                         }
63358                     ]
63359                 },
63360                  
63361             }
63362         };
63363         
63364         *//*
63365  * Based on:
63366  * Ext JS Library 1.1.1
63367  * Copyright(c) 2006-2007, Ext JS, LLC.
63368  *
63369  * Originally Released Under LGPL - original licence link has changed is not relivant.
63370  *
63371  * Fork - LGPL
63372  * <script type="text/javascript">
63373  */
63374  
63375 /**
63376  * @class Roo.LoadMask
63377  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63378  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63379  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63380  * element's UpdateManager load indicator and will be destroyed after the initial load.
63381  * @constructor
63382  * Create a new LoadMask
63383  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63384  * @param {Object} config The config object
63385  */
63386 Roo.LoadMask = function(el, config){
63387     this.el = Roo.get(el);
63388     Roo.apply(this, config);
63389     if(this.store){
63390         this.store.on('beforeload', this.onBeforeLoad, this);
63391         this.store.on('load', this.onLoad, this);
63392         this.store.on('loadexception', this.onLoadException, this);
63393         this.removeMask = false;
63394     }else{
63395         var um = this.el.getUpdateManager();
63396         um.showLoadIndicator = false; // disable the default indicator
63397         um.on('beforeupdate', this.onBeforeLoad, this);
63398         um.on('update', this.onLoad, this);
63399         um.on('failure', this.onLoad, this);
63400         this.removeMask = true;
63401     }
63402 };
63403
63404 Roo.LoadMask.prototype = {
63405     /**
63406      * @cfg {Boolean} removeMask
63407      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63408      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63409      */
63410     removeMask : false,
63411     /**
63412      * @cfg {String} msg
63413      * The text to display in a centered loading message box (defaults to 'Loading...')
63414      */
63415     msg : 'Loading...',
63416     /**
63417      * @cfg {String} msgCls
63418      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
63419      */
63420     msgCls : 'x-mask-loading',
63421
63422     /**
63423      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
63424      * @type Boolean
63425      */
63426     disabled: false,
63427
63428     /**
63429      * Disables the mask to prevent it from being displayed
63430      */
63431     disable : function(){
63432        this.disabled = true;
63433     },
63434
63435     /**
63436      * Enables the mask so that it can be displayed
63437      */
63438     enable : function(){
63439         this.disabled = false;
63440     },
63441     
63442     onLoadException : function()
63443     {
63444         Roo.log(arguments);
63445         
63446         if (typeof(arguments[3]) != 'undefined') {
63447             Roo.MessageBox.alert("Error loading",arguments[3]);
63448         } 
63449         /*
63450         try {
63451             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
63452                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
63453             }   
63454         } catch(e) {
63455             
63456         }
63457         */
63458     
63459         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63460     },
63461     // private
63462     onLoad : function()
63463     {
63464         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63465     },
63466
63467     // private
63468     onBeforeLoad : function(){
63469         if(!this.disabled){
63470             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
63471         }
63472     },
63473
63474     // private
63475     destroy : function(){
63476         if(this.store){
63477             this.store.un('beforeload', this.onBeforeLoad, this);
63478             this.store.un('load', this.onLoad, this);
63479             this.store.un('loadexception', this.onLoadException, this);
63480         }else{
63481             var um = this.el.getUpdateManager();
63482             um.un('beforeupdate', this.onBeforeLoad, this);
63483             um.un('update', this.onLoad, this);
63484             um.un('failure', this.onLoad, this);
63485         }
63486     }
63487 };/*
63488  * Based on:
63489  * Ext JS Library 1.1.1
63490  * Copyright(c) 2006-2007, Ext JS, LLC.
63491  *
63492  * Originally Released Under LGPL - original licence link has changed is not relivant.
63493  *
63494  * Fork - LGPL
63495  * <script type="text/javascript">
63496  */
63497
63498
63499 /**
63500  * @class Roo.XTemplate
63501  * @extends Roo.Template
63502  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
63503 <pre><code>
63504 var t = new Roo.XTemplate(
63505         '&lt;select name="{name}"&gt;',
63506                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
63507         '&lt;/select&gt;'
63508 );
63509  
63510 // then append, applying the master template values
63511  </code></pre>
63512  *
63513  * Supported features:
63514  *
63515  *  Tags:
63516
63517 <pre><code>
63518       {a_variable} - output encoded.
63519       {a_variable.format:("Y-m-d")} - call a method on the variable
63520       {a_variable:raw} - unencoded output
63521       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
63522       {a_variable:this.method_on_template(...)} - call a method on the template object.
63523  
63524 </code></pre>
63525  *  The tpl tag:
63526 <pre><code>
63527         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
63528         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
63529         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
63530         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
63531   
63532         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
63533         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
63534 </code></pre>
63535  *      
63536  */
63537 Roo.XTemplate = function()
63538 {
63539     Roo.XTemplate.superclass.constructor.apply(this, arguments);
63540     if (this.html) {
63541         this.compile();
63542     }
63543 };
63544
63545
63546 Roo.extend(Roo.XTemplate, Roo.Template, {
63547
63548     /**
63549      * The various sub templates
63550      */
63551     tpls : false,
63552     /**
63553      *
63554      * basic tag replacing syntax
63555      * WORD:WORD()
63556      *
63557      * // you can fake an object call by doing this
63558      *  x.t:(test,tesT) 
63559      * 
63560      */
63561     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
63562
63563     /**
63564      * compile the template
63565      *
63566      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
63567      *
63568      */
63569     compile: function()
63570     {
63571         var s = this.html;
63572      
63573         s = ['<tpl>', s, '</tpl>'].join('');
63574     
63575         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
63576             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
63577             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
63578             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
63579             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
63580             m,
63581             id     = 0,
63582             tpls   = [];
63583     
63584         while(true == !!(m = s.match(re))){
63585             var forMatch   = m[0].match(nameRe),
63586                 ifMatch   = m[0].match(ifRe),
63587                 execMatch   = m[0].match(execRe),
63588                 namedMatch   = m[0].match(namedRe),
63589                 
63590                 exp  = null, 
63591                 fn   = null,
63592                 exec = null,
63593                 name = forMatch && forMatch[1] ? forMatch[1] : '';
63594                 
63595             if (ifMatch) {
63596                 // if - puts fn into test..
63597                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
63598                 if(exp){
63599                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
63600                 }
63601             }
63602             
63603             if (execMatch) {
63604                 // exec - calls a function... returns empty if true is  returned.
63605                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
63606                 if(exp){
63607                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
63608                 }
63609             }
63610             
63611             
63612             if (name) {
63613                 // for = 
63614                 switch(name){
63615                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
63616                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
63617                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
63618                 }
63619             }
63620             var uid = namedMatch ? namedMatch[1] : id;
63621             
63622             
63623             tpls.push({
63624                 id:     namedMatch ? namedMatch[1] : id,
63625                 target: name,
63626                 exec:   exec,
63627                 test:   fn,
63628                 body:   m[1] || ''
63629             });
63630             if (namedMatch) {
63631                 s = s.replace(m[0], '');
63632             } else { 
63633                 s = s.replace(m[0], '{xtpl'+ id + '}');
63634             }
63635             ++id;
63636         }
63637         this.tpls = [];
63638         for(var i = tpls.length-1; i >= 0; --i){
63639             this.compileTpl(tpls[i]);
63640             this.tpls[tpls[i].id] = tpls[i];
63641         }
63642         this.master = tpls[tpls.length-1];
63643         return this;
63644     },
63645     /**
63646      * same as applyTemplate, except it's done to one of the subTemplates
63647      * when using named templates, you can do:
63648      *
63649      * var str = pl.applySubTemplate('your-name', values);
63650      *
63651      * 
63652      * @param {Number} id of the template
63653      * @param {Object} values to apply to template
63654      * @param {Object} parent (normaly the instance of this object)
63655      */
63656     applySubTemplate : function(id, values, parent)
63657     {
63658         
63659         
63660         var t = this.tpls[id];
63661         
63662         
63663         try { 
63664             if(t.test && !t.test.call(this, values, parent)){
63665                 return '';
63666             }
63667         } catch(e) {
63668             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
63669             Roo.log(e.toString());
63670             Roo.log(t.test);
63671             return ''
63672         }
63673         try { 
63674             
63675             if(t.exec && t.exec.call(this, values, parent)){
63676                 return '';
63677             }
63678         } catch(e) {
63679             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
63680             Roo.log(e.toString());
63681             Roo.log(t.exec);
63682             return ''
63683         }
63684         try {
63685             var vs = t.target ? t.target.call(this, values, parent) : values;
63686             parent = t.target ? values : parent;
63687             if(t.target && vs instanceof Array){
63688                 var buf = [];
63689                 for(var i = 0, len = vs.length; i < len; i++){
63690                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
63691                 }
63692                 return buf.join('');
63693             }
63694             return t.compiled.call(this, vs, parent);
63695         } catch (e) {
63696             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
63697             Roo.log(e.toString());
63698             Roo.log(t.compiled);
63699             return '';
63700         }
63701     },
63702
63703     compileTpl : function(tpl)
63704     {
63705         var fm = Roo.util.Format;
63706         var useF = this.disableFormats !== true;
63707         var sep = Roo.isGecko ? "+" : ",";
63708         var undef = function(str) {
63709             Roo.log("Property not found :"  + str);
63710             return '';
63711         };
63712         
63713         var fn = function(m, name, format, args)
63714         {
63715             //Roo.log(arguments);
63716             args = args ? args.replace(/\\'/g,"'") : args;
63717             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
63718             if (typeof(format) == 'undefined') {
63719                 format= 'htmlEncode';
63720             }
63721             if (format == 'raw' ) {
63722                 format = false;
63723             }
63724             
63725             if(name.substr(0, 4) == 'xtpl'){
63726                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
63727             }
63728             
63729             // build an array of options to determine if value is undefined..
63730             
63731             // basically get 'xxxx.yyyy' then do
63732             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
63733             //    (function () { Roo.log("Property not found"); return ''; })() :
63734             //    ......
63735             
63736             var udef_ar = [];
63737             var lookfor = '';
63738             Roo.each(name.split('.'), function(st) {
63739                 lookfor += (lookfor.length ? '.': '') + st;
63740                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
63741             });
63742             
63743             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
63744             
63745             
63746             if(format && useF){
63747                 
63748                 args = args ? ',' + args : "";
63749                  
63750                 if(format.substr(0, 5) != "this."){
63751                     format = "fm." + format + '(';
63752                 }else{
63753                     format = 'this.call("'+ format.substr(5) + '", ';
63754                     args = ", values";
63755                 }
63756                 
63757                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63758             }
63759              
63760             if (args.length) {
63761                 // called with xxyx.yuu:(test,test)
63762                 // change to ()
63763                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63764             }
63765             // raw.. - :raw modifier..
63766             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63767             
63768         };
63769         var body;
63770         // branched to use + in gecko and [].join() in others
63771         if(Roo.isGecko){
63772             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63773                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63774                     "';};};";
63775         }else{
63776             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63777             body.push(tpl.body.replace(/(\r\n|\n)/g,
63778                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63779             body.push("'].join('');};};");
63780             body = body.join('');
63781         }
63782         
63783         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63784        
63785         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63786         eval(body);
63787         
63788         return this;
63789     },
63790
63791     applyTemplate : function(values){
63792         return this.master.compiled.call(this, values, {});
63793         //var s = this.subs;
63794     },
63795
63796     apply : function(){
63797         return this.applyTemplate.apply(this, arguments);
63798     }
63799
63800  });
63801
63802 Roo.XTemplate.from = function(el){
63803     el = Roo.getDom(el);
63804     return new Roo.XTemplate(el.value || el.innerHTML);
63805 };