d955de178c9dd26bf867b0ea4eb5fb484cba2ed1
[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  * @singleton
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  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Date
1086  *
1087  * The date parsing and format syntax is a subset of
1088  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1089  * supported will provide results equivalent to their PHP versions.
1090  *
1091  * Following is the list of all currently supported formats:
1092  *<pre>
1093 Sample date:
1094 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1095
1096 Format  Output      Description
1097 ------  ----------  --------------------------------------------------------------
1098   d      10         Day of the month, 2 digits with leading zeros
1099   D      Wed        A textual representation of a day, three letters
1100   j      10         Day of the month without leading zeros
1101   l      Wednesday  A full textual representation of the day of the week
1102   S      th         English ordinal day of month suffix, 2 chars (use with j)
1103   w      3          Numeric representation of the day of the week
1104   z      9          The julian date, or day of the year (0-365)
1105   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1106   F      January    A full textual representation of the month
1107   m      01         Numeric representation of a month, with leading zeros
1108   M      Jan        Month name abbreviation, three letters
1109   n      1          Numeric representation of a month, without leading zeros
1110   t      31         Number of days in the given month
1111   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1112   Y      2007       A full numeric representation of a year, 4 digits
1113   y      07         A two digit representation of a year
1114   a      pm         Lowercase Ante meridiem and Post meridiem
1115   A      PM         Uppercase Ante meridiem and Post meridiem
1116   g      3          12-hour format of an hour without leading zeros
1117   G      15         24-hour format of an hour without leading zeros
1118   h      03         12-hour format of an hour with leading zeros
1119   H      15         24-hour format of an hour with leading zeros
1120   i      05         Minutes with leading zeros
1121   s      01         Seconds, with leading zeros
1122   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1123   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1124   T      CST        Timezone setting of the machine running the code
1125   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1126 </pre>
1127  *
1128  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1129  * <pre><code>
1130 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1131 document.write(dt.format('Y-m-d'));                         //2007-01-10
1132 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1133 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
1134  </code></pre>
1135  *
1136  * Here are some standard date/time patterns that you might find helpful.  They
1137  * are not part of the source of Date.js, but to use them you can simply copy this
1138  * block of code into any script that is included after Date.js and they will also become
1139  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1140  * <pre><code>
1141 Date.patterns = {
1142     ISO8601Long:"Y-m-d H:i:s",
1143     ISO8601Short:"Y-m-d",
1144     ShortDate: "n/j/Y",
1145     LongDate: "l, F d, Y",
1146     FullDateTime: "l, F d, Y g:i:s A",
1147     MonthDay: "F d",
1148     ShortTime: "g:i A",
1149     LongTime: "g:i:s A",
1150     SortableDateTime: "Y-m-d\\TH:i:s",
1151     UniversalSortableDateTime: "Y-m-d H:i:sO",
1152     YearMonth: "F, Y"
1153 };
1154 </code></pre>
1155  *
1156  * Example usage:
1157  * <pre><code>
1158 var dt = new Date();
1159 document.write(dt.format(Date.patterns.ShortDate));
1160  </code></pre>
1161  */
1162
1163 /*
1164  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1165  * They generate precompiled functions from date formats instead of parsing and
1166  * processing the pattern every time you format a date.  These functions are available
1167  * on every Date object (any javascript function).
1168  *
1169  * The original article and download are here:
1170  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1171  *
1172  */
1173  
1174  
1175  // was in core
1176 /**
1177  Returns the number of milliseconds between this date and date
1178  @param {Date} date (optional) Defaults to now
1179  @return {Number} The diff in milliseconds
1180  @member Date getElapsed
1181  */
1182 Date.prototype.getElapsed = function(date) {
1183         return Math.abs((date || new Date()).getTime()-this.getTime());
1184 };
1185 // was in date file..
1186
1187
1188 // private
1189 Date.parseFunctions = {count:0};
1190 // private
1191 Date.parseRegexes = [];
1192 // private
1193 Date.formatFunctions = {count:0};
1194
1195 // private
1196 Date.prototype.dateFormat = function(format) {
1197     if (Date.formatFunctions[format] == null) {
1198         Date.createNewFormat(format);
1199     }
1200     var func = Date.formatFunctions[format];
1201     return this[func]();
1202 };
1203
1204
1205 /**
1206  * Formats a date given the supplied format string
1207  * @param {String} format The format string
1208  * @return {String} The formatted date
1209  * @method
1210  */
1211 Date.prototype.format = Date.prototype.dateFormat;
1212
1213 // private
1214 Date.createNewFormat = function(format) {
1215     var funcName = "format" + Date.formatFunctions.count++;
1216     Date.formatFunctions[format] = funcName;
1217     var code = "Date.prototype." + funcName + " = function(){return ";
1218     var special = false;
1219     var ch = '';
1220     for (var i = 0; i < format.length; ++i) {
1221         ch = format.charAt(i);
1222         if (!special && ch == "\\") {
1223             special = true;
1224         }
1225         else if (special) {
1226             special = false;
1227             code += "'" + String.escape(ch) + "' + ";
1228         }
1229         else {
1230             code += Date.getFormatCode(ch);
1231         }
1232     }
1233     /** eval:var:zzzzzzzzzzzzz */
1234     eval(code.substring(0, code.length - 3) + ";}");
1235 };
1236
1237 // private
1238 Date.getFormatCode = function(character) {
1239     switch (character) {
1240     case "d":
1241         return "String.leftPad(this.getDate(), 2, '0') + ";
1242     case "D":
1243         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1244     case "j":
1245         return "this.getDate() + ";
1246     case "l":
1247         return "Date.dayNames[this.getDay()] + ";
1248     case "S":
1249         return "this.getSuffix() + ";
1250     case "w":
1251         return "this.getDay() + ";
1252     case "z":
1253         return "this.getDayOfYear() + ";
1254     case "W":
1255         return "this.getWeekOfYear() + ";
1256     case "F":
1257         return "Date.monthNames[this.getMonth()] + ";
1258     case "m":
1259         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1260     case "M":
1261         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1262     case "n":
1263         return "(this.getMonth() + 1) + ";
1264     case "t":
1265         return "this.getDaysInMonth() + ";
1266     case "L":
1267         return "(this.isLeapYear() ? 1 : 0) + ";
1268     case "Y":
1269         return "this.getFullYear() + ";
1270     case "y":
1271         return "('' + this.getFullYear()).substring(2, 4) + ";
1272     case "a":
1273         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1274     case "A":
1275         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1276     case "g":
1277         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1278     case "G":
1279         return "this.getHours() + ";
1280     case "h":
1281         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1282     case "H":
1283         return "String.leftPad(this.getHours(), 2, '0') + ";
1284     case "i":
1285         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1286     case "s":
1287         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1288     case "O":
1289         return "this.getGMTOffset() + ";
1290     case "P":
1291         return "this.getGMTColonOffset() + ";
1292     case "T":
1293         return "this.getTimezone() + ";
1294     case "Z":
1295         return "(this.getTimezoneOffset() * -60) + ";
1296     default:
1297         return "'" + String.escape(character) + "' + ";
1298     }
1299 };
1300
1301 /**
1302  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1303  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1304  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1305  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1306  * string or the parse operation will fail.
1307  * Example Usage:
1308 <pre><code>
1309 //dt = Fri May 25 2007 (current date)
1310 var dt = new Date();
1311
1312 //dt = Thu May 25 2006 (today's month/day in 2006)
1313 dt = Date.parseDate("2006", "Y");
1314
1315 //dt = Sun Jan 15 2006 (all date parts specified)
1316 dt = Date.parseDate("2006-1-15", "Y-m-d");
1317
1318 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1319 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1320 </code></pre>
1321  * @param {String} input The unparsed date as a string
1322  * @param {String} format The format the date is in
1323  * @return {Date} The parsed date
1324  * @static
1325  */
1326 Date.parseDate = function(input, format) {
1327     if (Date.parseFunctions[format] == null) {
1328         Date.createParser(format);
1329     }
1330     var func = Date.parseFunctions[format];
1331     return Date[func](input);
1332 };
1333 /**
1334  * @private
1335  */
1336
1337 Date.createParser = function(format) {
1338     var funcName = "parse" + Date.parseFunctions.count++;
1339     var regexNum = Date.parseRegexes.length;
1340     var currentGroup = 1;
1341     Date.parseFunctions[format] = funcName;
1342
1343     var code = "Date." + funcName + " = function(input){\n"
1344         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1345         + "var d = new Date();\n"
1346         + "y = d.getFullYear();\n"
1347         + "m = d.getMonth();\n"
1348         + "d = d.getDate();\n"
1349         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1350         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1351         + "if (results && results.length > 0) {";
1352     var regex = "";
1353
1354     var special = false;
1355     var ch = '';
1356     for (var i = 0; i < format.length; ++i) {
1357         ch = format.charAt(i);
1358         if (!special && ch == "\\") {
1359             special = true;
1360         }
1361         else if (special) {
1362             special = false;
1363             regex += String.escape(ch);
1364         }
1365         else {
1366             var obj = Date.formatCodeToRegex(ch, currentGroup);
1367             currentGroup += obj.g;
1368             regex += obj.s;
1369             if (obj.g && obj.c) {
1370                 code += obj.c;
1371             }
1372         }
1373     }
1374
1375     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1376         + "{v = new Date(y, m, d, h, i, s);}\n"
1377         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1378         + "{v = new Date(y, m, d, h, i);}\n"
1379         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1380         + "{v = new Date(y, m, d, h);}\n"
1381         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1382         + "{v = new Date(y, m, d);}\n"
1383         + "else if (y >= 0 && m >= 0)\n"
1384         + "{v = new Date(y, m);}\n"
1385         + "else if (y >= 0)\n"
1386         + "{v = new Date(y);}\n"
1387         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1388         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1389         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1390         + ";}";
1391
1392     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1393     /** eval:var:zzzzzzzzzzzzz */
1394     eval(code);
1395 };
1396
1397 // private
1398 Date.formatCodeToRegex = function(character, currentGroup) {
1399     switch (character) {
1400     case "D":
1401         return {g:0,
1402         c:null,
1403         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1404     case "j":
1405         return {g:1,
1406             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1407             s:"(\\d{1,2})"}; // day of month without leading zeroes
1408     case "d":
1409         return {g:1,
1410             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; // day of month with leading zeroes
1412     case "l":
1413         return {g:0,
1414             c:null,
1415             s:"(?:" + Date.dayNames.join("|") + ")"};
1416     case "S":
1417         return {g:0,
1418             c:null,
1419             s:"(?:st|nd|rd|th)"};
1420     case "w":
1421         return {g:0,
1422             c:null,
1423             s:"\\d"};
1424     case "z":
1425         return {g:0,
1426             c:null,
1427             s:"(?:\\d{1,3})"};
1428     case "W":
1429         return {g:0,
1430             c:null,
1431             s:"(?:\\d{2})"};
1432     case "F":
1433         return {g:1,
1434             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1435             s:"(" + Date.monthNames.join("|") + ")"};
1436     case "M":
1437         return {g:1,
1438             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1439             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1440     case "n":
1441         return {g:1,
1442             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1443             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1444     case "m":
1445         return {g:1,
1446             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1447             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1448     case "t":
1449         return {g:0,
1450             c:null,
1451             s:"\\d{1,2}"};
1452     case "L":
1453         return {g:0,
1454             c:null,
1455             s:"(?:1|0)"};
1456     case "Y":
1457         return {g:1,
1458             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{4})"};
1460     case "y":
1461         return {g:1,
1462             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1463                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1464             s:"(\\d{1,2})"};
1465     case "a":
1466         return {g:1,
1467             c:"if (results[" + currentGroup + "] == 'am') {\n"
1468                 + "if (h == 12) { h = 0; }\n"
1469                 + "} else { if (h < 12) { h += 12; }}",
1470             s:"(am|pm)"};
1471     case "A":
1472         return {g:1,
1473             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1474                 + "if (h == 12) { h = 0; }\n"
1475                 + "} else { if (h < 12) { h += 12; }}",
1476             s:"(AM|PM)"};
1477     case "g":
1478     case "G":
1479         return {g:1,
1480             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1481             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1482     case "h":
1483     case "H":
1484         return {g:1,
1485             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1486             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1487     case "i":
1488         return {g:1,
1489             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1490             s:"(\\d{2})"};
1491     case "s":
1492         return {g:1,
1493             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1494             s:"(\\d{2})"};
1495     case "O":
1496         return {g:1,
1497             c:[
1498                 "o = results[", currentGroup, "];\n",
1499                 "var sn = o.substring(0,1);\n", // get + / - sign
1500                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1501                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1502                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1503                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1504             ].join(""),
1505             s:"([+\-]\\d{2,4})"};
1506     
1507     
1508     case "P":
1509         return {g:1,
1510                 c:[
1511                    "o = results[", currentGroup, "];\n",
1512                    "var sn = o.substring(0,1);\n",
1513                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1514                    "var mn = o.substring(4,6) % 60;\n",
1515                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1516                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1517             ].join(""),
1518             s:"([+\-]\\d{4})"};
1519     case "T":
1520         return {g:0,
1521             c:null,
1522             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1523     case "Z":
1524         return {g:1,
1525             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1526                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1527             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1528     default:
1529         return {g:0,
1530             c:null,
1531             s:String.escape(character)};
1532     }
1533 };
1534
1535 /**
1536  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1537  * @return {String} The abbreviated timezone name (e.g. 'CST')
1538  */
1539 Date.prototype.getTimezone = function() {
1540     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1541 };
1542
1543 /**
1544  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1545  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1546  */
1547 Date.prototype.getGMTOffset = function() {
1548     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1549         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1550         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1551 };
1552
1553 /**
1554  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1555  * @return {String} 2-characters representing hours and 2-characters representing minutes
1556  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1557  */
1558 Date.prototype.getGMTColonOffset = function() {
1559         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1560                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1561                 + ":"
1562                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1563 }
1564
1565 /**
1566  * Get the numeric day number of the year, adjusted for leap year.
1567  * @return {Number} 0 through 364 (365 in leap years)
1568  */
1569 Date.prototype.getDayOfYear = function() {
1570     var num = 0;
1571     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1572     for (var i = 0; i < this.getMonth(); ++i) {
1573         num += Date.daysInMonth[i];
1574     }
1575     return num + this.getDate() - 1;
1576 };
1577
1578 /**
1579  * Get the string representation of the numeric week number of the year
1580  * (equivalent to the format specifier 'W').
1581  * @return {String} '00' through '52'
1582  */
1583 Date.prototype.getWeekOfYear = function() {
1584     // Skip to Thursday of this week
1585     var now = this.getDayOfYear() + (4 - this.getDay());
1586     // Find the first Thursday of the year
1587     var jan1 = new Date(this.getFullYear(), 0, 1);
1588     var then = (7 - jan1.getDay() + 4);
1589     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1590 };
1591
1592 /**
1593  * Whether or not the current date is in a leap year.
1594  * @return {Boolean} True if the current date is in a leap year, else false
1595  */
1596 Date.prototype.isLeapYear = function() {
1597     var year = this.getFullYear();
1598     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1599 };
1600
1601 /**
1602  * Get the first day of the current month, adjusted for leap year.  The returned value
1603  * is the numeric day index within the week (0-6) which can be used in conjunction with
1604  * the {@link #monthNames} array to retrieve the textual day name.
1605  * Example:
1606  *<pre><code>
1607 var dt = new Date('1/10/2007');
1608 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1609 </code></pre>
1610  * @return {Number} The day number (0-6)
1611  */
1612 Date.prototype.getFirstDayOfMonth = function() {
1613     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1614     return (day < 0) ? (day + 7) : day;
1615 };
1616
1617 /**
1618  * Get the last day of the current month, adjusted for leap year.  The returned value
1619  * is the numeric day index within the week (0-6) which can be used in conjunction with
1620  * the {@link #monthNames} array to retrieve the textual day name.
1621  * Example:
1622  *<pre><code>
1623 var dt = new Date('1/10/2007');
1624 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1625 </code></pre>
1626  * @return {Number} The day number (0-6)
1627  */
1628 Date.prototype.getLastDayOfMonth = function() {
1629     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1630     return (day < 0) ? (day + 7) : day;
1631 };
1632
1633
1634 /**
1635  * Get the first date of this date's month
1636  * @return {Date}
1637  */
1638 Date.prototype.getFirstDateOfMonth = function() {
1639     return new Date(this.getFullYear(), this.getMonth(), 1);
1640 };
1641
1642 /**
1643  * Get the last date of this date's month
1644  * @return {Date}
1645  */
1646 Date.prototype.getLastDateOfMonth = function() {
1647     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1648 };
1649 /**
1650  * Get the number of days in the current month, adjusted for leap year.
1651  * @return {Number} The number of days in the month
1652  */
1653 Date.prototype.getDaysInMonth = function() {
1654     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1655     return Date.daysInMonth[this.getMonth()];
1656 };
1657
1658 /**
1659  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1660  * @return {String} 'st, 'nd', 'rd' or 'th'
1661  */
1662 Date.prototype.getSuffix = function() {
1663     switch (this.getDate()) {
1664         case 1:
1665         case 21:
1666         case 31:
1667             return "st";
1668         case 2:
1669         case 22:
1670             return "nd";
1671         case 3:
1672         case 23:
1673             return "rd";
1674         default:
1675             return "th";
1676     }
1677 };
1678
1679 // private
1680 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1681
1682 /**
1683  * An array of textual month names.
1684  * Override these values for international dates, for example...
1685  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1686  * @type Array
1687  * @static
1688  */
1689 Date.monthNames =
1690    ["January",
1691     "February",
1692     "March",
1693     "April",
1694     "May",
1695     "June",
1696     "July",
1697     "August",
1698     "September",
1699     "October",
1700     "November",
1701     "December"];
1702
1703 /**
1704  * An array of textual day names.
1705  * Override these values for international dates, for example...
1706  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1707  * @type Array
1708  * @static
1709  */
1710 Date.dayNames =
1711    ["Sunday",
1712     "Monday",
1713     "Tuesday",
1714     "Wednesday",
1715     "Thursday",
1716     "Friday",
1717     "Saturday"];
1718
1719 // private
1720 Date.y2kYear = 50;
1721 // private
1722 Date.monthNumbers = {
1723     Jan:0,
1724     Feb:1,
1725     Mar:2,
1726     Apr:3,
1727     May:4,
1728     Jun:5,
1729     Jul:6,
1730     Aug:7,
1731     Sep:8,
1732     Oct:9,
1733     Nov:10,
1734     Dec:11};
1735
1736 /**
1737  * Creates and returns a new Date instance with the exact same date value as the called instance.
1738  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1739  * variable will also be changed.  When the intention is to create a new variable that will not
1740  * modify the original instance, you should create a clone.
1741  *
1742  * Example of correctly cloning a date:
1743  * <pre><code>
1744 //wrong way:
1745 var orig = new Date('10/1/2006');
1746 var copy = orig;
1747 copy.setDate(5);
1748 document.write(orig);  //returns 'Thu Oct 05 2006'!
1749
1750 //correct way:
1751 var orig = new Date('10/1/2006');
1752 var copy = orig.clone();
1753 copy.setDate(5);
1754 document.write(orig);  //returns 'Thu Oct 01 2006'
1755 </code></pre>
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.clone = function() {
1759         return new Date(this.getTime());
1760 };
1761
1762 /**
1763  * Clears any time information from this date
1764  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1765  @return {Date} this or the clone
1766  */
1767 Date.prototype.clearTime = function(clone){
1768     if(clone){
1769         return this.clone().clearTime();
1770     }
1771     this.setHours(0);
1772     this.setMinutes(0);
1773     this.setSeconds(0);
1774     this.setMilliseconds(0);
1775     return this;
1776 };
1777
1778 // private
1779 // safari setMonth is broken -- check that this is only donw once...
1780 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1781     Date.brokenSetMonth = Date.prototype.setMonth;
1782         Date.prototype.setMonth = function(num){
1783                 if(num <= -1){
1784                         var n = Math.ceil(-num);
1785                         var back_year = Math.ceil(n/12);
1786                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1787                         this.setFullYear(this.getFullYear() - back_year);
1788                         return Date.brokenSetMonth.call(this, month);
1789                 } else {
1790                         return Date.brokenSetMonth.apply(this, arguments);
1791                 }
1792         };
1793 }
1794
1795 /** Date interval constant 
1796 * @static 
1797 * @type String */
1798 Date.MILLI = "ms";
1799 /** Date interval constant 
1800 * @static 
1801 * @type String */
1802 Date.SECOND = "s";
1803 /** Date interval constant 
1804 * @static 
1805 * @type String */
1806 Date.MINUTE = "mi";
1807 /** Date interval constant 
1808 * @static 
1809 * @type String */
1810 Date.HOUR = "h";
1811 /** Date interval constant 
1812 * @static 
1813 * @type String */
1814 Date.DAY = "d";
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MONTH = "mo";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.YEAR = "y";
1823
1824 /**
1825  * Provides a convenient method of performing basic date arithmetic.  This method
1826  * does not modify the Date instance being called - it creates and returns
1827  * a new Date instance containing the resulting date value.
1828  *
1829  * Examples:
1830  * <pre><code>
1831 //Basic usage:
1832 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1833 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1834
1835 //Negative values will subtract correctly:
1836 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1837 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1838
1839 //You can even chain several calls together in one line!
1840 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1841 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1842  </code></pre>
1843  *
1844  * @param {String} interval   A valid date interval enum value
1845  * @param {Number} value      The amount to add to the current date
1846  * @return {Date} The new Date instance
1847  */
1848 Date.prototype.add = function(interval, value){
1849   var d = this.clone();
1850   if (!interval || value === 0) { return d; }
1851   switch(interval.toLowerCase()){
1852     case Date.MILLI:
1853       d.setMilliseconds(this.getMilliseconds() + value);
1854       break;
1855     case Date.SECOND:
1856       d.setSeconds(this.getSeconds() + value);
1857       break;
1858     case Date.MINUTE:
1859       d.setMinutes(this.getMinutes() + value);
1860       break;
1861     case Date.HOUR:
1862       d.setHours(this.getHours() + value);
1863       break;
1864     case Date.DAY:
1865       d.setDate(this.getDate() + value);
1866       break;
1867     case Date.MONTH:
1868       var day = this.getDate();
1869       if(day > 28){
1870           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1871       }
1872       d.setDate(day);
1873       d.setMonth(this.getMonth() + value);
1874       break;
1875     case Date.YEAR:
1876       d.setFullYear(this.getFullYear() + value);
1877       break;
1878   }
1879   return d;
1880 };
1881 /*
1882  * Based on:
1883  * Ext JS Library 1.1.1
1884  * Copyright(c) 2006-2007, Ext JS, LLC.
1885  *
1886  * Originally Released Under LGPL - original licence link has changed is not relivant.
1887  *
1888  * Fork - LGPL
1889  * <script type="text/javascript">
1890  */
1891
1892 /**
1893  * @class Roo.lib.Dom
1894  * @static
1895  * 
1896  * Dom utils (from YIU afaik)
1897  * 
1898  **/
1899 Roo.lib.Dom = {
1900     /**
1901      * Get the view width
1902      * @param {Boolean} full True will get the full document, otherwise it's the view width
1903      * @return {Number} The width
1904      */
1905      
1906     getViewWidth : function(full) {
1907         return full ? this.getDocumentWidth() : this.getViewportWidth();
1908     },
1909     /**
1910      * Get the view height
1911      * @param {Boolean} full True will get the full document, otherwise it's the view height
1912      * @return {Number} The height
1913      */
1914     getViewHeight : function(full) {
1915         return full ? this.getDocumentHeight() : this.getViewportHeight();
1916     },
1917
1918     getDocumentHeight: function() {
1919         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1920         return Math.max(scrollHeight, this.getViewportHeight());
1921     },
1922
1923     getDocumentWidth: function() {
1924         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1925         return Math.max(scrollWidth, this.getViewportWidth());
1926     },
1927
1928     getViewportHeight: function() {
1929         var height = self.innerHeight;
1930         var mode = document.compatMode;
1931
1932         if ((mode || Roo.isIE) && !Roo.isOpera) {
1933             height = (mode == "CSS1Compat") ?
1934                      document.documentElement.clientHeight :
1935                      document.body.clientHeight;
1936         }
1937
1938         return height;
1939     },
1940
1941     getViewportWidth: function() {
1942         var width = self.innerWidth;
1943         var mode = document.compatMode;
1944
1945         if (mode || Roo.isIE) {
1946             width = (mode == "CSS1Compat") ?
1947                     document.documentElement.clientWidth :
1948                     document.body.clientWidth;
1949         }
1950         return width;
1951     },
1952
1953     isAncestor : function(p, c) {
1954         p = Roo.getDom(p);
1955         c = Roo.getDom(c);
1956         if (!p || !c) {
1957             return false;
1958         }
1959
1960         if (p.contains && !Roo.isSafari) {
1961             return p.contains(c);
1962         } else if (p.compareDocumentPosition) {
1963             return !!(p.compareDocumentPosition(c) & 16);
1964         } else {
1965             var parent = c.parentNode;
1966             while (parent) {
1967                 if (parent == p) {
1968                     return true;
1969                 }
1970                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1971                     return false;
1972                 }
1973                 parent = parent.parentNode;
1974             }
1975             return false;
1976         }
1977     },
1978
1979     getRegion : function(el) {
1980         return Roo.lib.Region.getRegion(el);
1981     },
1982
1983     getY : function(el) {
1984         return this.getXY(el)[1];
1985     },
1986
1987     getX : function(el) {
1988         return this.getXY(el)[0];
1989     },
1990
1991     getXY : function(el) {
1992         var p, pe, b, scroll, bd = document.body;
1993         el = Roo.getDom(el);
1994         var fly = Roo.lib.AnimBase.fly;
1995         if (el.getBoundingClientRect) {
1996             b = el.getBoundingClientRect();
1997             scroll = fly(document).getScroll();
1998             return [b.left + scroll.left, b.top + scroll.top];
1999         }
2000         var x = 0, y = 0;
2001
2002         p = el;
2003
2004         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2005
2006         while (p) {
2007
2008             x += p.offsetLeft;
2009             y += p.offsetTop;
2010
2011             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2012                 hasAbsolute = true;
2013             }
2014
2015             if (Roo.isGecko) {
2016                 pe = fly(p);
2017
2018                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2019                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2020
2021
2022                 x += bl;
2023                 y += bt;
2024
2025
2026                 if (p != el && pe.getStyle('overflow') != 'visible') {
2027                     x += bl;
2028                     y += bt;
2029                 }
2030             }
2031             p = p.offsetParent;
2032         }
2033
2034         if (Roo.isSafari && hasAbsolute) {
2035             x -= bd.offsetLeft;
2036             y -= bd.offsetTop;
2037         }
2038
2039         if (Roo.isGecko && !hasAbsolute) {
2040             var dbd = fly(bd);
2041             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2042             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2043         }
2044
2045         p = el.parentNode;
2046         while (p && p != bd) {
2047             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2048                 x -= p.scrollLeft;
2049                 y -= p.scrollTop;
2050             }
2051             p = p.parentNode;
2052         }
2053         return [x, y];
2054     },
2055  
2056   
2057
2058
2059     setXY : function(el, xy) {
2060         el = Roo.fly(el, '_setXY');
2061         el.position();
2062         var pts = el.translatePoints(xy);
2063         if (xy[0] !== false) {
2064             el.dom.style.left = pts.left + "px";
2065         }
2066         if (xy[1] !== false) {
2067             el.dom.style.top = pts.top + "px";
2068         }
2069     },
2070
2071     setX : function(el, x) {
2072         this.setXY(el, [x, false]);
2073     },
2074
2075     setY : function(el, y) {
2076         this.setXY(el, [false, y]);
2077     }
2078 };
2079 /*
2080  * Portions of this file are based on pieces of Yahoo User Interface Library
2081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2082  * YUI licensed under the BSD License:
2083  * http://developer.yahoo.net/yui/license.txt
2084  * <script type="text/javascript">
2085  *
2086  */
2087
2088 Roo.lib.Event = function() {
2089     var loadComplete = false;
2090     var listeners = [];
2091     var unloadListeners = [];
2092     var retryCount = 0;
2093     var onAvailStack = [];
2094     var counter = 0;
2095     var lastError = null;
2096
2097     return {
2098         POLL_RETRYS: 200,
2099         POLL_INTERVAL: 20,
2100         EL: 0,
2101         TYPE: 1,
2102         FN: 2,
2103         WFN: 3,
2104         OBJ: 3,
2105         ADJ_SCOPE: 4,
2106         _interval: null,
2107
2108         startInterval: function() {
2109             if (!this._interval) {
2110                 var self = this;
2111                 var callback = function() {
2112                     self._tryPreloadAttach();
2113                 };
2114                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2115
2116             }
2117         },
2118
2119         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2120             onAvailStack.push({ id:         p_id,
2121                 fn:         p_fn,
2122                 obj:        p_obj,
2123                 override:   p_override,
2124                 checkReady: false    });
2125
2126             retryCount = this.POLL_RETRYS;
2127             this.startInterval();
2128         },
2129
2130
2131         addListener: function(el, eventName, fn) {
2132             el = Roo.getDom(el);
2133             if (!el || !fn) {
2134                 return false;
2135             }
2136
2137             if ("unload" == eventName) {
2138                 unloadListeners[unloadListeners.length] =
2139                 [el, eventName, fn];
2140                 return true;
2141             }
2142
2143             var wrappedFn = function(e) {
2144                 return fn(Roo.lib.Event.getEvent(e));
2145             };
2146
2147             var li = [el, eventName, fn, wrappedFn];
2148
2149             var index = listeners.length;
2150             listeners[index] = li;
2151
2152             this.doAdd(el, eventName, wrappedFn, false);
2153             return true;
2154
2155         },
2156
2157
2158         removeListener: function(el, eventName, fn) {
2159             var i, len;
2160
2161             el = Roo.getDom(el);
2162
2163             if(!fn) {
2164                 return this.purgeElement(el, false, eventName);
2165             }
2166
2167
2168             if ("unload" == eventName) {
2169
2170                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2171                     var li = unloadListeners[i];
2172                     if (li &&
2173                         li[0] == el &&
2174                         li[1] == eventName &&
2175                         li[2] == fn) {
2176                         unloadListeners.splice(i, 1);
2177                         return true;
2178                     }
2179                 }
2180
2181                 return false;
2182             }
2183
2184             var cacheItem = null;
2185
2186
2187             var index = arguments[3];
2188
2189             if ("undefined" == typeof index) {
2190                 index = this._getCacheIndex(el, eventName, fn);
2191             }
2192
2193             if (index >= 0) {
2194                 cacheItem = listeners[index];
2195             }
2196
2197             if (!el || !cacheItem) {
2198                 return false;
2199             }
2200
2201             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2202
2203             delete listeners[index][this.WFN];
2204             delete listeners[index][this.FN];
2205             listeners.splice(index, 1);
2206
2207             return true;
2208
2209         },
2210
2211
2212         getTarget: function(ev, resolveTextNode) {
2213             ev = ev.browserEvent || ev;
2214             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2215             var t = ev.target || ev.srcElement;
2216             return this.resolveTextNode(t);
2217         },
2218
2219
2220         resolveTextNode: function(node) {
2221             if (Roo.isSafari && node && 3 == node.nodeType) {
2222                 return node.parentNode;
2223             } else {
2224                 return node;
2225             }
2226         },
2227
2228
2229         getPageX: function(ev) {
2230             ev = ev.browserEvent || ev;
2231             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2232             var x = ev.pageX;
2233             if (!x && 0 !== x) {
2234                 x = ev.clientX || 0;
2235
2236                 if (Roo.isIE) {
2237                     x += this.getScroll()[1];
2238                 }
2239             }
2240
2241             return x;
2242         },
2243
2244
2245         getPageY: function(ev) {
2246             ev = ev.browserEvent || ev;
2247             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2248             var y = ev.pageY;
2249             if (!y && 0 !== y) {
2250                 y = ev.clientY || 0;
2251
2252                 if (Roo.isIE) {
2253                     y += this.getScroll()[0];
2254                 }
2255             }
2256
2257
2258             return y;
2259         },
2260
2261
2262         getXY: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             return [this.getPageX(ev), this.getPageY(ev)];
2266         },
2267
2268
2269         getRelatedTarget: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2272             var t = ev.relatedTarget;
2273             if (!t) {
2274                 if (ev.type == "mouseout") {
2275                     t = ev.toElement;
2276                 } else if (ev.type == "mouseover") {
2277                     t = ev.fromElement;
2278                 }
2279             }
2280
2281             return this.resolveTextNode(t);
2282         },
2283
2284
2285         getTime: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             if (!ev.time) {
2289                 var t = new Date().getTime();
2290                 try {
2291                     ev.time = t;
2292                 } catch(ex) {
2293                     this.lastError = ex;
2294                     return t;
2295                 }
2296             }
2297
2298             return ev.time;
2299         },
2300
2301
2302         stopEvent: function(ev) {
2303             this.stopPropagation(ev);
2304             this.preventDefault(ev);
2305         },
2306
2307
2308         stopPropagation: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             if (ev.stopPropagation) {
2311                 ev.stopPropagation();
2312             } else {
2313                 ev.cancelBubble = true;
2314             }
2315         },
2316
2317
2318         preventDefault: function(ev) {
2319             ev = ev.browserEvent || ev;
2320             if(ev.preventDefault) {
2321                 ev.preventDefault();
2322             } else {
2323                 ev.returnValue = false;
2324             }
2325         },
2326
2327
2328         getEvent: function(e) {
2329             var ev = e || window.event;
2330             if (!ev) {
2331                 var c = this.getEvent.caller;
2332                 while (c) {
2333                     ev = c.arguments[0];
2334                     if (ev && Event == ev.constructor) {
2335                         break;
2336                     }
2337                     c = c.caller;
2338                 }
2339             }
2340             return ev;
2341         },
2342
2343
2344         getCharCode: function(ev) {
2345             ev = ev.browserEvent || ev;
2346             return ev.charCode || ev.keyCode || 0;
2347         },
2348
2349
2350         _getCacheIndex: function(el, eventName, fn) {
2351             for (var i = 0,len = listeners.length; i < len; ++i) {
2352                 var li = listeners[i];
2353                 if (li &&
2354                     li[this.FN] == fn &&
2355                     li[this.EL] == el &&
2356                     li[this.TYPE] == eventName) {
2357                     return i;
2358                 }
2359             }
2360
2361             return -1;
2362         },
2363
2364
2365         elCache: {},
2366
2367
2368         getEl: function(id) {
2369             return document.getElementById(id);
2370         },
2371
2372
2373         clearCache: function() {
2374         },
2375
2376
2377         _load: function(e) {
2378             loadComplete = true;
2379             var EU = Roo.lib.Event;
2380
2381
2382             if (Roo.isIE) {
2383                 EU.doRemove(window, "load", EU._load);
2384             }
2385         },
2386
2387
2388         _tryPreloadAttach: function() {
2389
2390             if (this.locked) {
2391                 return false;
2392             }
2393
2394             this.locked = true;
2395
2396
2397             var tryAgain = !loadComplete;
2398             if (!tryAgain) {
2399                 tryAgain = (retryCount > 0);
2400             }
2401
2402
2403             var notAvail = [];
2404             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2405                 var item = onAvailStack[i];
2406                 if (item) {
2407                     var el = this.getEl(item.id);
2408
2409                     if (el) {
2410                         if (!item.checkReady ||
2411                             loadComplete ||
2412                             el.nextSibling ||
2413                             (document && document.body)) {
2414
2415                             var scope = el;
2416                             if (item.override) {
2417                                 if (item.override === true) {
2418                                     scope = item.obj;
2419                                 } else {
2420                                     scope = item.override;
2421                                 }
2422                             }
2423                             item.fn.call(scope, item.obj);
2424                             onAvailStack[i] = null;
2425                         }
2426                     } else {
2427                         notAvail.push(item);
2428                     }
2429                 }
2430             }
2431
2432             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2433
2434             if (tryAgain) {
2435
2436                 this.startInterval();
2437             } else {
2438                 clearInterval(this._interval);
2439                 this._interval = null;
2440             }
2441
2442             this.locked = false;
2443
2444             return true;
2445
2446         },
2447
2448
2449         purgeElement: function(el, recurse, eventName) {
2450             var elListeners = this.getListeners(el, eventName);
2451             if (elListeners) {
2452                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2453                     var l = elListeners[i];
2454                     this.removeListener(el, l.type, l.fn);
2455                 }
2456             }
2457
2458             if (recurse && el && el.childNodes) {
2459                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2460                     this.purgeElement(el.childNodes[i], recurse, eventName);
2461                 }
2462             }
2463         },
2464
2465
2466         getListeners: function(el, eventName) {
2467             var results = [], searchLists;
2468             if (!eventName) {
2469                 searchLists = [listeners, unloadListeners];
2470             } else if (eventName == "unload") {
2471                 searchLists = [unloadListeners];
2472             } else {
2473                 searchLists = [listeners];
2474             }
2475
2476             for (var j = 0; j < searchLists.length; ++j) {
2477                 var searchList = searchLists[j];
2478                 if (searchList && searchList.length > 0) {
2479                     for (var i = 0,len = searchList.length; i < len; ++i) {
2480                         var l = searchList[i];
2481                         if (l && l[this.EL] === el &&
2482                             (!eventName || eventName === l[this.TYPE])) {
2483                             results.push({
2484                                 type:   l[this.TYPE],
2485                                 fn:     l[this.FN],
2486                                 obj:    l[this.OBJ],
2487                                 adjust: l[this.ADJ_SCOPE],
2488                                 index:  i
2489                             });
2490                         }
2491                     }
2492                 }
2493             }
2494
2495             return (results.length) ? results : null;
2496         },
2497
2498
2499         _unload: function(e) {
2500
2501             var EU = Roo.lib.Event, i, j, l, len, index;
2502
2503             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2504                 l = unloadListeners[i];
2505                 if (l) {
2506                     var scope = window;
2507                     if (l[EU.ADJ_SCOPE]) {
2508                         if (l[EU.ADJ_SCOPE] === true) {
2509                             scope = l[EU.OBJ];
2510                         } else {
2511                             scope = l[EU.ADJ_SCOPE];
2512                         }
2513                     }
2514                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2515                     unloadListeners[i] = null;
2516                     l = null;
2517                     scope = null;
2518                 }
2519             }
2520
2521             unloadListeners = null;
2522
2523             if (listeners && listeners.length > 0) {
2524                 j = listeners.length;
2525                 while (j) {
2526                     index = j - 1;
2527                     l = listeners[index];
2528                     if (l) {
2529                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2530                                 l[EU.FN], index);
2531                     }
2532                     j = j - 1;
2533                 }
2534                 l = null;
2535
2536                 EU.clearCache();
2537             }
2538
2539             EU.doRemove(window, "unload", EU._unload);
2540
2541         },
2542
2543
2544         getScroll: function() {
2545             var dd = document.documentElement, db = document.body;
2546             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2547                 return [dd.scrollTop, dd.scrollLeft];
2548             } else if (db) {
2549                 return [db.scrollTop, db.scrollLeft];
2550             } else {
2551                 return [0, 0];
2552             }
2553         },
2554
2555
2556         doAdd: function () {
2557             if (window.addEventListener) {
2558                 return function(el, eventName, fn, capture) {
2559                     el.addEventListener(eventName, fn, (capture));
2560                 };
2561             } else if (window.attachEvent) {
2562                 return function(el, eventName, fn, capture) {
2563                     el.attachEvent("on" + eventName, fn);
2564                 };
2565             } else {
2566                 return function() {
2567                 };
2568             }
2569         }(),
2570
2571
2572         doRemove: function() {
2573             if (window.removeEventListener) {
2574                 return function (el, eventName, fn, capture) {
2575                     el.removeEventListener(eventName, fn, (capture));
2576                 };
2577             } else if (window.detachEvent) {
2578                 return function (el, eventName, fn) {
2579                     el.detachEvent("on" + eventName, fn);
2580                 };
2581             } else {
2582                 return function() {
2583                 };
2584             }
2585         }()
2586     };
2587     
2588 }();
2589 (function() {     
2590    
2591     var E = Roo.lib.Event;
2592     E.on = E.addListener;
2593     E.un = E.removeListener;
2594
2595     if (document && document.body) {
2596         E._load();
2597     } else {
2598         E.doAdd(window, "load", E._load);
2599     }
2600     E.doAdd(window, "unload", E._unload);
2601     E._tryPreloadAttach();
2602 })();
2603
2604 /*
2605  * Portions of this file are based on pieces of Yahoo User Interface Library
2606  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2607  * YUI licensed under the BSD License:
2608  * http://developer.yahoo.net/yui/license.txt
2609  * <script type="text/javascript">
2610  *
2611  */
2612
2613 (function() {
2614     /**
2615      * @class Roo.lib.Ajax
2616      *
2617      */
2618     Roo.lib.Ajax = {
2619         /**
2620          * @static 
2621          */
2622         request : function(method, uri, cb, data, options) {
2623             if(options){
2624                 var hs = options.headers;
2625                 if(hs){
2626                     for(var h in hs){
2627                         if(hs.hasOwnProperty(h)){
2628                             this.initHeader(h, hs[h], false);
2629                         }
2630                     }
2631                 }
2632                 if(options.xmlData){
2633                     this.initHeader('Content-Type', 'text/xml', false);
2634                     method = 'POST';
2635                     data = options.xmlData;
2636                 }
2637             }
2638
2639             return this.asyncRequest(method, uri, cb, data);
2640         },
2641
2642         serializeForm : function(form) {
2643             if(typeof form == 'string') {
2644                 form = (document.getElementById(form) || document.forms[form]);
2645             }
2646
2647             var el, name, val, disabled, data = '', hasSubmit = false;
2648             for (var i = 0; i < form.elements.length; i++) {
2649                 el = form.elements[i];
2650                 disabled = form.elements[i].disabled;
2651                 name = form.elements[i].name;
2652                 val = form.elements[i].value;
2653
2654                 if (!disabled && name){
2655                     switch (el.type)
2656                             {
2657                         case 'select-one':
2658                         case 'select-multiple':
2659                             for (var j = 0; j < el.options.length; j++) {
2660                                 if (el.options[j].selected) {
2661                                     if (Roo.isIE) {
2662                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2663                                     }
2664                                     else {
2665                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2666                                     }
2667                                 }
2668                             }
2669                             break;
2670                         case 'radio':
2671                         case 'checkbox':
2672                             if (el.checked) {
2673                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2674                             }
2675                             break;
2676                         case 'file':
2677
2678                         case undefined:
2679
2680                         case 'reset':
2681
2682                         case 'button':
2683
2684                             break;
2685                         case 'submit':
2686                             if(hasSubmit == false) {
2687                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2688                                 hasSubmit = true;
2689                             }
2690                             break;
2691                         default:
2692                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2693                             break;
2694                     }
2695                 }
2696             }
2697             data = data.substr(0, data.length - 1);
2698             return data;
2699         },
2700
2701         headers:{},
2702
2703         hasHeaders:false,
2704
2705         useDefaultHeader:true,
2706
2707         defaultPostHeader:'application/x-www-form-urlencoded',
2708
2709         useDefaultXhrHeader:true,
2710
2711         defaultXhrHeader:'XMLHttpRequest',
2712
2713         hasDefaultHeaders:true,
2714
2715         defaultHeaders:{},
2716
2717         poll:{},
2718
2719         timeout:{},
2720
2721         pollInterval:50,
2722
2723         transactionId:0,
2724
2725         setProgId:function(id)
2726         {
2727             this.activeX.unshift(id);
2728         },
2729
2730         setDefaultPostHeader:function(b)
2731         {
2732             this.useDefaultHeader = b;
2733         },
2734
2735         setDefaultXhrHeader:function(b)
2736         {
2737             this.useDefaultXhrHeader = b;
2738         },
2739
2740         setPollingInterval:function(i)
2741         {
2742             if (typeof i == 'number' && isFinite(i)) {
2743                 this.pollInterval = i;
2744             }
2745         },
2746
2747         createXhrObject:function(transactionId)
2748         {
2749             var obj,http;
2750             try
2751             {
2752
2753                 http = new XMLHttpRequest();
2754
2755                 obj = { conn:http, tId:transactionId };
2756             }
2757             catch(e)
2758             {
2759                 for (var i = 0; i < this.activeX.length; ++i) {
2760                     try
2761                     {
2762
2763                         http = new ActiveXObject(this.activeX[i]);
2764
2765                         obj = { conn:http, tId:transactionId };
2766                         break;
2767                     }
2768                     catch(e) {
2769                     }
2770                 }
2771             }
2772             finally
2773             {
2774                 return obj;
2775             }
2776         },
2777
2778         getConnectionObject:function()
2779         {
2780             var o;
2781             var tId = this.transactionId;
2782
2783             try
2784             {
2785                 o = this.createXhrObject(tId);
2786                 if (o) {
2787                     this.transactionId++;
2788                 }
2789             }
2790             catch(e) {
2791             }
2792             finally
2793             {
2794                 return o;
2795             }
2796         },
2797
2798         asyncRequest:function(method, uri, callback, postData)
2799         {
2800             var o = this.getConnectionObject();
2801
2802             if (!o) {
2803                 return null;
2804             }
2805             else {
2806                 o.conn.open(method, uri, true);
2807
2808                 if (this.useDefaultXhrHeader) {
2809                     if (!this.defaultHeaders['X-Requested-With']) {
2810                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2811                     }
2812                 }
2813
2814                 if(postData && this.useDefaultHeader){
2815                     this.initHeader('Content-Type', this.defaultPostHeader);
2816                 }
2817
2818                  if (this.hasDefaultHeaders || this.hasHeaders) {
2819                     this.setHeader(o);
2820                 }
2821
2822                 this.handleReadyState(o, callback);
2823                 o.conn.send(postData || null);
2824
2825                 return o;
2826             }
2827         },
2828
2829         handleReadyState:function(o, callback)
2830         {
2831             var oConn = this;
2832
2833             if (callback && callback.timeout) {
2834                 
2835                 this.timeout[o.tId] = window.setTimeout(function() {
2836                     oConn.abort(o, callback, true);
2837                 }, callback.timeout);
2838             }
2839
2840             this.poll[o.tId] = window.setInterval(
2841                     function() {
2842                         if (o.conn && o.conn.readyState == 4) {
2843                             window.clearInterval(oConn.poll[o.tId]);
2844                             delete oConn.poll[o.tId];
2845
2846                             if(callback && callback.timeout) {
2847                                 window.clearTimeout(oConn.timeout[o.tId]);
2848                                 delete oConn.timeout[o.tId];
2849                             }
2850
2851                             oConn.handleTransactionResponse(o, callback);
2852                         }
2853                     }
2854                     , this.pollInterval);
2855         },
2856
2857         handleTransactionResponse:function(o, callback, isAbort)
2858         {
2859
2860             if (!callback) {
2861                 this.releaseObject(o);
2862                 return;
2863             }
2864
2865             var httpStatus, responseObject;
2866
2867             try
2868             {
2869                 if (o.conn.status !== undefined && o.conn.status != 0) {
2870                     httpStatus = o.conn.status;
2871                 }
2872                 else {
2873                     httpStatus = 13030;
2874                 }
2875             }
2876             catch(e) {
2877
2878
2879                 httpStatus = 13030;
2880             }
2881
2882             if (httpStatus >= 200 && httpStatus < 300) {
2883                 responseObject = this.createResponseObject(o, callback.argument);
2884                 if (callback.success) {
2885                     if (!callback.scope) {
2886                         callback.success(responseObject);
2887                     }
2888                     else {
2889
2890
2891                         callback.success.apply(callback.scope, [responseObject]);
2892                     }
2893                 }
2894             }
2895             else {
2896                 switch (httpStatus) {
2897
2898                     case 12002:
2899                     case 12029:
2900                     case 12030:
2901                     case 12031:
2902                     case 12152:
2903                     case 13030:
2904                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2905                         if (callback.failure) {
2906                             if (!callback.scope) {
2907                                 callback.failure(responseObject);
2908                             }
2909                             else {
2910                                 callback.failure.apply(callback.scope, [responseObject]);
2911                             }
2912                         }
2913                         break;
2914                     default:
2915                         responseObject = this.createResponseObject(o, callback.argument);
2916                         if (callback.failure) {
2917                             if (!callback.scope) {
2918                                 callback.failure(responseObject);
2919                             }
2920                             else {
2921                                 callback.failure.apply(callback.scope, [responseObject]);
2922                             }
2923                         }
2924                 }
2925             }
2926
2927             this.releaseObject(o);
2928             responseObject = null;
2929         },
2930
2931         createResponseObject:function(o, callbackArg)
2932         {
2933             var obj = {};
2934             var headerObj = {};
2935
2936             try
2937             {
2938                 var headerStr = o.conn.getAllResponseHeaders();
2939                 var header = headerStr.split('\n');
2940                 for (var i = 0; i < header.length; i++) {
2941                     var delimitPos = header[i].indexOf(':');
2942                     if (delimitPos != -1) {
2943                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2944                     }
2945                 }
2946             }
2947             catch(e) {
2948             }
2949
2950             obj.tId = o.tId;
2951             obj.status = o.conn.status;
2952             obj.statusText = o.conn.statusText;
2953             obj.getResponseHeader = headerObj;
2954             obj.getAllResponseHeaders = headerStr;
2955             obj.responseText = o.conn.responseText;
2956             obj.responseXML = o.conn.responseXML;
2957
2958             if (typeof callbackArg !== undefined) {
2959                 obj.argument = callbackArg;
2960             }
2961
2962             return obj;
2963         },
2964
2965         createExceptionObject:function(tId, callbackArg, isAbort)
2966         {
2967             var COMM_CODE = 0;
2968             var COMM_ERROR = 'communication failure';
2969             var ABORT_CODE = -1;
2970             var ABORT_ERROR = 'transaction aborted';
2971
2972             var obj = {};
2973
2974             obj.tId = tId;
2975             if (isAbort) {
2976                 obj.status = ABORT_CODE;
2977                 obj.statusText = ABORT_ERROR;
2978             }
2979             else {
2980                 obj.status = COMM_CODE;
2981                 obj.statusText = COMM_ERROR;
2982             }
2983
2984             if (callbackArg) {
2985                 obj.argument = callbackArg;
2986             }
2987
2988             return obj;
2989         },
2990
2991         initHeader:function(label, value, isDefault)
2992         {
2993             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2994
2995             if (headerObj[label] === undefined) {
2996                 headerObj[label] = value;
2997             }
2998             else {
2999
3000
3001                 headerObj[label] = value + "," + headerObj[label];
3002             }
3003
3004             if (isDefault) {
3005                 this.hasDefaultHeaders = true;
3006             }
3007             else {
3008                 this.hasHeaders = true;
3009             }
3010         },
3011
3012
3013         setHeader:function(o)
3014         {
3015             if (this.hasDefaultHeaders) {
3016                 for (var prop in this.defaultHeaders) {
3017                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3018                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3019                     }
3020                 }
3021             }
3022
3023             if (this.hasHeaders) {
3024                 for (var prop in this.headers) {
3025                     if (this.headers.hasOwnProperty(prop)) {
3026                         o.conn.setRequestHeader(prop, this.headers[prop]);
3027                     }
3028                 }
3029                 this.headers = {};
3030                 this.hasHeaders = false;
3031             }
3032         },
3033
3034         resetDefaultHeaders:function() {
3035             delete this.defaultHeaders;
3036             this.defaultHeaders = {};
3037             this.hasDefaultHeaders = false;
3038         },
3039
3040         abort:function(o, callback, isTimeout)
3041         {
3042             if(this.isCallInProgress(o)) {
3043                 o.conn.abort();
3044                 window.clearInterval(this.poll[o.tId]);
3045                 delete this.poll[o.tId];
3046                 if (isTimeout) {
3047                     delete this.timeout[o.tId];
3048                 }
3049
3050                 this.handleTransactionResponse(o, callback, true);
3051
3052                 return true;
3053             }
3054             else {
3055                 return false;
3056             }
3057         },
3058
3059
3060         isCallInProgress:function(o)
3061         {
3062             if (o && o.conn) {
3063                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3064             }
3065             else {
3066
3067                 return false;
3068             }
3069         },
3070
3071
3072         releaseObject:function(o)
3073         {
3074
3075             o.conn = null;
3076
3077             o = null;
3078         },
3079
3080         activeX:[
3081         'MSXML2.XMLHTTP.3.0',
3082         'MSXML2.XMLHTTP',
3083         'Microsoft.XMLHTTP'
3084         ]
3085
3086
3087     };
3088 })();/*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096
3097 Roo.lib.Region = function(t, r, b, l) {
3098     this.top = t;
3099     this[1] = t;
3100     this.right = r;
3101     this.bottom = b;
3102     this.left = l;
3103     this[0] = l;
3104 };
3105
3106
3107 Roo.lib.Region.prototype = {
3108     contains : function(region) {
3109         return ( region.left >= this.left &&
3110                  region.right <= this.right &&
3111                  region.top >= this.top &&
3112                  region.bottom <= this.bottom    );
3113
3114     },
3115
3116     getArea : function() {
3117         return ( (this.bottom - this.top) * (this.right - this.left) );
3118     },
3119
3120     intersect : function(region) {
3121         var t = Math.max(this.top, region.top);
3122         var r = Math.min(this.right, region.right);
3123         var b = Math.min(this.bottom, region.bottom);
3124         var l = Math.max(this.left, region.left);
3125
3126         if (b >= t && r >= l) {
3127             return new Roo.lib.Region(t, r, b, l);
3128         } else {
3129             return null;
3130         }
3131     },
3132     union : function(region) {
3133         var t = Math.min(this.top, region.top);
3134         var r = Math.max(this.right, region.right);
3135         var b = Math.max(this.bottom, region.bottom);
3136         var l = Math.min(this.left, region.left);
3137
3138         return new Roo.lib.Region(t, r, b, l);
3139     },
3140
3141     adjust : function(t, l, b, r) {
3142         this.top += t;
3143         this.left += l;
3144         this.right += r;
3145         this.bottom += b;
3146         return this;
3147     }
3148 };
3149
3150 Roo.lib.Region.getRegion = function(el) {
3151     var p = Roo.lib.Dom.getXY(el);
3152
3153     var t = p[1];
3154     var r = p[0] + el.offsetWidth;
3155     var b = p[1] + el.offsetHeight;
3156     var l = p[0];
3157
3158     return new Roo.lib.Region(t, r, b, l);
3159 };
3160 /*
3161  * Portions of this file are based on pieces of Yahoo User Interface Library
3162  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3163  * YUI licensed under the BSD License:
3164  * http://developer.yahoo.net/yui/license.txt
3165  * <script type="text/javascript">
3166  *
3167  */
3168 //@@dep Roo.lib.Region
3169
3170
3171 Roo.lib.Point = function(x, y) {
3172     if (x instanceof Array) {
3173         y = x[1];
3174         x = x[0];
3175     }
3176     this.x = this.right = this.left = this[0] = x;
3177     this.y = this.top = this.bottom = this[1] = y;
3178 };
3179
3180 Roo.lib.Point.prototype = new Roo.lib.Region();
3181 /*
3182  * Portions of this file are based on pieces of Yahoo User Interface Library
3183  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3184  * YUI licensed under the BSD License:
3185  * http://developer.yahoo.net/yui/license.txt
3186  * <script type="text/javascript">
3187  *
3188  */
3189  
3190 (function() {   
3191
3192     Roo.lib.Anim = {
3193         scroll : function(el, args, duration, easing, cb, scope) {
3194             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3195         },
3196
3197         motion : function(el, args, duration, easing, cb, scope) {
3198             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3199         },
3200
3201         color : function(el, args, duration, easing, cb, scope) {
3202             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3203         },
3204
3205         run : function(el, args, duration, easing, cb, scope, type) {
3206             type = type || Roo.lib.AnimBase;
3207             if (typeof easing == "string") {
3208                 easing = Roo.lib.Easing[easing];
3209             }
3210             var anim = new type(el, args, duration, easing);
3211             anim.animateX(function() {
3212                 Roo.callback(cb, scope);
3213             });
3214             return anim;
3215         }
3216     };
3217 })();/*
3218  * Portions of this file are based on pieces of Yahoo User Interface Library
3219  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3220  * YUI licensed under the BSD License:
3221  * http://developer.yahoo.net/yui/license.txt
3222  * <script type="text/javascript">
3223  *
3224  */
3225
3226 (function() {    
3227     var libFlyweight;
3228     
3229     function fly(el) {
3230         if (!libFlyweight) {
3231             libFlyweight = new Roo.Element.Flyweight();
3232         }
3233         libFlyweight.dom = el;
3234         return libFlyweight;
3235     }
3236
3237     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3238     
3239    
3240     
3241     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3242         if (el) {
3243             this.init(el, attributes, duration, method);
3244         }
3245     };
3246
3247     Roo.lib.AnimBase.fly = fly;
3248     
3249     
3250     
3251     Roo.lib.AnimBase.prototype = {
3252
3253         toString: function() {
3254             var el = this.getEl();
3255             var id = el.id || el.tagName;
3256             return ("Anim " + id);
3257         },
3258
3259         patterns: {
3260             noNegatives:        /width|height|opacity|padding/i,
3261             offsetAttribute:  /^((width|height)|(top|left))$/,
3262             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3263             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3264         },
3265
3266
3267         doMethod: function(attr, start, end) {
3268             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3269         },
3270
3271
3272         setAttribute: function(attr, val, unit) {
3273             if (this.patterns.noNegatives.test(attr)) {
3274                 val = (val > 0) ? val : 0;
3275             }
3276
3277             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3278         },
3279
3280
3281         getAttribute: function(attr) {
3282             var el = this.getEl();
3283             var val = fly(el).getStyle(attr);
3284
3285             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3286                 return parseFloat(val);
3287             }
3288
3289             var a = this.patterns.offsetAttribute.exec(attr) || [];
3290             var pos = !!( a[3] );
3291             var box = !!( a[2] );
3292
3293
3294             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3295                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3296             } else {
3297                 val = 0;
3298             }
3299
3300             return val;
3301         },
3302
3303
3304         getDefaultUnit: function(attr) {
3305             if (this.patterns.defaultUnit.test(attr)) {
3306                 return 'px';
3307             }
3308
3309             return '';
3310         },
3311
3312         animateX : function(callback, scope) {
3313             var f = function() {
3314                 this.onComplete.removeListener(f);
3315                 if (typeof callback == "function") {
3316                     callback.call(scope || this, this);
3317                 }
3318             };
3319             this.onComplete.addListener(f, this);
3320             this.animate();
3321         },
3322
3323
3324         setRuntimeAttribute: function(attr) {
3325             var start;
3326             var end;
3327             var attributes = this.attributes;
3328
3329             this.runtimeAttributes[attr] = {};
3330
3331             var isset = function(prop) {
3332                 return (typeof prop !== 'undefined');
3333             };
3334
3335             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3336                 return false;
3337             }
3338
3339             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3340
3341
3342             if (isset(attributes[attr]['to'])) {
3343                 end = attributes[attr]['to'];
3344             } else if (isset(attributes[attr]['by'])) {
3345                 if (start.constructor == Array) {
3346                     end = [];
3347                     for (var i = 0, len = start.length; i < len; ++i) {
3348                         end[i] = start[i] + attributes[attr]['by'][i];
3349                     }
3350                 } else {
3351                     end = start + attributes[attr]['by'];
3352                 }
3353             }
3354
3355             this.runtimeAttributes[attr].start = start;
3356             this.runtimeAttributes[attr].end = end;
3357
3358
3359             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3360         },
3361
3362
3363         init: function(el, attributes, duration, method) {
3364
3365             var isAnimated = false;
3366
3367
3368             var startTime = null;
3369
3370
3371             var actualFrames = 0;
3372
3373
3374             el = Roo.getDom(el);
3375
3376
3377             this.attributes = attributes || {};
3378
3379
3380             this.duration = duration || 1;
3381
3382
3383             this.method = method || Roo.lib.Easing.easeNone;
3384
3385
3386             this.useSeconds = true;
3387
3388
3389             this.currentFrame = 0;
3390
3391
3392             this.totalFrames = Roo.lib.AnimMgr.fps;
3393
3394
3395             this.getEl = function() {
3396                 return el;
3397             };
3398
3399
3400             this.isAnimated = function() {
3401                 return isAnimated;
3402             };
3403
3404
3405             this.getStartTime = function() {
3406                 return startTime;
3407             };
3408
3409             this.runtimeAttributes = {};
3410
3411
3412             this.animate = function() {
3413                 if (this.isAnimated()) {
3414                     return false;
3415                 }
3416
3417                 this.currentFrame = 0;
3418
3419                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3420
3421                 Roo.lib.AnimMgr.registerElement(this);
3422             };
3423
3424
3425             this.stop = function(finish) {
3426                 if (finish) {
3427                     this.currentFrame = this.totalFrames;
3428                     this._onTween.fire();
3429                 }
3430                 Roo.lib.AnimMgr.stop(this);
3431             };
3432
3433             var onStart = function() {
3434                 this.onStart.fire();
3435
3436                 this.runtimeAttributes = {};
3437                 for (var attr in this.attributes) {
3438                     this.setRuntimeAttribute(attr);
3439                 }
3440
3441                 isAnimated = true;
3442                 actualFrames = 0;
3443                 startTime = new Date();
3444             };
3445
3446
3447             var onTween = function() {
3448                 var data = {
3449                     duration: new Date() - this.getStartTime(),
3450                     currentFrame: this.currentFrame
3451                 };
3452
3453                 data.toString = function() {
3454                     return (
3455                             'duration: ' + data.duration +
3456                             ', currentFrame: ' + data.currentFrame
3457                             );
3458                 };
3459
3460                 this.onTween.fire(data);
3461
3462                 var runtimeAttributes = this.runtimeAttributes;
3463
3464                 for (var attr in runtimeAttributes) {
3465                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3466                 }
3467
3468                 actualFrames += 1;
3469             };
3470
3471             var onComplete = function() {
3472                 var actual_duration = (new Date() - startTime) / 1000 ;
3473
3474                 var data = {
3475                     duration: actual_duration,
3476                     frames: actualFrames,
3477                     fps: actualFrames / actual_duration
3478                 };
3479
3480                 data.toString = function() {
3481                     return (
3482                             'duration: ' + data.duration +
3483                             ', frames: ' + data.frames +
3484                             ', fps: ' + data.fps
3485                             );
3486                 };
3487
3488                 isAnimated = false;
3489                 actualFrames = 0;
3490                 this.onComplete.fire(data);
3491             };
3492
3493
3494             this._onStart = new Roo.util.Event(this);
3495             this.onStart = new Roo.util.Event(this);
3496             this.onTween = new Roo.util.Event(this);
3497             this._onTween = new Roo.util.Event(this);
3498             this.onComplete = new Roo.util.Event(this);
3499             this._onComplete = new Roo.util.Event(this);
3500             this._onStart.addListener(onStart);
3501             this._onTween.addListener(onTween);
3502             this._onComplete.addListener(onComplete);
3503         }
3504     };
3505 })();
3506 /*
3507  * Portions of this file are based on pieces of Yahoo User Interface Library
3508  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3509  * YUI licensed under the BSD License:
3510  * http://developer.yahoo.net/yui/license.txt
3511  * <script type="text/javascript">
3512  *
3513  */
3514
3515 Roo.lib.AnimMgr = new function() {
3516
3517     var thread = null;
3518
3519
3520     var queue = [];
3521
3522
3523     var tweenCount = 0;
3524
3525
3526     this.fps = 1000;
3527
3528
3529     this.delay = 1;
3530
3531
3532     this.registerElement = function(tween) {
3533         queue[queue.length] = tween;
3534         tweenCount += 1;
3535         tween._onStart.fire();
3536         this.start();
3537     };
3538
3539
3540     this.unRegister = function(tween, index) {
3541         tween._onComplete.fire();
3542         index = index || getIndex(tween);
3543         if (index != -1) {
3544             queue.splice(index, 1);
3545         }
3546
3547         tweenCount -= 1;
3548         if (tweenCount <= 0) {
3549             this.stop();
3550         }
3551     };
3552
3553
3554     this.start = function() {
3555         if (thread === null) {
3556             thread = setInterval(this.run, this.delay);
3557         }
3558     };
3559
3560
3561     this.stop = function(tween) {
3562         if (!tween) {
3563             clearInterval(thread);
3564
3565             for (var i = 0, len = queue.length; i < len; ++i) {
3566                 if (queue[0].isAnimated()) {
3567                     this.unRegister(queue[0], 0);
3568                 }
3569             }
3570
3571             queue = [];
3572             thread = null;
3573             tweenCount = 0;
3574         }
3575         else {
3576             this.unRegister(tween);
3577         }
3578     };
3579
3580
3581     this.run = function() {
3582         for (var i = 0, len = queue.length; i < len; ++i) {
3583             var tween = queue[i];
3584             if (!tween || !tween.isAnimated()) {
3585                 continue;
3586             }
3587
3588             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3589             {
3590                 tween.currentFrame += 1;
3591
3592                 if (tween.useSeconds) {
3593                     correctFrame(tween);
3594                 }
3595                 tween._onTween.fire();
3596             }
3597             else {
3598                 Roo.lib.AnimMgr.stop(tween, i);
3599             }
3600         }
3601     };
3602
3603     var getIndex = function(anim) {
3604         for (var i = 0, len = queue.length; i < len; ++i) {
3605             if (queue[i] == anim) {
3606                 return i;
3607             }
3608         }
3609         return -1;
3610     };
3611
3612
3613     var correctFrame = function(tween) {
3614         var frames = tween.totalFrames;
3615         var frame = tween.currentFrame;
3616         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3617         var elapsed = (new Date() - tween.getStartTime());
3618         var tweak = 0;
3619
3620         if (elapsed < tween.duration * 1000) {
3621             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3622         } else {
3623             tweak = frames - (frame + 1);
3624         }
3625         if (tweak > 0 && isFinite(tweak)) {
3626             if (tween.currentFrame + tweak >= frames) {
3627                 tweak = frames - (frame + 1);
3628             }
3629
3630             tween.currentFrame += tweak;
3631         }
3632     };
3633 };
3634
3635     /*
3636  * Portions of this file are based on pieces of Yahoo User Interface Library
3637  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3638  * YUI licensed under the BSD License:
3639  * http://developer.yahoo.net/yui/license.txt
3640  * <script type="text/javascript">
3641  *
3642  */
3643 Roo.lib.Bezier = new function() {
3644
3645         this.getPosition = function(points, t) {
3646             var n = points.length;
3647             var tmp = [];
3648
3649             for (var i = 0; i < n; ++i) {
3650                 tmp[i] = [points[i][0], points[i][1]];
3651             }
3652
3653             for (var j = 1; j < n; ++j) {
3654                 for (i = 0; i < n - j; ++i) {
3655                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3656                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3657                 }
3658             }
3659
3660             return [ tmp[0][0], tmp[0][1] ];
3661
3662         };
3663     };/*
3664  * Portions of this file are based on pieces of Yahoo User Interface Library
3665  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3666  * YUI licensed under the BSD License:
3667  * http://developer.yahoo.net/yui/license.txt
3668  * <script type="text/javascript">
3669  *
3670  */
3671 (function() {
3672
3673     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3674         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3675     };
3676
3677     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3678
3679     var fly = Roo.lib.AnimBase.fly;
3680     var Y = Roo.lib;
3681     var superclass = Y.ColorAnim.superclass;
3682     var proto = Y.ColorAnim.prototype;
3683
3684     proto.toString = function() {
3685         var el = this.getEl();
3686         var id = el.id || el.tagName;
3687         return ("ColorAnim " + id);
3688     };
3689
3690     proto.patterns.color = /color$/i;
3691     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3692     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3693     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3694     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3695
3696
3697     proto.parseColor = function(s) {
3698         if (s.length == 3) {
3699             return s;
3700         }
3701
3702         var c = this.patterns.hex.exec(s);
3703         if (c && c.length == 4) {
3704             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3705         }
3706
3707         c = this.patterns.rgb.exec(s);
3708         if (c && c.length == 4) {
3709             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3710         }
3711
3712         c = this.patterns.hex3.exec(s);
3713         if (c && c.length == 4) {
3714             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3715         }
3716
3717         return null;
3718     };
3719     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743     proto.getAttribute = function(attr) {
3744         var el = this.getEl();
3745         if (this.patterns.color.test(attr)) {
3746             var val = fly(el).getStyle(attr);
3747
3748             if (this.patterns.transparent.test(val)) {
3749                 var parent = el.parentNode;
3750                 val = fly(parent).getStyle(attr);
3751
3752                 while (parent && this.patterns.transparent.test(val)) {
3753                     parent = parent.parentNode;
3754                     val = fly(parent).getStyle(attr);
3755                     if (parent.tagName.toUpperCase() == 'HTML') {
3756                         val = '#fff';
3757                     }
3758                 }
3759             }
3760         } else {
3761             val = superclass.getAttribute.call(this, attr);
3762         }
3763
3764         return val;
3765     };
3766
3767     proto.doMethod = function(attr, start, end) {
3768         var val;
3769
3770         if (this.patterns.color.test(attr)) {
3771             val = [];
3772             for (var i = 0, len = start.length; i < len; ++i) {
3773                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3774             }
3775
3776             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3777         }
3778         else {
3779             val = superclass.doMethod.call(this, attr, start, end);
3780         }
3781
3782         return val;
3783     };
3784
3785     proto.setRuntimeAttribute = function(attr) {
3786         superclass.setRuntimeAttribute.call(this, attr);
3787
3788         if (this.patterns.color.test(attr)) {
3789             var attributes = this.attributes;
3790             var start = this.parseColor(this.runtimeAttributes[attr].start);
3791             var end = this.parseColor(this.runtimeAttributes[attr].end);
3792
3793             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3794                 end = this.parseColor(attributes[attr].by);
3795
3796                 for (var i = 0, len = start.length; i < len; ++i) {
3797                     end[i] = start[i] + end[i];
3798                 }
3799             }
3800
3801             this.runtimeAttributes[attr].start = start;
3802             this.runtimeAttributes[attr].end = end;
3803         }
3804     };
3805 })();
3806
3807 /*
3808  * Portions of this file are based on pieces of Yahoo User Interface Library
3809  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3810  * YUI licensed under the BSD License:
3811  * http://developer.yahoo.net/yui/license.txt
3812  * <script type="text/javascript">
3813  *
3814  */
3815 Roo.lib.Easing = {
3816
3817
3818     easeNone: function (t, b, c, d) {
3819         return c * t / d + b;
3820     },
3821
3822
3823     easeIn: function (t, b, c, d) {
3824         return c * (t /= d) * t + b;
3825     },
3826
3827
3828     easeOut: function (t, b, c, d) {
3829         return -c * (t /= d) * (t - 2) + b;
3830     },
3831
3832
3833     easeBoth: function (t, b, c, d) {
3834         if ((t /= d / 2) < 1) {
3835             return c / 2 * t * t + b;
3836         }
3837
3838         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3839     },
3840
3841
3842     easeInStrong: function (t, b, c, d) {
3843         return c * (t /= d) * t * t * t + b;
3844     },
3845
3846
3847     easeOutStrong: function (t, b, c, d) {
3848         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3849     },
3850
3851
3852     easeBothStrong: function (t, b, c, d) {
3853         if ((t /= d / 2) < 1) {
3854             return c / 2 * t * t * t * t + b;
3855         }
3856
3857         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3858     },
3859
3860
3861
3862     elasticIn: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3882     },
3883
3884
3885     elasticOut: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889         if ((t /= d) == 1) {
3890             return b + c;
3891         }
3892         if (!p) {
3893             p = d * .3;
3894         }
3895
3896         if (!a || a < Math.abs(c)) {
3897             a = c;
3898             var s = p / 4;
3899         }
3900         else {
3901             var s = p / (2 * Math.PI) * Math.asin(c / a);
3902         }
3903
3904         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3905     },
3906
3907
3908     elasticBoth: function (t, b, c, d, a, p) {
3909         if (t == 0) {
3910             return b;
3911         }
3912
3913         if ((t /= d / 2) == 2) {
3914             return b + c;
3915         }
3916
3917         if (!p) {
3918             p = d * (.3 * 1.5);
3919         }
3920
3921         if (!a || a < Math.abs(c)) {
3922             a = c;
3923             var s = p / 4;
3924         }
3925         else {
3926             var s = p / (2 * Math.PI) * Math.asin(c / a);
3927         }
3928
3929         if (t < 1) {
3930             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3931                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3932         }
3933         return a * Math.pow(2, -10 * (t -= 1)) *
3934                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3935     },
3936
3937
3938
3939     backIn: function (t, b, c, d, s) {
3940         if (typeof s == 'undefined') {
3941             s = 1.70158;
3942         }
3943         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3944     },
3945
3946
3947     backOut: function (t, b, c, d, s) {
3948         if (typeof s == 'undefined') {
3949             s = 1.70158;
3950         }
3951         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3952     },
3953
3954
3955     backBoth: function (t, b, c, d, s) {
3956         if (typeof s == 'undefined') {
3957             s = 1.70158;
3958         }
3959
3960         if ((t /= d / 2 ) < 1) {
3961             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3962         }
3963         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3964     },
3965
3966
3967     bounceIn: function (t, b, c, d) {
3968         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3969     },
3970
3971
3972     bounceOut: function (t, b, c, d) {
3973         if ((t /= d) < (1 / 2.75)) {
3974             return c * (7.5625 * t * t) + b;
3975         } else if (t < (2 / 2.75)) {
3976             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3977         } else if (t < (2.5 / 2.75)) {
3978             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3979         }
3980         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3981     },
3982
3983
3984     bounceBoth: function (t, b, c, d) {
3985         if (t < d / 2) {
3986             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3987         }
3988         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3989     }
3990 };/*
3991  * Portions of this file are based on pieces of Yahoo User Interface Library
3992  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3993  * YUI licensed under the BSD License:
3994  * http://developer.yahoo.net/yui/license.txt
3995  * <script type="text/javascript">
3996  *
3997  */
3998     (function() {
3999         Roo.lib.Motion = function(el, attributes, duration, method) {
4000             if (el) {
4001                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4002             }
4003         };
4004
4005         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4006
4007
4008         var Y = Roo.lib;
4009         var superclass = Y.Motion.superclass;
4010         var proto = Y.Motion.prototype;
4011
4012         proto.toString = function() {
4013             var el = this.getEl();
4014             var id = el.id || el.tagName;
4015             return ("Motion " + id);
4016         };
4017
4018         proto.patterns.points = /^points$/i;
4019
4020         proto.setAttribute = function(attr, val, unit) {
4021             if (this.patterns.points.test(attr)) {
4022                 unit = unit || 'px';
4023                 superclass.setAttribute.call(this, 'left', val[0], unit);
4024                 superclass.setAttribute.call(this, 'top', val[1], unit);
4025             } else {
4026                 superclass.setAttribute.call(this, attr, val, unit);
4027             }
4028         };
4029
4030         proto.getAttribute = function(attr) {
4031             if (this.patterns.points.test(attr)) {
4032                 var val = [
4033                         superclass.getAttribute.call(this, 'left'),
4034                         superclass.getAttribute.call(this, 'top')
4035                         ];
4036             } else {
4037                 val = superclass.getAttribute.call(this, attr);
4038             }
4039
4040             return val;
4041         };
4042
4043         proto.doMethod = function(attr, start, end) {
4044             var val = null;
4045
4046             if (this.patterns.points.test(attr)) {
4047                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4048                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4049             } else {
4050                 val = superclass.doMethod.call(this, attr, start, end);
4051             }
4052             return val;
4053         };
4054
4055         proto.setRuntimeAttribute = function(attr) {
4056             if (this.patterns.points.test(attr)) {
4057                 var el = this.getEl();
4058                 var attributes = this.attributes;
4059                 var start;
4060                 var control = attributes['points']['control'] || [];
4061                 var end;
4062                 var i, len;
4063
4064                 if (control.length > 0 && !(control[0] instanceof Array)) {
4065                     control = [control];
4066                 } else {
4067                     var tmp = [];
4068                     for (i = 0,len = control.length; i < len; ++i) {
4069                         tmp[i] = control[i];
4070                     }
4071                     control = tmp;
4072                 }
4073
4074                 Roo.fly(el).position();
4075
4076                 if (isset(attributes['points']['from'])) {
4077                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4078                 }
4079                 else {
4080                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4081                 }
4082
4083                 start = this.getAttribute('points');
4084
4085
4086                 if (isset(attributes['points']['to'])) {
4087                     end = translateValues.call(this, attributes['points']['to'], start);
4088
4089                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4090                     for (i = 0,len = control.length; i < len; ++i) {
4091                         control[i] = translateValues.call(this, control[i], start);
4092                     }
4093
4094
4095                 } else if (isset(attributes['points']['by'])) {
4096                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4097
4098                     for (i = 0,len = control.length; i < len; ++i) {
4099                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4100                     }
4101                 }
4102
4103                 this.runtimeAttributes[attr] = [start];
4104
4105                 if (control.length > 0) {
4106                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4107                 }
4108
4109                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4110             }
4111             else {
4112                 superclass.setRuntimeAttribute.call(this, attr);
4113             }
4114         };
4115
4116         var translateValues = function(val, start) {
4117             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4118             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4119
4120             return val;
4121         };
4122
4123         var isset = function(prop) {
4124             return (typeof prop !== 'undefined');
4125         };
4126     })();
4127 /*
4128  * Portions of this file are based on pieces of Yahoo User Interface Library
4129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4130  * YUI licensed under the BSD License:
4131  * http://developer.yahoo.net/yui/license.txt
4132  * <script type="text/javascript">
4133  *
4134  */
4135     (function() {
4136         Roo.lib.Scroll = function(el, attributes, duration, method) {
4137             if (el) {
4138                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4139             }
4140         };
4141
4142         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4143
4144
4145         var Y = Roo.lib;
4146         var superclass = Y.Scroll.superclass;
4147         var proto = Y.Scroll.prototype;
4148
4149         proto.toString = function() {
4150             var el = this.getEl();
4151             var id = el.id || el.tagName;
4152             return ("Scroll " + id);
4153         };
4154
4155         proto.doMethod = function(attr, start, end) {
4156             var val = null;
4157
4158             if (attr == 'scroll') {
4159                 val = [
4160                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4161                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4162                         ];
4163
4164             } else {
4165                 val = superclass.doMethod.call(this, attr, start, end);
4166             }
4167             return val;
4168         };
4169
4170         proto.getAttribute = function(attr) {
4171             var val = null;
4172             var el = this.getEl();
4173
4174             if (attr == 'scroll') {
4175                 val = [ el.scrollLeft, el.scrollTop ];
4176             } else {
4177                 val = superclass.getAttribute.call(this, attr);
4178             }
4179
4180             return val;
4181         };
4182
4183         proto.setAttribute = function(attr, val, unit) {
4184             var el = this.getEl();
4185
4186             if (attr == 'scroll') {
4187                 el.scrollLeft = val[0];
4188                 el.scrollTop = val[1];
4189             } else {
4190                 superclass.setAttribute.call(this, attr, val, unit);
4191             }
4192         };
4193     })();
4194 /*
4195  * Based on:
4196  * Ext JS Library 1.1.1
4197  * Copyright(c) 2006-2007, Ext JS, LLC.
4198  *
4199  * Originally Released Under LGPL - original licence link has changed is not relivant.
4200  *
4201  * Fork - LGPL
4202  * <script type="text/javascript">
4203  */
4204
4205
4206 // nasty IE9 hack - what a pile of crap that is..
4207
4208  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4209     Range.prototype.createContextualFragment = function (html) {
4210         var doc = window.document;
4211         var container = doc.createElement("div");
4212         container.innerHTML = html;
4213         var frag = doc.createDocumentFragment(), n;
4214         while ((n = container.firstChild)) {
4215             frag.appendChild(n);
4216         }
4217         return frag;
4218     };
4219 }
4220
4221 /**
4222  * @class Roo.DomHelper
4223  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4224  * 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>.
4225  * @singleton
4226  */
4227 Roo.DomHelper = function(){
4228     var tempTableEl = null;
4229     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4230     var tableRe = /^table|tbody|tr|td$/i;
4231     var xmlns = {};
4232     // build as innerHTML where available
4233     /** @ignore */
4234     var createHtml = function(o){
4235         if(typeof o == 'string'){
4236             return o;
4237         }
4238         var b = "";
4239         if(!o.tag){
4240             o.tag = "div";
4241         }
4242         b += "<" + o.tag;
4243         for(var attr in o){
4244             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4245             if(attr == "style"){
4246                 var s = o["style"];
4247                 if(typeof s == "function"){
4248                     s = s.call();
4249                 }
4250                 if(typeof s == "string"){
4251                     b += ' style="' + s + '"';
4252                 }else if(typeof s == "object"){
4253                     b += ' style="';
4254                     for(var key in s){
4255                         if(typeof s[key] != "function"){
4256                             b += key + ":" + s[key] + ";";
4257                         }
4258                     }
4259                     b += '"';
4260                 }
4261             }else{
4262                 if(attr == "cls"){
4263                     b += ' class="' + o["cls"] + '"';
4264                 }else if(attr == "htmlFor"){
4265                     b += ' for="' + o["htmlFor"] + '"';
4266                 }else{
4267                     b += " " + attr + '="' + o[attr] + '"';
4268                 }
4269             }
4270         }
4271         if(emptyTags.test(o.tag)){
4272             b += "/>";
4273         }else{
4274             b += ">";
4275             var cn = o.children || o.cn;
4276             if(cn){
4277                 //http://bugs.kde.org/show_bug.cgi?id=71506
4278                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4279                     for(var i = 0, len = cn.length; i < len; i++) {
4280                         b += createHtml(cn[i], b);
4281                     }
4282                 }else{
4283                     b += createHtml(cn, b);
4284                 }
4285             }
4286             if(o.html){
4287                 b += o.html;
4288             }
4289             b += "</" + o.tag + ">";
4290         }
4291         return b;
4292     };
4293
4294     // build as dom
4295     /** @ignore */
4296     var createDom = function(o, parentNode){
4297          
4298         // defininition craeted..
4299         var ns = false;
4300         if (o.ns && o.ns != 'html') {
4301                
4302             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4303                 xmlns[o.ns] = o.xmlns;
4304                 ns = o.xmlns;
4305             }
4306             if (typeof(xmlns[o.ns]) == 'undefined') {
4307                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4308             }
4309             ns = xmlns[o.ns];
4310         }
4311         
4312         
4313         if (typeof(o) == 'string') {
4314             return parentNode.appendChild(document.createTextNode(o));
4315         }
4316         o.tag = o.tag || div;
4317         if (o.ns && Roo.isIE) {
4318             ns = false;
4319             o.tag = o.ns + ':' + o.tag;
4320             
4321         }
4322         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4323         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4324         for(var attr in o){
4325             
4326             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4327                     attr == "style" || typeof o[attr] == "function") { continue; }
4328                     
4329             if(attr=="cls" && Roo.isIE){
4330                 el.className = o["cls"];
4331             }else{
4332                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4333                 else { 
4334                     el[attr] = o[attr];
4335                 }
4336             }
4337         }
4338         Roo.DomHelper.applyStyles(el, o.style);
4339         var cn = o.children || o.cn;
4340         if(cn){
4341             //http://bugs.kde.org/show_bug.cgi?id=71506
4342              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4343                 for(var i = 0, len = cn.length; i < len; i++) {
4344                     createDom(cn[i], el);
4345                 }
4346             }else{
4347                 createDom(cn, el);
4348             }
4349         }
4350         if(o.html){
4351             el.innerHTML = o.html;
4352         }
4353         if(parentNode){
4354            parentNode.appendChild(el);
4355         }
4356         return el;
4357     };
4358
4359     var ieTable = function(depth, s, h, e){
4360         tempTableEl.innerHTML = [s, h, e].join('');
4361         var i = -1, el = tempTableEl;
4362         while(++i < depth){
4363             el = el.firstChild;
4364         }
4365         return el;
4366     };
4367
4368     // kill repeat to save bytes
4369     var ts = '<table>',
4370         te = '</table>',
4371         tbs = ts+'<tbody>',
4372         tbe = '</tbody>'+te,
4373         trs = tbs + '<tr>',
4374         tre = '</tr>'+tbe;
4375
4376     /**
4377      * @ignore
4378      * Nasty code for IE's broken table implementation
4379      */
4380     var insertIntoTable = function(tag, where, el, html){
4381         if(!tempTableEl){
4382             tempTableEl = document.createElement('div');
4383         }
4384         var node;
4385         var before = null;
4386         if(tag == 'td'){
4387             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4388                 return;
4389             }
4390             if(where == 'beforebegin'){
4391                 before = el;
4392                 el = el.parentNode;
4393             } else{
4394                 before = el.nextSibling;
4395                 el = el.parentNode;
4396             }
4397             node = ieTable(4, trs, html, tre);
4398         }
4399         else if(tag == 'tr'){
4400             if(where == 'beforebegin'){
4401                 before = el;
4402                 el = el.parentNode;
4403                 node = ieTable(3, tbs, html, tbe);
4404             } else if(where == 'afterend'){
4405                 before = el.nextSibling;
4406                 el = el.parentNode;
4407                 node = ieTable(3, tbs, html, tbe);
4408             } else{ // INTO a TR
4409                 if(where == 'afterbegin'){
4410                     before = el.firstChild;
4411                 }
4412                 node = ieTable(4, trs, html, tre);
4413             }
4414         } else if(tag == 'tbody'){
4415             if(where == 'beforebegin'){
4416                 before = el;
4417                 el = el.parentNode;
4418                 node = ieTable(2, ts, html, te);
4419             } else if(where == 'afterend'){
4420                 before = el.nextSibling;
4421                 el = el.parentNode;
4422                 node = ieTable(2, ts, html, te);
4423             } else{
4424                 if(where == 'afterbegin'){
4425                     before = el.firstChild;
4426                 }
4427                 node = ieTable(3, tbs, html, tbe);
4428             }
4429         } else{ // TABLE
4430             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4431                 return;
4432             }
4433             if(where == 'afterbegin'){
4434                 before = el.firstChild;
4435             }
4436             node = ieTable(2, ts, html, te);
4437         }
4438         el.insertBefore(node, before);
4439         return node;
4440     };
4441
4442     return {
4443     /** True to force the use of DOM instead of html fragments @type Boolean */
4444     useDom : false,
4445
4446     /**
4447      * Returns the markup for the passed Element(s) config
4448      * @param {Object} o The Dom object spec (and children)
4449      * @return {String}
4450      */
4451     markup : function(o){
4452         return createHtml(o);
4453     },
4454
4455     /**
4456      * Applies a style specification to an element
4457      * @param {String/HTMLElement} el The element to apply styles to
4458      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4459      * a function which returns such a specification.
4460      */
4461     applyStyles : function(el, styles){
4462         if(styles){
4463            el = Roo.fly(el);
4464            if(typeof styles == "string"){
4465                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4466                var matches;
4467                while ((matches = re.exec(styles)) != null){
4468                    el.setStyle(matches[1], matches[2]);
4469                }
4470            }else if (typeof styles == "object"){
4471                for (var style in styles){
4472                   el.setStyle(style, styles[style]);
4473                }
4474            }else if (typeof styles == "function"){
4475                 Roo.DomHelper.applyStyles(el, styles.call());
4476            }
4477         }
4478     },
4479
4480     /**
4481      * Inserts an HTML fragment into the Dom
4482      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4483      * @param {HTMLElement} el The context element
4484      * @param {String} html The HTML fragmenet
4485      * @return {HTMLElement} The new node
4486      */
4487     insertHtml : function(where, el, html){
4488         where = where.toLowerCase();
4489         if(el.insertAdjacentHTML){
4490             if(tableRe.test(el.tagName)){
4491                 var rs;
4492                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4493                     return rs;
4494                 }
4495             }
4496             switch(where){
4497                 case "beforebegin":
4498                     el.insertAdjacentHTML('BeforeBegin', html);
4499                     return el.previousSibling;
4500                 case "afterbegin":
4501                     el.insertAdjacentHTML('AfterBegin', html);
4502                     return el.firstChild;
4503                 case "beforeend":
4504                     el.insertAdjacentHTML('BeforeEnd', html);
4505                     return el.lastChild;
4506                 case "afterend":
4507                     el.insertAdjacentHTML('AfterEnd', html);
4508                     return el.nextSibling;
4509             }
4510             throw 'Illegal insertion point -> "' + where + '"';
4511         }
4512         var range = el.ownerDocument.createRange();
4513         var frag;
4514         switch(where){
4515              case "beforebegin":
4516                 range.setStartBefore(el);
4517                 frag = range.createContextualFragment(html);
4518                 el.parentNode.insertBefore(frag, el);
4519                 return el.previousSibling;
4520              case "afterbegin":
4521                 if(el.firstChild){
4522                     range.setStartBefore(el.firstChild);
4523                     frag = range.createContextualFragment(html);
4524                     el.insertBefore(frag, el.firstChild);
4525                     return el.firstChild;
4526                 }else{
4527                     el.innerHTML = html;
4528                     return el.firstChild;
4529                 }
4530             case "beforeend":
4531                 if(el.lastChild){
4532                     range.setStartAfter(el.lastChild);
4533                     frag = range.createContextualFragment(html);
4534                     el.appendChild(frag);
4535                     return el.lastChild;
4536                 }else{
4537                     el.innerHTML = html;
4538                     return el.lastChild;
4539                 }
4540             case "afterend":
4541                 range.setStartAfter(el);
4542                 frag = range.createContextualFragment(html);
4543                 el.parentNode.insertBefore(frag, el.nextSibling);
4544                 return el.nextSibling;
4545             }
4546             throw 'Illegal insertion point -> "' + where + '"';
4547     },
4548
4549     /**
4550      * Creates new Dom element(s) and inserts them before el
4551      * @param {String/HTMLElement/Element} el The context element
4552      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4553      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4554      * @return {HTMLElement/Roo.Element} The new node
4555      */
4556     insertBefore : function(el, o, returnElement){
4557         return this.doInsert(el, o, returnElement, "beforeBegin");
4558     },
4559
4560     /**
4561      * Creates new Dom element(s) and inserts them after el
4562      * @param {String/HTMLElement/Element} el The context element
4563      * @param {Object} o The Dom object spec (and children)
4564      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4565      * @return {HTMLElement/Roo.Element} The new node
4566      */
4567     insertAfter : function(el, o, returnElement){
4568         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4569     },
4570
4571     /**
4572      * Creates new Dom element(s) and inserts them as the first child of el
4573      * @param {String/HTMLElement/Element} el The context element
4574      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4575      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4576      * @return {HTMLElement/Roo.Element} The new node
4577      */
4578     insertFirst : function(el, o, returnElement){
4579         return this.doInsert(el, o, returnElement, "afterBegin");
4580     },
4581
4582     // private
4583     doInsert : function(el, o, returnElement, pos, sibling){
4584         el = Roo.getDom(el);
4585         var newNode;
4586         if(this.useDom || o.ns){
4587             newNode = createDom(o, null);
4588             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4589         }else{
4590             var html = createHtml(o);
4591             newNode = this.insertHtml(pos, el, html);
4592         }
4593         return returnElement ? Roo.get(newNode, true) : newNode;
4594     },
4595
4596     /**
4597      * Creates new Dom element(s) and appends them to el
4598      * @param {String/HTMLElement/Element} el The context element
4599      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4600      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4601      * @return {HTMLElement/Roo.Element} The new node
4602      */
4603     append : function(el, o, returnElement){
4604         el = Roo.getDom(el);
4605         var newNode;
4606         if(this.useDom || o.ns){
4607             newNode = createDom(o, null);
4608             el.appendChild(newNode);
4609         }else{
4610             var html = createHtml(o);
4611             newNode = this.insertHtml("beforeEnd", el, html);
4612         }
4613         return returnElement ? Roo.get(newNode, true) : newNode;
4614     },
4615
4616     /**
4617      * Creates new Dom element(s) and overwrites the contents of el with them
4618      * @param {String/HTMLElement/Element} el The context element
4619      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4620      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4621      * @return {HTMLElement/Roo.Element} The new node
4622      */
4623     overwrite : function(el, o, returnElement){
4624         el = Roo.getDom(el);
4625         if (o.ns) {
4626           
4627             while (el.childNodes.length) {
4628                 el.removeChild(el.firstChild);
4629             }
4630             createDom(o, el);
4631         } else {
4632             el.innerHTML = createHtml(o);   
4633         }
4634         
4635         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4636     },
4637
4638     /**
4639      * Creates a new Roo.DomHelper.Template from the Dom object spec
4640      * @param {Object} o The Dom object spec (and children)
4641      * @return {Roo.DomHelper.Template} The new template
4642      */
4643     createTemplate : function(o){
4644         var html = createHtml(o);
4645         return new Roo.Template(html);
4646     }
4647     };
4648 }();
4649 /*
4650  * Based on:
4651  * Ext JS Library 1.1.1
4652  * Copyright(c) 2006-2007, Ext JS, LLC.
4653  *
4654  * Originally Released Under LGPL - original licence link has changed is not relivant.
4655  *
4656  * Fork - LGPL
4657  * <script type="text/javascript">
4658  */
4659  
4660 /**
4661 * @class Roo.Template
4662 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4663 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4664 * Usage:
4665 <pre><code>
4666 var t = new Roo.Template({
4667     html :  '&lt;div name="{id}"&gt;' + 
4668         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4669         '&lt;/div&gt;',
4670     myformat: function (value, allValues) {
4671         return 'XX' + value;
4672     }
4673 });
4674 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4675 </code></pre>
4676 * For more information see this blog post with examples:
4677 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4678      - Create Elements using DOM, HTML fragments and Templates</a>. 
4679 * @constructor
4680 * @param {Object} cfg - Configuration object.
4681 */
4682 Roo.Template = function(cfg){
4683     // BC!
4684     if(cfg instanceof Array){
4685         cfg = cfg.join("");
4686     }else if(arguments.length > 1){
4687         cfg = Array.prototype.join.call(arguments, "");
4688     }
4689     
4690     
4691     if (typeof(cfg) == 'object') {
4692         Roo.apply(this,cfg)
4693     } else {
4694         // bc
4695         this.html = cfg;
4696     }
4697     if (this.url) {
4698         this.load();
4699     }
4700     
4701 };
4702 Roo.Template.prototype = {
4703     
4704     /**
4705      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4706      */
4707     onLoad : false,
4708     
4709     
4710     /**
4711      * @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..
4712      *                    it should be fixed so that template is observable...
4713      */
4714     url : false,
4715     /**
4716      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4717      */
4718     html : '',
4719     
4720     
4721     compiled : false,
4722     loaded : false,
4723     /**
4724      * Returns an HTML fragment of this template with the specified values applied.
4725      * @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'})
4726      * @return {String} The HTML fragment
4727      */
4728     
4729    
4730     
4731     applyTemplate : function(values){
4732         //Roo.log(["applyTemplate", values]);
4733         try {
4734            
4735             if(this.compiled){
4736                 return this.compiled(values);
4737             }
4738             var useF = this.disableFormats !== true;
4739             var fm = Roo.util.Format, tpl = this;
4740             var fn = function(m, name, format, args){
4741                 if(format && useF){
4742                     if(format.substr(0, 5) == "this."){
4743                         return tpl.call(format.substr(5), values[name], values);
4744                     }else{
4745                         if(args){
4746                             // quoted values are required for strings in compiled templates, 
4747                             // but for non compiled we need to strip them
4748                             // quoted reversed for jsmin
4749                             var re = /^\s*['"](.*)["']\s*$/;
4750                             args = args.split(',');
4751                             for(var i = 0, len = args.length; i < len; i++){
4752                                 args[i] = args[i].replace(re, "$1");
4753                             }
4754                             args = [values[name]].concat(args);
4755                         }else{
4756                             args = [values[name]];
4757                         }
4758                         return fm[format].apply(fm, args);
4759                     }
4760                 }else{
4761                     return values[name] !== undefined ? values[name] : "";
4762                 }
4763             };
4764             return this.html.replace(this.re, fn);
4765         } catch (e) {
4766             Roo.log(e);
4767             throw e;
4768         }
4769          
4770     },
4771     
4772     loading : false,
4773       
4774     load : function ()
4775     {
4776          
4777         if (this.loading) {
4778             return;
4779         }
4780         var _t = this;
4781         
4782         this.loading = true;
4783         this.compiled = false;
4784         
4785         var cx = new Roo.data.Connection();
4786         cx.request({
4787             url : this.url,
4788             method : 'GET',
4789             success : function (response) {
4790                 _t.loading = false;
4791                 _t.url = false;
4792                 
4793                 _t.set(response.responseText,true);
4794                 _t.loaded = true;
4795                 if (_t.onLoad) {
4796                     _t.onLoad();
4797                 }
4798              },
4799             failure : function(response) {
4800                 Roo.log("Template failed to load from " + _t.url);
4801                 _t.loading = false;
4802             }
4803         });
4804     },
4805
4806     /**
4807      * Sets the HTML used as the template and optionally compiles it.
4808      * @param {String} html
4809      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4810      * @return {Roo.Template} this
4811      */
4812     set : function(html, compile){
4813         this.html = html;
4814         this.compiled = false;
4815         if(compile){
4816             this.compile();
4817         }
4818         return this;
4819     },
4820     
4821     /**
4822      * True to disable format functions (defaults to false)
4823      * @type Boolean
4824      */
4825     disableFormats : false,
4826     
4827     /**
4828     * The regular expression used to match template variables 
4829     * @type RegExp
4830     * @property 
4831     */
4832     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4833     
4834     /**
4835      * Compiles the template into an internal function, eliminating the RegEx overhead.
4836      * @return {Roo.Template} this
4837      */
4838     compile : function(){
4839         var fm = Roo.util.Format;
4840         var useF = this.disableFormats !== true;
4841         var sep = Roo.isGecko ? "+" : ",";
4842         var fn = function(m, name, format, args){
4843             if(format && useF){
4844                 args = args ? ',' + args : "";
4845                 if(format.substr(0, 5) != "this."){
4846                     format = "fm." + format + '(';
4847                 }else{
4848                     format = 'this.call("'+ format.substr(5) + '", ';
4849                     args = ", values";
4850                 }
4851             }else{
4852                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4853             }
4854             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4855         };
4856         var body;
4857         // branched to use + in gecko and [].join() in others
4858         if(Roo.isGecko){
4859             body = "this.compiled = function(values){ return '" +
4860                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4861                     "';};";
4862         }else{
4863             body = ["this.compiled = function(values){ return ['"];
4864             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4865             body.push("'].join('');};");
4866             body = body.join('');
4867         }
4868         /**
4869          * eval:var:values
4870          * eval:var:fm
4871          */
4872         eval(body);
4873         return this;
4874     },
4875     
4876     // private function used to call members
4877     call : function(fnName, value, allValues){
4878         return this[fnName](value, allValues);
4879     },
4880     
4881     /**
4882      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4883      * @param {String/HTMLElement/Roo.Element} el The context element
4884      * @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'})
4885      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4886      * @return {HTMLElement/Roo.Element} The new node or Element
4887      */
4888     insertFirst: function(el, values, returnElement){
4889         return this.doInsert('afterBegin', el, values, returnElement);
4890     },
4891
4892     /**
4893      * Applies the supplied values to the template and inserts the new node(s) before el.
4894      * @param {String/HTMLElement/Roo.Element} el The context element
4895      * @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'})
4896      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4897      * @return {HTMLElement/Roo.Element} The new node or Element
4898      */
4899     insertBefore: function(el, values, returnElement){
4900         return this.doInsert('beforeBegin', el, values, returnElement);
4901     },
4902
4903     /**
4904      * Applies the supplied values to the template and inserts the new node(s) after el.
4905      * @param {String/HTMLElement/Roo.Element} el The context element
4906      * @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'})
4907      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4908      * @return {HTMLElement/Roo.Element} The new node or Element
4909      */
4910     insertAfter : function(el, values, returnElement){
4911         return this.doInsert('afterEnd', el, values, returnElement);
4912     },
4913     
4914     /**
4915      * Applies the supplied values to the template and appends the new node(s) to el.
4916      * @param {String/HTMLElement/Roo.Element} el The context element
4917      * @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'})
4918      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4919      * @return {HTMLElement/Roo.Element} The new node or Element
4920      */
4921     append : function(el, values, returnElement){
4922         return this.doInsert('beforeEnd', el, values, returnElement);
4923     },
4924
4925     doInsert : function(where, el, values, returnEl){
4926         el = Roo.getDom(el);
4927         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4928         return returnEl ? Roo.get(newNode, true) : newNode;
4929     },
4930
4931     /**
4932      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4933      * @param {String/HTMLElement/Roo.Element} el The context element
4934      * @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'})
4935      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4936      * @return {HTMLElement/Roo.Element} The new node or Element
4937      */
4938     overwrite : function(el, values, returnElement){
4939         el = Roo.getDom(el);
4940         el.innerHTML = this.applyTemplate(values);
4941         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4942     }
4943 };
4944 /**
4945  * Alias for {@link #applyTemplate}
4946  * @method
4947  */
4948 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4949
4950 // backwards compat
4951 Roo.DomHelper.Template = Roo.Template;
4952
4953 /**
4954  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4955  * @param {String/HTMLElement} el A DOM element or its id
4956  * @returns {Roo.Template} The created template
4957  * @static
4958  */
4959 Roo.Template.from = function(el){
4960     el = Roo.getDom(el);
4961     return new Roo.Template(el.value || el.innerHTML);
4962 };/*
4963  * Based on:
4964  * Ext JS Library 1.1.1
4965  * Copyright(c) 2006-2007, Ext JS, LLC.
4966  *
4967  * Originally Released Under LGPL - original licence link has changed is not relivant.
4968  *
4969  * Fork - LGPL
4970  * <script type="text/javascript">
4971  */
4972  
4973
4974 /*
4975  * This is code is also distributed under MIT license for use
4976  * with jQuery and prototype JavaScript libraries.
4977  */
4978 /**
4979  * @class Roo.DomQuery
4980 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).
4981 <p>
4982 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>
4983
4984 <p>
4985 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.
4986 </p>
4987 <h4>Element Selectors:</h4>
4988 <ul class="list">
4989     <li> <b>*</b> any element</li>
4990     <li> <b>E</b> an element with the tag E</li>
4991     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4992     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4993     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4994     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4995 </ul>
4996 <h4>Attribute Selectors:</h4>
4997 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4998 <ul class="list">
4999     <li> <b>E[foo]</b> has an attribute "foo"</li>
5000     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5001     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5002     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5003     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5004     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5005     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5006 </ul>
5007 <h4>Pseudo Classes:</h4>
5008 <ul class="list">
5009     <li> <b>E:first-child</b> E is the first child of its parent</li>
5010     <li> <b>E:last-child</b> E is the last child of its parent</li>
5011     <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>
5012     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5013     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5014     <li> <b>E:only-child</b> E is the only child of its parent</li>
5015     <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>
5016     <li> <b>E:first</b> the first E in the resultset</li>
5017     <li> <b>E:last</b> the last E in the resultset</li>
5018     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5019     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5020     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5021     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5022     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5023     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5024     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5025     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5026     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5027 </ul>
5028 <h4>CSS Value Selectors:</h4>
5029 <ul class="list">
5030     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5031     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5032     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5033     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5034     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5035     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5036 </ul>
5037  * @singleton
5038  */
5039 Roo.DomQuery = function(){
5040     var cache = {}, simpleCache = {}, valueCache = {};
5041     var nonSpace = /\S/;
5042     var trimRe = /^\s+|\s+$/g;
5043     var tplRe = /\{(\d+)\}/g;
5044     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5045     var tagTokenRe = /^(#)?([\w-\*]+)/;
5046     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5047
5048     function child(p, index){
5049         var i = 0;
5050         var n = p.firstChild;
5051         while(n){
5052             if(n.nodeType == 1){
5053                if(++i == index){
5054                    return n;
5055                }
5056             }
5057             n = n.nextSibling;
5058         }
5059         return null;
5060     };
5061
5062     function next(n){
5063         while((n = n.nextSibling) && n.nodeType != 1);
5064         return n;
5065     };
5066
5067     function prev(n){
5068         while((n = n.previousSibling) && n.nodeType != 1);
5069         return n;
5070     };
5071
5072     function children(d){
5073         var n = d.firstChild, ni = -1;
5074             while(n){
5075                 var nx = n.nextSibling;
5076                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5077                     d.removeChild(n);
5078                 }else{
5079                     n.nodeIndex = ++ni;
5080                 }
5081                 n = nx;
5082             }
5083             return this;
5084         };
5085
5086     function byClassName(c, a, v){
5087         if(!v){
5088             return c;
5089         }
5090         var r = [], ri = -1, cn;
5091         for(var i = 0, ci; ci = c[i]; i++){
5092             
5093             
5094             if((' '+
5095                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5096                  +' ').indexOf(v) != -1){
5097                 r[++ri] = ci;
5098             }
5099         }
5100         return r;
5101     };
5102
5103     function attrValue(n, attr){
5104         if(!n.tagName && typeof n.length != "undefined"){
5105             n = n[0];
5106         }
5107         if(!n){
5108             return null;
5109         }
5110         if(attr == "for"){
5111             return n.htmlFor;
5112         }
5113         if(attr == "class" || attr == "className"){
5114             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5115         }
5116         return n.getAttribute(attr) || n[attr];
5117
5118     };
5119
5120     function getNodes(ns, mode, tagName){
5121         var result = [], ri = -1, cs;
5122         if(!ns){
5123             return result;
5124         }
5125         tagName = tagName || "*";
5126         if(typeof ns.getElementsByTagName != "undefined"){
5127             ns = [ns];
5128         }
5129         if(!mode){
5130             for(var i = 0, ni; ni = ns[i]; i++){
5131                 cs = ni.getElementsByTagName(tagName);
5132                 for(var j = 0, ci; ci = cs[j]; j++){
5133                     result[++ri] = ci;
5134                 }
5135             }
5136         }else if(mode == "/" || mode == ">"){
5137             var utag = tagName.toUpperCase();
5138             for(var i = 0, ni, cn; ni = ns[i]; i++){
5139                 cn = ni.children || ni.childNodes;
5140                 for(var j = 0, cj; cj = cn[j]; j++){
5141                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5142                         result[++ri] = cj;
5143                     }
5144                 }
5145             }
5146         }else if(mode == "+"){
5147             var utag = tagName.toUpperCase();
5148             for(var i = 0, n; n = ns[i]; i++){
5149                 while((n = n.nextSibling) && n.nodeType != 1);
5150                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5151                     result[++ri] = n;
5152                 }
5153             }
5154         }else if(mode == "~"){
5155             for(var i = 0, n; n = ns[i]; i++){
5156                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5157                 if(n){
5158                     result[++ri] = n;
5159                 }
5160             }
5161         }
5162         return result;
5163     };
5164
5165     function concat(a, b){
5166         if(b.slice){
5167             return a.concat(b);
5168         }
5169         for(var i = 0, l = b.length; i < l; i++){
5170             a[a.length] = b[i];
5171         }
5172         return a;
5173     }
5174
5175     function byTag(cs, tagName){
5176         if(cs.tagName || cs == document){
5177             cs = [cs];
5178         }
5179         if(!tagName){
5180             return cs;
5181         }
5182         var r = [], ri = -1;
5183         tagName = tagName.toLowerCase();
5184         for(var i = 0, ci; ci = cs[i]; i++){
5185             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5186                 r[++ri] = ci;
5187             }
5188         }
5189         return r;
5190     };
5191
5192     function byId(cs, attr, id){
5193         if(cs.tagName || cs == document){
5194             cs = [cs];
5195         }
5196         if(!id){
5197             return cs;
5198         }
5199         var r = [], ri = -1;
5200         for(var i = 0,ci; ci = cs[i]; i++){
5201             if(ci && ci.id == id){
5202                 r[++ri] = ci;
5203                 return r;
5204             }
5205         }
5206         return r;
5207     };
5208
5209     function byAttribute(cs, attr, value, op, custom){
5210         var r = [], ri = -1, st = custom=="{";
5211         var f = Roo.DomQuery.operators[op];
5212         for(var i = 0, ci; ci = cs[i]; i++){
5213             var a;
5214             if(st){
5215                 a = Roo.DomQuery.getStyle(ci, attr);
5216             }
5217             else if(attr == "class" || attr == "className"){
5218                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5219             }else if(attr == "for"){
5220                 a = ci.htmlFor;
5221             }else if(attr == "href"){
5222                 a = ci.getAttribute("href", 2);
5223             }else{
5224                 a = ci.getAttribute(attr);
5225             }
5226             if((f && f(a, value)) || (!f && a)){
5227                 r[++ri] = ci;
5228             }
5229         }
5230         return r;
5231     };
5232
5233     function byPseudo(cs, name, value){
5234         return Roo.DomQuery.pseudos[name](cs, value);
5235     };
5236
5237     // This is for IE MSXML which does not support expandos.
5238     // IE runs the same speed using setAttribute, however FF slows way down
5239     // and Safari completely fails so they need to continue to use expandos.
5240     var isIE = window.ActiveXObject ? true : false;
5241
5242     // this eval is stop the compressor from
5243     // renaming the variable to something shorter
5244     
5245     /** eval:var:batch */
5246     var batch = 30803; 
5247
5248     var key = 30803;
5249
5250     function nodupIEXml(cs){
5251         var d = ++key;
5252         cs[0].setAttribute("_nodup", d);
5253         var r = [cs[0]];
5254         for(var i = 1, len = cs.length; i < len; i++){
5255             var c = cs[i];
5256             if(!c.getAttribute("_nodup") != d){
5257                 c.setAttribute("_nodup", d);
5258                 r[r.length] = c;
5259             }
5260         }
5261         for(var i = 0, len = cs.length; i < len; i++){
5262             cs[i].removeAttribute("_nodup");
5263         }
5264         return r;
5265     }
5266
5267     function nodup(cs){
5268         if(!cs){
5269             return [];
5270         }
5271         var len = cs.length, c, i, r = cs, cj, ri = -1;
5272         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5273             return cs;
5274         }
5275         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5276             return nodupIEXml(cs);
5277         }
5278         var d = ++key;
5279         cs[0]._nodup = d;
5280         for(i = 1; c = cs[i]; i++){
5281             if(c._nodup != d){
5282                 c._nodup = d;
5283             }else{
5284                 r = [];
5285                 for(var j = 0; j < i; j++){
5286                     r[++ri] = cs[j];
5287                 }
5288                 for(j = i+1; cj = cs[j]; j++){
5289                     if(cj._nodup != d){
5290                         cj._nodup = d;
5291                         r[++ri] = cj;
5292                     }
5293                 }
5294                 return r;
5295             }
5296         }
5297         return r;
5298     }
5299
5300     function quickDiffIEXml(c1, c2){
5301         var d = ++key;
5302         for(var i = 0, len = c1.length; i < len; i++){
5303             c1[i].setAttribute("_qdiff", d);
5304         }
5305         var r = [];
5306         for(var i = 0, len = c2.length; i < len; i++){
5307             if(c2[i].getAttribute("_qdiff") != d){
5308                 r[r.length] = c2[i];
5309             }
5310         }
5311         for(var i = 0, len = c1.length; i < len; i++){
5312            c1[i].removeAttribute("_qdiff");
5313         }
5314         return r;
5315     }
5316
5317     function quickDiff(c1, c2){
5318         var len1 = c1.length;
5319         if(!len1){
5320             return c2;
5321         }
5322         if(isIE && c1[0].selectSingleNode){
5323             return quickDiffIEXml(c1, c2);
5324         }
5325         var d = ++key;
5326         for(var i = 0; i < len1; i++){
5327             c1[i]._qdiff = d;
5328         }
5329         var r = [];
5330         for(var i = 0, len = c2.length; i < len; i++){
5331             if(c2[i]._qdiff != d){
5332                 r[r.length] = c2[i];
5333             }
5334         }
5335         return r;
5336     }
5337
5338     function quickId(ns, mode, root, id){
5339         if(ns == root){
5340            var d = root.ownerDocument || root;
5341            return d.getElementById(id);
5342         }
5343         ns = getNodes(ns, mode, "*");
5344         return byId(ns, null, id);
5345     }
5346
5347     return {
5348         getStyle : function(el, name){
5349             return Roo.fly(el).getStyle(name);
5350         },
5351         /**
5352          * Compiles a selector/xpath query into a reusable function. The returned function
5353          * takes one parameter "root" (optional), which is the context node from where the query should start.
5354          * @param {String} selector The selector/xpath query
5355          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5356          * @return {Function}
5357          */
5358         compile : function(path, type){
5359             type = type || "select";
5360             
5361             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5362             var q = path, mode, lq;
5363             var tk = Roo.DomQuery.matchers;
5364             var tklen = tk.length;
5365             var mm;
5366
5367             // accept leading mode switch
5368             var lmode = q.match(modeRe);
5369             if(lmode && lmode[1]){
5370                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5371                 q = q.replace(lmode[1], "");
5372             }
5373             // strip leading slashes
5374             while(path.substr(0, 1)=="/"){
5375                 path = path.substr(1);
5376             }
5377
5378             while(q && lq != q){
5379                 lq = q;
5380                 var tm = q.match(tagTokenRe);
5381                 if(type == "select"){
5382                     if(tm){
5383                         if(tm[1] == "#"){
5384                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5385                         }else{
5386                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5387                         }
5388                         q = q.replace(tm[0], "");
5389                     }else if(q.substr(0, 1) != '@'){
5390                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5391                     }
5392                 }else{
5393                     if(tm){
5394                         if(tm[1] == "#"){
5395                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5396                         }else{
5397                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5398                         }
5399                         q = q.replace(tm[0], "");
5400                     }
5401                 }
5402                 while(!(mm = q.match(modeRe))){
5403                     var matched = false;
5404                     for(var j = 0; j < tklen; j++){
5405                         var t = tk[j];
5406                         var m = q.match(t.re);
5407                         if(m){
5408                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5409                                                     return m[i];
5410                                                 });
5411                             q = q.replace(m[0], "");
5412                             matched = true;
5413                             break;
5414                         }
5415                     }
5416                     // prevent infinite loop on bad selector
5417                     if(!matched){
5418                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5419                     }
5420                 }
5421                 if(mm[1]){
5422                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5423                     q = q.replace(mm[1], "");
5424                 }
5425             }
5426             fn[fn.length] = "return nodup(n);\n}";
5427             
5428              /** 
5429               * list of variables that need from compression as they are used by eval.
5430              *  eval:var:batch 
5431              *  eval:var:nodup
5432              *  eval:var:byTag
5433              *  eval:var:ById
5434              *  eval:var:getNodes
5435              *  eval:var:quickId
5436              *  eval:var:mode
5437              *  eval:var:root
5438              *  eval:var:n
5439              *  eval:var:byClassName
5440              *  eval:var:byPseudo
5441              *  eval:var:byAttribute
5442              *  eval:var:attrValue
5443              * 
5444              **/ 
5445             eval(fn.join(""));
5446             return f;
5447         },
5448
5449         /**
5450          * Selects a group of elements.
5451          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5452          * @param {Node} root (optional) The start of the query (defaults to document).
5453          * @return {Array}
5454          */
5455         select : function(path, root, type){
5456             if(!root || root == document){
5457                 root = document;
5458             }
5459             if(typeof root == "string"){
5460                 root = document.getElementById(root);
5461             }
5462             var paths = path.split(",");
5463             var results = [];
5464             for(var i = 0, len = paths.length; i < len; i++){
5465                 var p = paths[i].replace(trimRe, "");
5466                 if(!cache[p]){
5467                     cache[p] = Roo.DomQuery.compile(p);
5468                     if(!cache[p]){
5469                         throw p + " is not a valid selector";
5470                     }
5471                 }
5472                 var result = cache[p](root);
5473                 if(result && result != document){
5474                     results = results.concat(result);
5475                 }
5476             }
5477             if(paths.length > 1){
5478                 return nodup(results);
5479             }
5480             return results;
5481         },
5482
5483         /**
5484          * Selects a single element.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @return {Element}
5488          */
5489         selectNode : function(path, root){
5490             return Roo.DomQuery.select(path, root)[0];
5491         },
5492
5493         /**
5494          * Selects the value of a node, optionally replacing null with the defaultValue.
5495          * @param {String} selector The selector/xpath query
5496          * @param {Node} root (optional) The start of the query (defaults to document).
5497          * @param {String} defaultValue
5498          */
5499         selectValue : function(path, root, defaultValue){
5500             path = path.replace(trimRe, "");
5501             if(!valueCache[path]){
5502                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5503             }
5504             var n = valueCache[path](root);
5505             n = n[0] ? n[0] : n;
5506             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5507             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5508         },
5509
5510         /**
5511          * Selects the value of a node, parsing integers and floats.
5512          * @param {String} selector The selector/xpath query
5513          * @param {Node} root (optional) The start of the query (defaults to document).
5514          * @param {Number} defaultValue
5515          * @return {Number}
5516          */
5517         selectNumber : function(path, root, defaultValue){
5518             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5519             return parseFloat(v);
5520         },
5521
5522         /**
5523          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5524          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5525          * @param {String} selector The simple selector to test
5526          * @return {Boolean}
5527          */
5528         is : function(el, ss){
5529             if(typeof el == "string"){
5530                 el = document.getElementById(el);
5531             }
5532             var isArray = (el instanceof Array);
5533             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5534             return isArray ? (result.length == el.length) : (result.length > 0);
5535         },
5536
5537         /**
5538          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5539          * @param {Array} el An array of elements to filter
5540          * @param {String} selector The simple selector to test
5541          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5542          * the selector instead of the ones that match
5543          * @return {Array}
5544          */
5545         filter : function(els, ss, nonMatches){
5546             ss = ss.replace(trimRe, "");
5547             if(!simpleCache[ss]){
5548                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5549             }
5550             var result = simpleCache[ss](els);
5551             return nonMatches ? quickDiff(result, els) : result;
5552         },
5553
5554         /**
5555          * Collection of matching regular expressions and code snippets.
5556          */
5557         matchers : [{
5558                 re: /^\.([\w-]+)/,
5559                 select: 'n = byClassName(n, null, " {1} ");'
5560             }, {
5561                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5562                 select: 'n = byPseudo(n, "{1}", "{2}");'
5563             },{
5564                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5565                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5566             }, {
5567                 re: /^#([\w-]+)/,
5568                 select: 'n = byId(n, null, "{1}");'
5569             },{
5570                 re: /^@([\w-]+)/,
5571                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5572             }
5573         ],
5574
5575         /**
5576          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5577          * 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;.
5578          */
5579         operators : {
5580             "=" : function(a, v){
5581                 return a == v;
5582             },
5583             "!=" : function(a, v){
5584                 return a != v;
5585             },
5586             "^=" : function(a, v){
5587                 return a && a.substr(0, v.length) == v;
5588             },
5589             "$=" : function(a, v){
5590                 return a && a.substr(a.length-v.length) == v;
5591             },
5592             "*=" : function(a, v){
5593                 return a && a.indexOf(v) !== -1;
5594             },
5595             "%=" : function(a, v){
5596                 return (a % v) == 0;
5597             },
5598             "|=" : function(a, v){
5599                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5600             },
5601             "~=" : function(a, v){
5602                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5603             }
5604         },
5605
5606         /**
5607          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5608          * and the argument (if any) supplied in the selector.
5609          */
5610         pseudos : {
5611             "first-child" : function(c){
5612                 var r = [], ri = -1, n;
5613                 for(var i = 0, ci; ci = n = c[i]; i++){
5614                     while((n = n.previousSibling) && n.nodeType != 1);
5615                     if(!n){
5616                         r[++ri] = ci;
5617                     }
5618                 }
5619                 return r;
5620             },
5621
5622             "last-child" : function(c){
5623                 var r = [], ri = -1, n;
5624                 for(var i = 0, ci; ci = n = c[i]; i++){
5625                     while((n = n.nextSibling) && n.nodeType != 1);
5626                     if(!n){
5627                         r[++ri] = ci;
5628                     }
5629                 }
5630                 return r;
5631             },
5632
5633             "nth-child" : function(c, a) {
5634                 var r = [], ri = -1;
5635                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5636                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5637                 for(var i = 0, n; n = c[i]; i++){
5638                     var pn = n.parentNode;
5639                     if (batch != pn._batch) {
5640                         var j = 0;
5641                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5642                             if(cn.nodeType == 1){
5643                                cn.nodeIndex = ++j;
5644                             }
5645                         }
5646                         pn._batch = batch;
5647                     }
5648                     if (f == 1) {
5649                         if (l == 0 || n.nodeIndex == l){
5650                             r[++ri] = n;
5651                         }
5652                     } else if ((n.nodeIndex + l) % f == 0){
5653                         r[++ri] = n;
5654                     }
5655                 }
5656
5657                 return r;
5658             },
5659
5660             "only-child" : function(c){
5661                 var r = [], ri = -1;;
5662                 for(var i = 0, ci; ci = c[i]; i++){
5663                     if(!prev(ci) && !next(ci)){
5664                         r[++ri] = ci;
5665                     }
5666                 }
5667                 return r;
5668             },
5669
5670             "empty" : function(c){
5671                 var r = [], ri = -1;
5672                 for(var i = 0, ci; ci = c[i]; i++){
5673                     var cns = ci.childNodes, j = 0, cn, empty = true;
5674                     while(cn = cns[j]){
5675                         ++j;
5676                         if(cn.nodeType == 1 || cn.nodeType == 3){
5677                             empty = false;
5678                             break;
5679                         }
5680                     }
5681                     if(empty){
5682                         r[++ri] = ci;
5683                     }
5684                 }
5685                 return r;
5686             },
5687
5688             "contains" : function(c, v){
5689                 var r = [], ri = -1;
5690                 for(var i = 0, ci; ci = c[i]; i++){
5691                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5692                         r[++ri] = ci;
5693                     }
5694                 }
5695                 return r;
5696             },
5697
5698             "nodeValue" : function(c, v){
5699                 var r = [], ri = -1;
5700                 for(var i = 0, ci; ci = c[i]; i++){
5701                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5702                         r[++ri] = ci;
5703                     }
5704                 }
5705                 return r;
5706             },
5707
5708             "checked" : function(c){
5709                 var r = [], ri = -1;
5710                 for(var i = 0, ci; ci = c[i]; i++){
5711                     if(ci.checked == true){
5712                         r[++ri] = ci;
5713                     }
5714                 }
5715                 return r;
5716             },
5717
5718             "not" : function(c, ss){
5719                 return Roo.DomQuery.filter(c, ss, true);
5720             },
5721
5722             "odd" : function(c){
5723                 return this["nth-child"](c, "odd");
5724             },
5725
5726             "even" : function(c){
5727                 return this["nth-child"](c, "even");
5728             },
5729
5730             "nth" : function(c, a){
5731                 return c[a-1] || [];
5732             },
5733
5734             "first" : function(c){
5735                 return c[0] || [];
5736             },
5737
5738             "last" : function(c){
5739                 return c[c.length-1] || [];
5740             },
5741
5742             "has" : function(c, ss){
5743                 var s = Roo.DomQuery.select;
5744                 var r = [], ri = -1;
5745                 for(var i = 0, ci; ci = c[i]; i++){
5746                     if(s(ss, ci).length > 0){
5747                         r[++ri] = ci;
5748                     }
5749                 }
5750                 return r;
5751             },
5752
5753             "next" : function(c, ss){
5754                 var is = Roo.DomQuery.is;
5755                 var r = [], ri = -1;
5756                 for(var i = 0, ci; ci = c[i]; i++){
5757                     var n = next(ci);
5758                     if(n && is(n, ss)){
5759                         r[++ri] = ci;
5760                     }
5761                 }
5762                 return r;
5763             },
5764
5765             "prev" : function(c, ss){
5766                 var is = Roo.DomQuery.is;
5767                 var r = [], ri = -1;
5768                 for(var i = 0, ci; ci = c[i]; i++){
5769                     var n = prev(ci);
5770                     if(n && is(n, ss)){
5771                         r[++ri] = ci;
5772                     }
5773                 }
5774                 return r;
5775             }
5776         }
5777     };
5778 }();
5779
5780 /**
5781  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5782  * @param {String} path The selector/xpath query
5783  * @param {Node} root (optional) The start of the query (defaults to document).
5784  * @return {Array}
5785  * @member Roo
5786  * @method query
5787  */
5788 Roo.query = Roo.DomQuery.select;
5789 /*
5790  * Based on:
5791  * Ext JS Library 1.1.1
5792  * Copyright(c) 2006-2007, Ext JS, LLC.
5793  *
5794  * Originally Released Under LGPL - original licence link has changed is not relivant.
5795  *
5796  * Fork - LGPL
5797  * <script type="text/javascript">
5798  */
5799
5800 /**
5801  * @class Roo.util.Observable
5802  * Base class that provides a common interface for publishing events. Subclasses are expected to
5803  * to have a property "events" with all the events defined.<br>
5804  * For example:
5805  * <pre><code>
5806  Employee = function(name){
5807     this.name = name;
5808     this.addEvents({
5809         "fired" : true,
5810         "quit" : true
5811     });
5812  }
5813  Roo.extend(Employee, Roo.util.Observable);
5814 </code></pre>
5815  * @param {Object} config properties to use (incuding events / listeners)
5816  */
5817
5818 Roo.util.Observable = function(cfg){
5819     
5820     cfg = cfg|| {};
5821     this.addEvents(cfg.events || {});
5822     if (cfg.events) {
5823         delete cfg.events; // make sure
5824     }
5825      
5826     Roo.apply(this, cfg);
5827     
5828     if(this.listeners){
5829         this.on(this.listeners);
5830         delete this.listeners;
5831     }
5832 };
5833 Roo.util.Observable.prototype = {
5834     /** 
5835  * @cfg {Object} listeners  list of events and functions to call for this object, 
5836  * For example :
5837  * <pre><code>
5838     listeners :  { 
5839        'click' : function(e) {
5840            ..... 
5841         } ,
5842         .... 
5843     } 
5844   </code></pre>
5845  */
5846     
5847     
5848     /**
5849      * Fires the specified event with the passed parameters (minus the event name).
5850      * @param {String} eventName
5851      * @param {Object...} args Variable number of parameters are passed to handlers
5852      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5853      */
5854     fireEvent : function(){
5855         var ce = this.events[arguments[0].toLowerCase()];
5856         if(typeof ce == "object"){
5857             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5858         }else{
5859             return true;
5860         }
5861     },
5862
5863     // private
5864     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5865
5866     /**
5867      * Appends an event handler to this component
5868      * @param {String}   eventName The type of event to listen for
5869      * @param {Function} handler The method the event invokes
5870      * @param {Object}   scope (optional) The scope in which to execute the handler
5871      * function. The handler function's "this" context.
5872      * @param {Object}   options (optional) An object containing handler configuration
5873      * properties. This may contain any of the following properties:<ul>
5874      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5875      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5876      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5877      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5878      * by the specified number of milliseconds. If the event fires again within that time, the original
5879      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5880      * </ul><br>
5881      * <p>
5882      * <b>Combining Options</b><br>
5883      * Using the options argument, it is possible to combine different types of listeners:<br>
5884      * <br>
5885      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5886                 <pre><code>
5887                 el.on('click', this.onClick, this, {
5888                         single: true,
5889                 delay: 100,
5890                 forumId: 4
5891                 });
5892                 </code></pre>
5893      * <p>
5894      * <b>Attaching multiple handlers in 1 call</b><br>
5895      * The method also allows for a single argument to be passed which is a config object containing properties
5896      * which specify multiple handlers.
5897      * <pre><code>
5898                 el.on({
5899                         'click': {
5900                         fn: this.onClick,
5901                         scope: this,
5902                         delay: 100
5903                 }, 
5904                 'mouseover': {
5905                         fn: this.onMouseOver,
5906                         scope: this
5907                 },
5908                 'mouseout': {
5909                         fn: this.onMouseOut,
5910                         scope: this
5911                 }
5912                 });
5913                 </code></pre>
5914      * <p>
5915      * Or a shorthand syntax which passes the same scope object to all handlers:
5916         <pre><code>
5917                 el.on({
5918                         'click': this.onClick,
5919                 'mouseover': this.onMouseOver,
5920                 'mouseout': this.onMouseOut,
5921                 scope: this
5922                 });
5923                 </code></pre>
5924      */
5925     addListener : function(eventName, fn, scope, o){
5926         if(typeof eventName == "object"){
5927             o = eventName;
5928             for(var e in o){
5929                 if(this.filterOptRe.test(e)){
5930                     continue;
5931                 }
5932                 if(typeof o[e] == "function"){
5933                     // shared options
5934                     this.addListener(e, o[e], o.scope,  o);
5935                 }else{
5936                     // individual options
5937                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5938                 }
5939             }
5940             return;
5941         }
5942         o = (!o || typeof o == "boolean") ? {} : o;
5943         eventName = eventName.toLowerCase();
5944         var ce = this.events[eventName] || true;
5945         if(typeof ce == "boolean"){
5946             ce = new Roo.util.Event(this, eventName);
5947             this.events[eventName] = ce;
5948         }
5949         ce.addListener(fn, scope, o);
5950     },
5951
5952     /**
5953      * Removes a listener
5954      * @param {String}   eventName     The type of event to listen for
5955      * @param {Function} handler        The handler to remove
5956      * @param {Object}   scope  (optional) The scope (this object) for the handler
5957      */
5958     removeListener : function(eventName, fn, scope){
5959         var ce = this.events[eventName.toLowerCase()];
5960         if(typeof ce == "object"){
5961             ce.removeListener(fn, scope);
5962         }
5963     },
5964
5965     /**
5966      * Removes all listeners for this object
5967      */
5968     purgeListeners : function(){
5969         for(var evt in this.events){
5970             if(typeof this.events[evt] == "object"){
5971                  this.events[evt].clearListeners();
5972             }
5973         }
5974     },
5975
5976     relayEvents : function(o, events){
5977         var createHandler = function(ename){
5978             return function(){
5979                  
5980                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5981             };
5982         };
5983         for(var i = 0, len = events.length; i < len; i++){
5984             var ename = events[i];
5985             if(!this.events[ename]){
5986                 this.events[ename] = true;
5987             };
5988             o.on(ename, createHandler(ename), this);
5989         }
5990     },
5991
5992     /**
5993      * Used to define events on this Observable
5994      * @param {Object} object The object with the events defined
5995      */
5996     addEvents : function(o){
5997         if(!this.events){
5998             this.events = {};
5999         }
6000         Roo.applyIf(this.events, o);
6001     },
6002
6003     /**
6004      * Checks to see if this object has any listeners for a specified event
6005      * @param {String} eventName The name of the event to check for
6006      * @return {Boolean} True if the event is being listened for, else false
6007      */
6008     hasListener : function(eventName){
6009         var e = this.events[eventName];
6010         return typeof e == "object" && e.listeners.length > 0;
6011     }
6012 };
6013 /**
6014  * Appends an event handler to this element (shorthand for addListener)
6015  * @param {String}   eventName     The type of event to listen for
6016  * @param {Function} handler        The method the event invokes
6017  * @param {Object}   scope (optional) The scope in which to execute the handler
6018  * function. The handler function's "this" context.
6019  * @param {Object}   options  (optional)
6020  * @method
6021  */
6022 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6023 /**
6024  * Removes a listener (shorthand for removeListener)
6025  * @param {String}   eventName     The type of event to listen for
6026  * @param {Function} handler        The handler to remove
6027  * @param {Object}   scope  (optional) The scope (this object) for the handler
6028  * @method
6029  */
6030 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6031
6032 /**
6033  * Starts capture on the specified Observable. All events will be passed
6034  * to the supplied function with the event name + standard signature of the event
6035  * <b>before</b> the event is fired. If the supplied function returns false,
6036  * the event will not fire.
6037  * @param {Observable} o The Observable to capture
6038  * @param {Function} fn The function to call
6039  * @param {Object} scope (optional) The scope (this object) for the fn
6040  * @static
6041  */
6042 Roo.util.Observable.capture = function(o, fn, scope){
6043     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6044 };
6045
6046 /**
6047  * Removes <b>all</b> added captures from the Observable.
6048  * @param {Observable} o The Observable to release
6049  * @static
6050  */
6051 Roo.util.Observable.releaseCapture = function(o){
6052     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6053 };
6054
6055 (function(){
6056
6057     var createBuffered = function(h, o, scope){
6058         var task = new Roo.util.DelayedTask();
6059         return function(){
6060             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6061         };
6062     };
6063
6064     var createSingle = function(h, e, fn, scope){
6065         return function(){
6066             e.removeListener(fn, scope);
6067             return h.apply(scope, arguments);
6068         };
6069     };
6070
6071     var createDelayed = function(h, o, scope){
6072         return function(){
6073             var args = Array.prototype.slice.call(arguments, 0);
6074             setTimeout(function(){
6075                 h.apply(scope, args);
6076             }, o.delay || 10);
6077         };
6078     };
6079
6080     Roo.util.Event = function(obj, name){
6081         this.name = name;
6082         this.obj = obj;
6083         this.listeners = [];
6084     };
6085
6086     Roo.util.Event.prototype = {
6087         addListener : function(fn, scope, options){
6088             var o = options || {};
6089             scope = scope || this.obj;
6090             if(!this.isListening(fn, scope)){
6091                 var l = {fn: fn, scope: scope, options: o};
6092                 var h = fn;
6093                 if(o.delay){
6094                     h = createDelayed(h, o, scope);
6095                 }
6096                 if(o.single){
6097                     h = createSingle(h, this, fn, scope);
6098                 }
6099                 if(o.buffer){
6100                     h = createBuffered(h, o, scope);
6101                 }
6102                 l.fireFn = h;
6103                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6104                     this.listeners.push(l);
6105                 }else{
6106                     this.listeners = this.listeners.slice(0);
6107                     this.listeners.push(l);
6108                 }
6109             }
6110         },
6111
6112         findListener : function(fn, scope){
6113             scope = scope || this.obj;
6114             var ls = this.listeners;
6115             for(var i = 0, len = ls.length; i < len; i++){
6116                 var l = ls[i];
6117                 if(l.fn == fn && l.scope == scope){
6118                     return i;
6119                 }
6120             }
6121             return -1;
6122         },
6123
6124         isListening : function(fn, scope){
6125             return this.findListener(fn, scope) != -1;
6126         },
6127
6128         removeListener : function(fn, scope){
6129             var index;
6130             if((index = this.findListener(fn, scope)) != -1){
6131                 if(!this.firing){
6132                     this.listeners.splice(index, 1);
6133                 }else{
6134                     this.listeners = this.listeners.slice(0);
6135                     this.listeners.splice(index, 1);
6136                 }
6137                 return true;
6138             }
6139             return false;
6140         },
6141
6142         clearListeners : function(){
6143             this.listeners = [];
6144         },
6145
6146         fire : function(){
6147             var ls = this.listeners, scope, len = ls.length;
6148             if(len > 0){
6149                 this.firing = true;
6150                 var args = Array.prototype.slice.call(arguments, 0);                
6151                 for(var i = 0; i < len; i++){
6152                     var l = ls[i];
6153                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6154                         this.firing = false;
6155                         return false;
6156                     }
6157                 }
6158                 this.firing = false;
6159             }
6160             return true;
6161         }
6162     };
6163 })();/*
6164  * RooJS Library 
6165  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6166  *
6167  * Licence LGPL 
6168  *
6169  */
6170  
6171 /**
6172  * @class Roo.Document
6173  * @extends Roo.util.Observable
6174  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6175  * 
6176  * @param {Object} config the methods and properties of the 'base' class for the application.
6177  * 
6178  *  Generic Page handler - implement this to start your app..
6179  * 
6180  * eg.
6181  *  MyProject = new Roo.Document({
6182         events : {
6183             'load' : true // your events..
6184         },
6185         listeners : {
6186             'ready' : function() {
6187                 // fired on Roo.onReady()
6188             }
6189         }
6190  * 
6191  */
6192 Roo.Document = function(cfg) {
6193      
6194     this.addEvents({ 
6195         'ready' : true
6196     });
6197     Roo.util.Observable.call(this,cfg);
6198     
6199     var _this = this;
6200     
6201     Roo.onReady(function() {
6202         _this.fireEvent('ready');
6203     },null,false);
6204     
6205     
6206 }
6207
6208 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6209  * Based on:
6210  * Ext JS Library 1.1.1
6211  * Copyright(c) 2006-2007, Ext JS, LLC.
6212  *
6213  * Originally Released Under LGPL - original licence link has changed is not relivant.
6214  *
6215  * Fork - LGPL
6216  * <script type="text/javascript">
6217  */
6218
6219 /**
6220  * @class Roo.EventManager
6221  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6222  * several useful events directly.
6223  * See {@link Roo.EventObject} for more details on normalized event objects.
6224  * @singleton
6225  */
6226 Roo.EventManager = function(){
6227     var docReadyEvent, docReadyProcId, docReadyState = false;
6228     var resizeEvent, resizeTask, textEvent, textSize;
6229     var E = Roo.lib.Event;
6230     var D = Roo.lib.Dom;
6231
6232     
6233     
6234
6235     var fireDocReady = function(){
6236         if(!docReadyState){
6237             docReadyState = true;
6238             Roo.isReady = true;
6239             if(docReadyProcId){
6240                 clearInterval(docReadyProcId);
6241             }
6242             if(Roo.isGecko || Roo.isOpera) {
6243                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6244             }
6245             if(Roo.isIE){
6246                 var defer = document.getElementById("ie-deferred-loader");
6247                 if(defer){
6248                     defer.onreadystatechange = null;
6249                     defer.parentNode.removeChild(defer);
6250                 }
6251             }
6252             if(docReadyEvent){
6253                 docReadyEvent.fire();
6254                 docReadyEvent.clearListeners();
6255             }
6256         }
6257     };
6258     
6259     var initDocReady = function(){
6260         docReadyEvent = new Roo.util.Event();
6261         if(Roo.isGecko || Roo.isOpera) {
6262             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6263         }else if(Roo.isIE){
6264             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6265             var defer = document.getElementById("ie-deferred-loader");
6266             defer.onreadystatechange = function(){
6267                 if(this.readyState == "complete"){
6268                     fireDocReady();
6269                 }
6270             };
6271         }else if(Roo.isSafari){ 
6272             docReadyProcId = setInterval(function(){
6273                 var rs = document.readyState;
6274                 if(rs == "complete") {
6275                     fireDocReady();     
6276                  }
6277             }, 10);
6278         }
6279         // no matter what, make sure it fires on load
6280         E.on(window, "load", fireDocReady);
6281     };
6282
6283     var createBuffered = function(h, o){
6284         var task = new Roo.util.DelayedTask(h);
6285         return function(e){
6286             // create new event object impl so new events don't wipe out properties
6287             e = new Roo.EventObjectImpl(e);
6288             task.delay(o.buffer, h, null, [e]);
6289         };
6290     };
6291
6292     var createSingle = function(h, el, ename, fn){
6293         return function(e){
6294             Roo.EventManager.removeListener(el, ename, fn);
6295             h(e);
6296         };
6297     };
6298
6299     var createDelayed = function(h, o){
6300         return function(e){
6301             // create new event object impl so new events don't wipe out properties
6302             e = new Roo.EventObjectImpl(e);
6303             setTimeout(function(){
6304                 h(e);
6305             }, o.delay || 10);
6306         };
6307     };
6308     var transitionEndVal = false;
6309     
6310     var transitionEnd = function()
6311     {
6312         if (transitionEndVal) {
6313             return transitionEndVal;
6314         }
6315         var el = document.createElement('div');
6316
6317         var transEndEventNames = {
6318             WebkitTransition : 'webkitTransitionEnd',
6319             MozTransition    : 'transitionend',
6320             OTransition      : 'oTransitionEnd otransitionend',
6321             transition       : 'transitionend'
6322         };
6323     
6324         for (var name in transEndEventNames) {
6325             if (el.style[name] !== undefined) {
6326                 transitionEndVal = transEndEventNames[name];
6327                 return  transitionEndVal ;
6328             }
6329         }
6330     }
6331     
6332   
6333
6334     var listen = function(element, ename, opt, fn, scope)
6335     {
6336         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6337         fn = fn || o.fn; scope = scope || o.scope;
6338         var el = Roo.getDom(element);
6339         
6340         
6341         if(!el){
6342             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6343         }
6344         
6345         if (ename == 'transitionend') {
6346             ename = transitionEnd();
6347         }
6348         var h = function(e){
6349             e = Roo.EventObject.setEvent(e);
6350             var t;
6351             if(o.delegate){
6352                 t = e.getTarget(o.delegate, el);
6353                 if(!t){
6354                     return;
6355                 }
6356             }else{
6357                 t = e.target;
6358             }
6359             if(o.stopEvent === true){
6360                 e.stopEvent();
6361             }
6362             if(o.preventDefault === true){
6363                e.preventDefault();
6364             }
6365             if(o.stopPropagation === true){
6366                 e.stopPropagation();
6367             }
6368
6369             if(o.normalized === false){
6370                 e = e.browserEvent;
6371             }
6372
6373             fn.call(scope || el, e, t, o);
6374         };
6375         if(o.delay){
6376             h = createDelayed(h, o);
6377         }
6378         if(o.single){
6379             h = createSingle(h, el, ename, fn);
6380         }
6381         if(o.buffer){
6382             h = createBuffered(h, o);
6383         }
6384         
6385         fn._handlers = fn._handlers || [];
6386         
6387         
6388         fn._handlers.push([Roo.id(el), ename, h]);
6389         
6390         
6391          
6392         E.on(el, ename, h); // this adds the actuall listener to the object..
6393         
6394         
6395         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6396             el.addEventListener("DOMMouseScroll", h, false);
6397             E.on(window, 'unload', function(){
6398                 el.removeEventListener("DOMMouseScroll", h, false);
6399             });
6400         }
6401         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6402             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6403         }
6404         return h;
6405     };
6406
6407     var stopListening = function(el, ename, fn){
6408         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6409         if(hds){
6410             for(var i = 0, len = hds.length; i < len; i++){
6411                 var h = hds[i];
6412                 if(h[0] == id && h[1] == ename){
6413                     hd = h[2];
6414                     hds.splice(i, 1);
6415                     break;
6416                 }
6417             }
6418         }
6419         E.un(el, ename, hd);
6420         el = Roo.getDom(el);
6421         if(ename == "mousewheel" && el.addEventListener){
6422             el.removeEventListener("DOMMouseScroll", hd, false);
6423         }
6424         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6425             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6426         }
6427     };
6428
6429     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6430     
6431     var pub = {
6432         
6433         
6434         /** 
6435          * Fix for doc tools
6436          * @scope Roo.EventManager
6437          */
6438         
6439         
6440         /** 
6441          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6442          * object with a Roo.EventObject
6443          * @param {Function} fn        The method the event invokes
6444          * @param {Object}   scope    An object that becomes the scope of the handler
6445          * @param {boolean}  override If true, the obj passed in becomes
6446          *                             the execution scope of the listener
6447          * @return {Function} The wrapped function
6448          * @deprecated
6449          */
6450         wrap : function(fn, scope, override){
6451             return function(e){
6452                 Roo.EventObject.setEvent(e);
6453                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6454             };
6455         },
6456         
6457         /**
6458      * Appends an event handler to an element (shorthand for addListener)
6459      * @param {String/HTMLElement}   element        The html element or id to assign the
6460      * @param {String}   eventName The type of event to listen for
6461      * @param {Function} handler The method the event invokes
6462      * @param {Object}   scope (optional) The scope in which to execute the handler
6463      * function. The handler function's "this" context.
6464      * @param {Object}   options (optional) An object containing handler configuration
6465      * properties. This may contain any of the following properties:<ul>
6466      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6467      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6468      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6469      * <li>preventDefault {Boolean} True to prevent the default action</li>
6470      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6471      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6472      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6473      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6474      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6475      * by the specified number of milliseconds. If the event fires again within that time, the original
6476      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6477      * </ul><br>
6478      * <p>
6479      * <b>Combining Options</b><br>
6480      * Using the options argument, it is possible to combine different types of listeners:<br>
6481      * <br>
6482      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6483      * Code:<pre><code>
6484 el.on('click', this.onClick, this, {
6485     single: true,
6486     delay: 100,
6487     stopEvent : true,
6488     forumId: 4
6489 });</code></pre>
6490      * <p>
6491      * <b>Attaching multiple handlers in 1 call</b><br>
6492       * The method also allows for a single argument to be passed which is a config object containing properties
6493      * which specify multiple handlers.
6494      * <p>
6495      * Code:<pre><code>
6496 el.on({
6497     'click' : {
6498         fn: this.onClick
6499         scope: this,
6500         delay: 100
6501     },
6502     'mouseover' : {
6503         fn: this.onMouseOver
6504         scope: this
6505     },
6506     'mouseout' : {
6507         fn: this.onMouseOut
6508         scope: this
6509     }
6510 });</code></pre>
6511      * <p>
6512      * Or a shorthand syntax:<br>
6513      * Code:<pre><code>
6514 el.on({
6515     'click' : this.onClick,
6516     'mouseover' : this.onMouseOver,
6517     'mouseout' : this.onMouseOut
6518     scope: this
6519 });</code></pre>
6520      */
6521         addListener : function(element, eventName, fn, scope, options){
6522             if(typeof eventName == "object"){
6523                 var o = eventName;
6524                 for(var e in o){
6525                     if(propRe.test(e)){
6526                         continue;
6527                     }
6528                     if(typeof o[e] == "function"){
6529                         // shared options
6530                         listen(element, e, o, o[e], o.scope);
6531                     }else{
6532                         // individual options
6533                         listen(element, e, o[e]);
6534                     }
6535                 }
6536                 return;
6537             }
6538             return listen(element, eventName, options, fn, scope);
6539         },
6540         
6541         /**
6542          * Removes an event handler
6543          *
6544          * @param {String/HTMLElement}   element        The id or html element to remove the 
6545          *                             event from
6546          * @param {String}   eventName     The type of event
6547          * @param {Function} fn
6548          * @return {Boolean} True if a listener was actually removed
6549          */
6550         removeListener : function(element, eventName, fn){
6551             return stopListening(element, eventName, fn);
6552         },
6553         
6554         /**
6555          * Fires when the document is ready (before onload and before images are loaded). Can be 
6556          * accessed shorthanded Roo.onReady().
6557          * @param {Function} fn        The method the event invokes
6558          * @param {Object}   scope    An  object that becomes the scope of the handler
6559          * @param {boolean}  options
6560          */
6561         onDocumentReady : function(fn, scope, options){
6562             if(docReadyState){ // if it already fired
6563                 docReadyEvent.addListener(fn, scope, options);
6564                 docReadyEvent.fire();
6565                 docReadyEvent.clearListeners();
6566                 return;
6567             }
6568             if(!docReadyEvent){
6569                 initDocReady();
6570             }
6571             docReadyEvent.addListener(fn, scope, options);
6572         },
6573         
6574         /**
6575          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6576          * @param {Function} fn        The method the event invokes
6577          * @param {Object}   scope    An object that becomes the scope of the handler
6578          * @param {boolean}  options
6579          */
6580         onWindowResize : function(fn, scope, options){
6581             if(!resizeEvent){
6582                 resizeEvent = new Roo.util.Event();
6583                 resizeTask = new Roo.util.DelayedTask(function(){
6584                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6585                 });
6586                 E.on(window, "resize", function(){
6587                     if(Roo.isIE){
6588                         resizeTask.delay(50);
6589                     }else{
6590                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6591                     }
6592                 });
6593             }
6594             resizeEvent.addListener(fn, scope, options);
6595         },
6596
6597         /**
6598          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6599          * @param {Function} fn        The method the event invokes
6600          * @param {Object}   scope    An object that becomes the scope of the handler
6601          * @param {boolean}  options
6602          */
6603         onTextResize : function(fn, scope, options){
6604             if(!textEvent){
6605                 textEvent = new Roo.util.Event();
6606                 var textEl = new Roo.Element(document.createElement('div'));
6607                 textEl.dom.className = 'x-text-resize';
6608                 textEl.dom.innerHTML = 'X';
6609                 textEl.appendTo(document.body);
6610                 textSize = textEl.dom.offsetHeight;
6611                 setInterval(function(){
6612                     if(textEl.dom.offsetHeight != textSize){
6613                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6614                     }
6615                 }, this.textResizeInterval);
6616             }
6617             textEvent.addListener(fn, scope, options);
6618         },
6619
6620         /**
6621          * Removes the passed window resize listener.
6622          * @param {Function} fn        The method the event invokes
6623          * @param {Object}   scope    The scope of handler
6624          */
6625         removeResizeListener : function(fn, scope){
6626             if(resizeEvent){
6627                 resizeEvent.removeListener(fn, scope);
6628             }
6629         },
6630
6631         // private
6632         fireResize : function(){
6633             if(resizeEvent){
6634                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6635             }   
6636         },
6637         /**
6638          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6639          */
6640         ieDeferSrc : false,
6641         /**
6642          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6643          */
6644         textResizeInterval : 50
6645     };
6646     
6647     /**
6648      * Fix for doc tools
6649      * @scopeAlias pub=Roo.EventManager
6650      */
6651     
6652      /**
6653      * Appends an event handler to an element (shorthand for addListener)
6654      * @param {String/HTMLElement}   element        The html element or id to assign the
6655      * @param {String}   eventName The type of event to listen for
6656      * @param {Function} handler The method the event invokes
6657      * @param {Object}   scope (optional) The scope in which to execute the handler
6658      * function. The handler function's "this" context.
6659      * @param {Object}   options (optional) An object containing handler configuration
6660      * properties. This may contain any of the following properties:<ul>
6661      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6662      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6663      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6664      * <li>preventDefault {Boolean} True to prevent the default action</li>
6665      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6666      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6667      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6668      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6669      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6670      * by the specified number of milliseconds. If the event fires again within that time, the original
6671      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6672      * </ul><br>
6673      * <p>
6674      * <b>Combining Options</b><br>
6675      * Using the options argument, it is possible to combine different types of listeners:<br>
6676      * <br>
6677      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6678      * Code:<pre><code>
6679 el.on('click', this.onClick, this, {
6680     single: true,
6681     delay: 100,
6682     stopEvent : true,
6683     forumId: 4
6684 });</code></pre>
6685      * <p>
6686      * <b>Attaching multiple handlers in 1 call</b><br>
6687       * The method also allows for a single argument to be passed which is a config object containing properties
6688      * which specify multiple handlers.
6689      * <p>
6690      * Code:<pre><code>
6691 el.on({
6692     'click' : {
6693         fn: this.onClick
6694         scope: this,
6695         delay: 100
6696     },
6697     'mouseover' : {
6698         fn: this.onMouseOver
6699         scope: this
6700     },
6701     'mouseout' : {
6702         fn: this.onMouseOut
6703         scope: this
6704     }
6705 });</code></pre>
6706      * <p>
6707      * Or a shorthand syntax:<br>
6708      * Code:<pre><code>
6709 el.on({
6710     'click' : this.onClick,
6711     'mouseover' : this.onMouseOver,
6712     'mouseout' : this.onMouseOut
6713     scope: this
6714 });</code></pre>
6715      */
6716     pub.on = pub.addListener;
6717     pub.un = pub.removeListener;
6718
6719     pub.stoppedMouseDownEvent = new Roo.util.Event();
6720     return pub;
6721 }();
6722 /**
6723   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6724   * @param {Function} fn        The method the event invokes
6725   * @param {Object}   scope    An  object that becomes the scope of the handler
6726   * @param {boolean}  override If true, the obj passed in becomes
6727   *                             the execution scope of the listener
6728   * @member Roo
6729   * @method onReady
6730  */
6731 Roo.onReady = Roo.EventManager.onDocumentReady;
6732
6733 Roo.onReady(function(){
6734     var bd = Roo.get(document.body);
6735     if(!bd){ return; }
6736
6737     var cls = [
6738             Roo.isIE ? "roo-ie"
6739             : Roo.isIE11 ? "roo-ie11"
6740             : Roo.isEdge ? "roo-edge"
6741             : Roo.isGecko ? "roo-gecko"
6742             : Roo.isOpera ? "roo-opera"
6743             : Roo.isSafari ? "roo-safari" : ""];
6744
6745     if(Roo.isMac){
6746         cls.push("roo-mac");
6747     }
6748     if(Roo.isLinux){
6749         cls.push("roo-linux");
6750     }
6751     if(Roo.isIOS){
6752         cls.push("roo-ios");
6753     }
6754     if(Roo.isTouch){
6755         cls.push("roo-touch");
6756     }
6757     if(Roo.isBorderBox){
6758         cls.push('roo-border-box');
6759     }
6760     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6761         var p = bd.dom.parentNode;
6762         if(p){
6763             p.className += ' roo-strict';
6764         }
6765     }
6766     bd.addClass(cls.join(' '));
6767 });
6768
6769 /**
6770  * @class Roo.EventObject
6771  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6772  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6773  * Example:
6774  * <pre><code>
6775  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6776     e.preventDefault();
6777     var target = e.getTarget();
6778     ...
6779  }
6780  var myDiv = Roo.get("myDiv");
6781  myDiv.on("click", handleClick);
6782  //or
6783  Roo.EventManager.on("myDiv", 'click', handleClick);
6784  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6785  </code></pre>
6786  * @singleton
6787  */
6788 Roo.EventObject = function(){
6789     
6790     var E = Roo.lib.Event;
6791     
6792     // safari keypress events for special keys return bad keycodes
6793     var safariKeys = {
6794         63234 : 37, // left
6795         63235 : 39, // right
6796         63232 : 38, // up
6797         63233 : 40, // down
6798         63276 : 33, // page up
6799         63277 : 34, // page down
6800         63272 : 46, // delete
6801         63273 : 36, // home
6802         63275 : 35  // end
6803     };
6804
6805     // normalize button clicks
6806     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6807                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6808
6809     Roo.EventObjectImpl = function(e){
6810         if(e){
6811             this.setEvent(e.browserEvent || e);
6812         }
6813     };
6814     Roo.EventObjectImpl.prototype = {
6815         /**
6816          * Used to fix doc tools.
6817          * @scope Roo.EventObject.prototype
6818          */
6819             
6820
6821         
6822         
6823         /** The normal browser event */
6824         browserEvent : null,
6825         /** The button pressed in a mouse event */
6826         button : -1,
6827         /** True if the shift key was down during the event */
6828         shiftKey : false,
6829         /** True if the control key was down during the event */
6830         ctrlKey : false,
6831         /** True if the alt key was down during the event */
6832         altKey : false,
6833
6834         /** Key constant 
6835         * @type Number */
6836         BACKSPACE : 8,
6837         /** Key constant 
6838         * @type Number */
6839         TAB : 9,
6840         /** Key constant 
6841         * @type Number */
6842         RETURN : 13,
6843         /** Key constant 
6844         * @type Number */
6845         ENTER : 13,
6846         /** Key constant 
6847         * @type Number */
6848         SHIFT : 16,
6849         /** Key constant 
6850         * @type Number */
6851         CONTROL : 17,
6852         /** Key constant 
6853         * @type Number */
6854         ESC : 27,
6855         /** Key constant 
6856         * @type Number */
6857         SPACE : 32,
6858         /** Key constant 
6859         * @type Number */
6860         PAGEUP : 33,
6861         /** Key constant 
6862         * @type Number */
6863         PAGEDOWN : 34,
6864         /** Key constant 
6865         * @type Number */
6866         END : 35,
6867         /** Key constant 
6868         * @type Number */
6869         HOME : 36,
6870         /** Key constant 
6871         * @type Number */
6872         LEFT : 37,
6873         /** Key constant 
6874         * @type Number */
6875         UP : 38,
6876         /** Key constant 
6877         * @type Number */
6878         RIGHT : 39,
6879         /** Key constant 
6880         * @type Number */
6881         DOWN : 40,
6882         /** Key constant 
6883         * @type Number */
6884         DELETE : 46,
6885         /** Key constant 
6886         * @type Number */
6887         F5 : 116,
6888
6889            /** @private */
6890         setEvent : function(e){
6891             if(e == this || (e && e.browserEvent)){ // already wrapped
6892                 return e;
6893             }
6894             this.browserEvent = e;
6895             if(e){
6896                 // normalize buttons
6897                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6898                 if(e.type == 'click' && this.button == -1){
6899                     this.button = 0;
6900                 }
6901                 this.type = e.type;
6902                 this.shiftKey = e.shiftKey;
6903                 // mac metaKey behaves like ctrlKey
6904                 this.ctrlKey = e.ctrlKey || e.metaKey;
6905                 this.altKey = e.altKey;
6906                 // in getKey these will be normalized for the mac
6907                 this.keyCode = e.keyCode;
6908                 // keyup warnings on firefox.
6909                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6910                 // cache the target for the delayed and or buffered events
6911                 this.target = E.getTarget(e);
6912                 // same for XY
6913                 this.xy = E.getXY(e);
6914             }else{
6915                 this.button = -1;
6916                 this.shiftKey = false;
6917                 this.ctrlKey = false;
6918                 this.altKey = false;
6919                 this.keyCode = 0;
6920                 this.charCode =0;
6921                 this.target = null;
6922                 this.xy = [0, 0];
6923             }
6924             return this;
6925         },
6926
6927         /**
6928          * Stop the event (preventDefault and stopPropagation)
6929          */
6930         stopEvent : function(){
6931             if(this.browserEvent){
6932                 if(this.browserEvent.type == 'mousedown'){
6933                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6934                 }
6935                 E.stopEvent(this.browserEvent);
6936             }
6937         },
6938
6939         /**
6940          * Prevents the browsers default handling of the event.
6941          */
6942         preventDefault : function(){
6943             if(this.browserEvent){
6944                 E.preventDefault(this.browserEvent);
6945             }
6946         },
6947
6948         /** @private */
6949         isNavKeyPress : function(){
6950             var k = this.keyCode;
6951             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6952             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6953         },
6954
6955         isSpecialKey : function(){
6956             var k = this.keyCode;
6957             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6958             (k == 16) || (k == 17) ||
6959             (k >= 18 && k <= 20) ||
6960             (k >= 33 && k <= 35) ||
6961             (k >= 36 && k <= 39) ||
6962             (k >= 44 && k <= 45);
6963         },
6964         /**
6965          * Cancels bubbling of the event.
6966          */
6967         stopPropagation : function(){
6968             if(this.browserEvent){
6969                 if(this.type == 'mousedown'){
6970                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6971                 }
6972                 E.stopPropagation(this.browserEvent);
6973             }
6974         },
6975
6976         /**
6977          * Gets the key code for the event.
6978          * @return {Number}
6979          */
6980         getCharCode : function(){
6981             return this.charCode || this.keyCode;
6982         },
6983
6984         /**
6985          * Returns a normalized keyCode for the event.
6986          * @return {Number} The key code
6987          */
6988         getKey : function(){
6989             var k = this.keyCode || this.charCode;
6990             return Roo.isSafari ? (safariKeys[k] || k) : k;
6991         },
6992
6993         /**
6994          * Gets the x coordinate of the event.
6995          * @return {Number}
6996          */
6997         getPageX : function(){
6998             return this.xy[0];
6999         },
7000
7001         /**
7002          * Gets the y coordinate of the event.
7003          * @return {Number}
7004          */
7005         getPageY : function(){
7006             return this.xy[1];
7007         },
7008
7009         /**
7010          * Gets the time of the event.
7011          * @return {Number}
7012          */
7013         getTime : function(){
7014             if(this.browserEvent){
7015                 return E.getTime(this.browserEvent);
7016             }
7017             return null;
7018         },
7019
7020         /**
7021          * Gets the page coordinates of the event.
7022          * @return {Array} The xy values like [x, y]
7023          */
7024         getXY : function(){
7025             return this.xy;
7026         },
7027
7028         /**
7029          * Gets the target for the event.
7030          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7031          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7032                 search as a number or element (defaults to 10 || document.body)
7033          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7034          * @return {HTMLelement}
7035          */
7036         getTarget : function(selector, maxDepth, returnEl){
7037             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7038         },
7039         /**
7040          * Gets the related target.
7041          * @return {HTMLElement}
7042          */
7043         getRelatedTarget : function(){
7044             if(this.browserEvent){
7045                 return E.getRelatedTarget(this.browserEvent);
7046             }
7047             return null;
7048         },
7049
7050         /**
7051          * Normalizes mouse wheel delta across browsers
7052          * @return {Number} The delta
7053          */
7054         getWheelDelta : function(){
7055             var e = this.browserEvent;
7056             var delta = 0;
7057             if(e.wheelDelta){ /* IE/Opera. */
7058                 delta = e.wheelDelta/120;
7059             }else if(e.detail){ /* Mozilla case. */
7060                 delta = -e.detail/3;
7061             }
7062             return delta;
7063         },
7064
7065         /**
7066          * Returns true if the control, meta, shift or alt key was pressed during this event.
7067          * @return {Boolean}
7068          */
7069         hasModifier : function(){
7070             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7071         },
7072
7073         /**
7074          * Returns true if the target of this event equals el or is a child of el
7075          * @param {String/HTMLElement/Element} el
7076          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7077          * @return {Boolean}
7078          */
7079         within : function(el, related){
7080             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7081             return t && Roo.fly(el).contains(t);
7082         },
7083
7084         getPoint : function(){
7085             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7086         }
7087     };
7088
7089     return new Roo.EventObjectImpl();
7090 }();
7091             
7092     /*
7093  * Based on:
7094  * Ext JS Library 1.1.1
7095  * Copyright(c) 2006-2007, Ext JS, LLC.
7096  *
7097  * Originally Released Under LGPL - original licence link has changed is not relivant.
7098  *
7099  * Fork - LGPL
7100  * <script type="text/javascript">
7101  */
7102
7103  
7104 // was in Composite Element!??!?!
7105  
7106 (function(){
7107     var D = Roo.lib.Dom;
7108     var E = Roo.lib.Event;
7109     var A = Roo.lib.Anim;
7110
7111     // local style camelizing for speed
7112     var propCache = {};
7113     var camelRe = /(-[a-z])/gi;
7114     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7115     var view = document.defaultView;
7116
7117 /**
7118  * @class Roo.Element
7119  * Represents an Element in the DOM.<br><br>
7120  * Usage:<br>
7121 <pre><code>
7122 var el = Roo.get("my-div");
7123
7124 // or with getEl
7125 var el = getEl("my-div");
7126
7127 // or with a DOM element
7128 var el = Roo.get(myDivElement);
7129 </code></pre>
7130  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7131  * each call instead of constructing a new one.<br><br>
7132  * <b>Animations</b><br />
7133  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7134  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7135 <pre>
7136 Option    Default   Description
7137 --------- --------  ---------------------------------------------
7138 duration  .35       The duration of the animation in seconds
7139 easing    easeOut   The YUI easing method
7140 callback  none      A function to execute when the anim completes
7141 scope     this      The scope (this) of the callback function
7142 </pre>
7143 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7144 * manipulate the animation. Here's an example:
7145 <pre><code>
7146 var el = Roo.get("my-div");
7147
7148 // no animation
7149 el.setWidth(100);
7150
7151 // default animation
7152 el.setWidth(100, true);
7153
7154 // animation with some options set
7155 el.setWidth(100, {
7156     duration: 1,
7157     callback: this.foo,
7158     scope: this
7159 });
7160
7161 // using the "anim" property to get the Anim object
7162 var opt = {
7163     duration: 1,
7164     callback: this.foo,
7165     scope: this
7166 };
7167 el.setWidth(100, opt);
7168 ...
7169 if(opt.anim.isAnimated()){
7170     opt.anim.stop();
7171 }
7172 </code></pre>
7173 * <b> Composite (Collections of) Elements</b><br />
7174  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7175  * @constructor Create a new Element directly.
7176  * @param {String/HTMLElement} element
7177  * @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).
7178  */
7179     Roo.Element = function(element, forceNew)
7180     {
7181         var dom = typeof element == "string" ?
7182                 document.getElementById(element) : element;
7183         
7184         this.listeners = {};
7185         
7186         if(!dom){ // invalid id/element
7187             return null;
7188         }
7189         var id = dom.id;
7190         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7191             return Roo.Element.cache[id];
7192         }
7193
7194         /**
7195          * The DOM element
7196          * @type HTMLElement
7197          */
7198         this.dom = dom;
7199
7200         /**
7201          * The DOM element ID
7202          * @type String
7203          */
7204         this.id = id || Roo.id(dom);
7205         
7206         return this; // assumed for cctor?
7207     };
7208
7209     var El = Roo.Element;
7210
7211     El.prototype = {
7212         /**
7213          * The element's default display mode  (defaults to "") 
7214          * @type String
7215          */
7216         originalDisplay : "",
7217
7218         
7219         // note this is overridden in BS version..
7220         visibilityMode : 1, 
7221         /**
7222          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7223          * @type String
7224          */
7225         defaultUnit : "px",
7226         
7227         /**
7228          * Sets the element's visibility mode. When setVisible() is called it
7229          * will use this to determine whether to set the visibility or the display property.
7230          * @param visMode Element.VISIBILITY or Element.DISPLAY
7231          * @return {Roo.Element} this
7232          */
7233         setVisibilityMode : function(visMode){
7234             this.visibilityMode = visMode;
7235             return this;
7236         },
7237         /**
7238          * Convenience method for setVisibilityMode(Element.DISPLAY)
7239          * @param {String} display (optional) What to set display to when visible
7240          * @return {Roo.Element} this
7241          */
7242         enableDisplayMode : function(display){
7243             this.setVisibilityMode(El.DISPLAY);
7244             if(typeof display != "undefined") { this.originalDisplay = display; }
7245             return this;
7246         },
7247
7248         /**
7249          * 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)
7250          * @param {String} selector The simple selector to test
7251          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7252                 search as a number or element (defaults to 10 || document.body)
7253          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7254          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7255          */
7256         findParent : function(simpleSelector, maxDepth, returnEl){
7257             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7258             maxDepth = maxDepth || 50;
7259             if(typeof maxDepth != "number"){
7260                 stopEl = Roo.getDom(maxDepth);
7261                 maxDepth = 10;
7262             }
7263             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7264                 if(dq.is(p, simpleSelector)){
7265                     return returnEl ? Roo.get(p) : p;
7266                 }
7267                 depth++;
7268                 p = p.parentNode;
7269             }
7270             return null;
7271         },
7272
7273
7274         /**
7275          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7276          * @param {String} selector The simple selector to test
7277          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7278                 search as a number or element (defaults to 10 || document.body)
7279          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7280          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7281          */
7282         findParentNode : function(simpleSelector, maxDepth, returnEl){
7283             var p = Roo.fly(this.dom.parentNode, '_internal');
7284             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7285         },
7286         
7287         /**
7288          * Looks at  the scrollable parent element
7289          */
7290         findScrollableParent : function()
7291         {
7292             var overflowRegex = /(auto|scroll)/;
7293             
7294             if(this.getStyle('position') === 'fixed'){
7295                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7296             }
7297             
7298             var excludeStaticParent = this.getStyle('position') === "absolute";
7299             
7300             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7301                 
7302                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7303                     continue;
7304                 }
7305                 
7306                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7307                     return parent;
7308                 }
7309                 
7310                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7311                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7312                 }
7313             }
7314             
7315             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7316         },
7317
7318         /**
7319          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7320          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7321          * @param {String} selector The simple selector to test
7322          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7323                 search as a number or element (defaults to 10 || document.body)
7324          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7325          */
7326         up : function(simpleSelector, maxDepth){
7327             return this.findParentNode(simpleSelector, maxDepth, true);
7328         },
7329
7330
7331
7332         /**
7333          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7334          * @param {String} selector The simple selector to test
7335          * @return {Boolean} True if this element matches the selector, else false
7336          */
7337         is : function(simpleSelector){
7338             return Roo.DomQuery.is(this.dom, simpleSelector);
7339         },
7340
7341         /**
7342          * Perform animation on this element.
7343          * @param {Object} args The YUI animation control args
7344          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7345          * @param {Function} onComplete (optional) Function to call when animation completes
7346          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7347          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7348          * @return {Roo.Element} this
7349          */
7350         animate : function(args, duration, onComplete, easing, animType){
7351             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7352             return this;
7353         },
7354
7355         /*
7356          * @private Internal animation call
7357          */
7358         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7359             animType = animType || 'run';
7360             opt = opt || {};
7361             var anim = Roo.lib.Anim[animType](
7362                 this.dom, args,
7363                 (opt.duration || defaultDur) || .35,
7364                 (opt.easing || defaultEase) || 'easeOut',
7365                 function(){
7366                     Roo.callback(cb, this);
7367                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7368                 },
7369                 this
7370             );
7371             opt.anim = anim;
7372             return anim;
7373         },
7374
7375         // private legacy anim prep
7376         preanim : function(a, i){
7377             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7378         },
7379
7380         /**
7381          * Removes worthless text nodes
7382          * @param {Boolean} forceReclean (optional) By default the element
7383          * keeps track if it has been cleaned already so
7384          * you can call this over and over. However, if you update the element and
7385          * need to force a reclean, you can pass true.
7386          */
7387         clean : function(forceReclean){
7388             if(this.isCleaned && forceReclean !== true){
7389                 return this;
7390             }
7391             var ns = /\S/;
7392             var d = this.dom, n = d.firstChild, ni = -1;
7393             while(n){
7394                 var nx = n.nextSibling;
7395                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7396                     d.removeChild(n);
7397                 }else{
7398                     n.nodeIndex = ++ni;
7399                 }
7400                 n = nx;
7401             }
7402             this.isCleaned = true;
7403             return this;
7404         },
7405
7406         // private
7407         calcOffsetsTo : function(el){
7408             el = Roo.get(el);
7409             var d = el.dom;
7410             var restorePos = false;
7411             if(el.getStyle('position') == 'static'){
7412                 el.position('relative');
7413                 restorePos = true;
7414             }
7415             var x = 0, y =0;
7416             var op = this.dom;
7417             while(op && op != d && op.tagName != 'HTML'){
7418                 x+= op.offsetLeft;
7419                 y+= op.offsetTop;
7420                 op = op.offsetParent;
7421             }
7422             if(restorePos){
7423                 el.position('static');
7424             }
7425             return [x, y];
7426         },
7427
7428         /**
7429          * Scrolls this element into view within the passed container.
7430          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7431          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7432          * @return {Roo.Element} this
7433          */
7434         scrollIntoView : function(container, hscroll){
7435             var c = Roo.getDom(container) || document.body;
7436             var el = this.dom;
7437
7438             var o = this.calcOffsetsTo(c),
7439                 l = o[0],
7440                 t = o[1],
7441                 b = t+el.offsetHeight,
7442                 r = l+el.offsetWidth;
7443
7444             var ch = c.clientHeight;
7445             var ct = parseInt(c.scrollTop, 10);
7446             var cl = parseInt(c.scrollLeft, 10);
7447             var cb = ct + ch;
7448             var cr = cl + c.clientWidth;
7449
7450             if(t < ct){
7451                 c.scrollTop = t;
7452             }else if(b > cb){
7453                 c.scrollTop = b-ch;
7454             }
7455
7456             if(hscroll !== false){
7457                 if(l < cl){
7458                     c.scrollLeft = l;
7459                 }else if(r > cr){
7460                     c.scrollLeft = r-c.clientWidth;
7461                 }
7462             }
7463             return this;
7464         },
7465
7466         // private
7467         scrollChildIntoView : function(child, hscroll){
7468             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7469         },
7470
7471         /**
7472          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7473          * the new height may not be available immediately.
7474          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7475          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7476          * @param {Function} onComplete (optional) Function to call when animation completes
7477          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7478          * @return {Roo.Element} this
7479          */
7480         autoHeight : function(animate, duration, onComplete, easing){
7481             var oldHeight = this.getHeight();
7482             this.clip();
7483             this.setHeight(1); // force clipping
7484             setTimeout(function(){
7485                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7486                 if(!animate){
7487                     this.setHeight(height);
7488                     this.unclip();
7489                     if(typeof onComplete == "function"){
7490                         onComplete();
7491                     }
7492                 }else{
7493                     this.setHeight(oldHeight); // restore original height
7494                     this.setHeight(height, animate, duration, function(){
7495                         this.unclip();
7496                         if(typeof onComplete == "function") { onComplete(); }
7497                     }.createDelegate(this), easing);
7498                 }
7499             }.createDelegate(this), 0);
7500             return this;
7501         },
7502
7503         /**
7504          * Returns true if this element is an ancestor of the passed element
7505          * @param {HTMLElement/String} el The element to check
7506          * @return {Boolean} True if this element is an ancestor of el, else false
7507          */
7508         contains : function(el){
7509             if(!el){return false;}
7510             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7511         },
7512
7513         /**
7514          * Checks whether the element is currently visible using both visibility and display properties.
7515          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7516          * @return {Boolean} True if the element is currently visible, else false
7517          */
7518         isVisible : function(deep) {
7519             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7520             if(deep !== true || !vis){
7521                 return vis;
7522             }
7523             var p = this.dom.parentNode;
7524             while(p && p.tagName.toLowerCase() != "body"){
7525                 if(!Roo.fly(p, '_isVisible').isVisible()){
7526                     return false;
7527                 }
7528                 p = p.parentNode;
7529             }
7530             return true;
7531         },
7532
7533         /**
7534          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7535          * @param {String} selector The CSS selector
7536          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7537          * @return {CompositeElement/CompositeElementLite} The composite element
7538          */
7539         select : function(selector, unique){
7540             return El.select(selector, unique, this.dom);
7541         },
7542
7543         /**
7544          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7545          * @param {String} selector The CSS selector
7546          * @return {Array} An array of the matched nodes
7547          */
7548         query : function(selector, unique){
7549             return Roo.DomQuery.select(selector, this.dom);
7550         },
7551
7552         /**
7553          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7554          * @param {String} selector The CSS selector
7555          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7556          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7557          */
7558         child : function(selector, returnDom){
7559             var n = Roo.DomQuery.selectNode(selector, this.dom);
7560             return returnDom ? n : Roo.get(n);
7561         },
7562
7563         /**
7564          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7565          * @param {String} selector The CSS selector
7566          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7567          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7568          */
7569         down : function(selector, returnDom){
7570             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7571             return returnDom ? n : Roo.get(n);
7572         },
7573
7574         /**
7575          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7576          * @param {String} group The group the DD object is member of
7577          * @param {Object} config The DD config object
7578          * @param {Object} overrides An object containing methods to override/implement on the DD object
7579          * @return {Roo.dd.DD} The DD object
7580          */
7581         initDD : function(group, config, overrides){
7582             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7583             return Roo.apply(dd, overrides);
7584         },
7585
7586         /**
7587          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7588          * @param {String} group The group the DDProxy object is member of
7589          * @param {Object} config The DDProxy config object
7590          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7591          * @return {Roo.dd.DDProxy} The DDProxy object
7592          */
7593         initDDProxy : function(group, config, overrides){
7594             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7595             return Roo.apply(dd, overrides);
7596         },
7597
7598         /**
7599          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7600          * @param {String} group The group the DDTarget object is member of
7601          * @param {Object} config The DDTarget config object
7602          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7603          * @return {Roo.dd.DDTarget} The DDTarget object
7604          */
7605         initDDTarget : function(group, config, overrides){
7606             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7607             return Roo.apply(dd, overrides);
7608         },
7609
7610         /**
7611          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7612          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7613          * @param {Boolean} visible Whether the element is visible
7614          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7615          * @return {Roo.Element} this
7616          */
7617          setVisible : function(visible, animate){
7618             if(!animate || !A){
7619                 if(this.visibilityMode == El.DISPLAY){
7620                     this.setDisplayed(visible);
7621                 }else{
7622                     this.fixDisplay();
7623                     this.dom.style.visibility = visible ? "visible" : "hidden";
7624                 }
7625             }else{
7626                 // closure for composites
7627                 var dom = this.dom;
7628                 var visMode = this.visibilityMode;
7629                 if(visible){
7630                     this.setOpacity(.01);
7631                     this.setVisible(true);
7632                 }
7633                 this.anim({opacity: { to: (visible?1:0) }},
7634                       this.preanim(arguments, 1),
7635                       null, .35, 'easeIn', function(){
7636                          if(!visible){
7637                              if(visMode == El.DISPLAY){
7638                                  dom.style.display = "none";
7639                              }else{
7640                                  dom.style.visibility = "hidden";
7641                              }
7642                              Roo.get(dom).setOpacity(1);
7643                          }
7644                      });
7645             }
7646             return this;
7647         },
7648
7649         /**
7650          * Returns true if display is not "none"
7651          * @return {Boolean}
7652          */
7653         isDisplayed : function() {
7654             return this.getStyle("display") != "none";
7655         },
7656
7657         /**
7658          * Toggles the element's visibility or display, depending on visibility mode.
7659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662         toggle : function(animate){
7663             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7669          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7670          * @return {Roo.Element} this
7671          */
7672         setDisplayed : function(value) {
7673             if(typeof value == "boolean"){
7674                value = value ? this.originalDisplay : "none";
7675             }
7676             this.setStyle("display", value);
7677             return this;
7678         },
7679
7680         /**
7681          * Tries to focus the element. Any exceptions are caught and ignored.
7682          * @return {Roo.Element} this
7683          */
7684         focus : function() {
7685             try{
7686                 this.dom.focus();
7687             }catch(e){}
7688             return this;
7689         },
7690
7691         /**
7692          * Tries to blur the element. Any exceptions are caught and ignored.
7693          * @return {Roo.Element} this
7694          */
7695         blur : function() {
7696             try{
7697                 this.dom.blur();
7698             }catch(e){}
7699             return this;
7700         },
7701
7702         /**
7703          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7704          * @param {String/Array} className The CSS class to add, or an array of classes
7705          * @return {Roo.Element} this
7706          */
7707         addClass : function(className){
7708             if(className instanceof Array){
7709                 for(var i = 0, len = className.length; i < len; i++) {
7710                     this.addClass(className[i]);
7711                 }
7712             }else{
7713                 if(className && !this.hasClass(className)){
7714                     if (this.dom instanceof SVGElement) {
7715                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7716                     } else {
7717                         this.dom.className = this.dom.className + " " + className;
7718                     }
7719                 }
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7726          * @param {String/Array} className The CSS class to add, or an array of classes
7727          * @return {Roo.Element} this
7728          */
7729         radioClass : function(className){
7730             var siblings = this.dom.parentNode.childNodes;
7731             for(var i = 0; i < siblings.length; i++) {
7732                 var s = siblings[i];
7733                 if(s.nodeType == 1){
7734                     Roo.get(s).removeClass(className);
7735                 }
7736             }
7737             this.addClass(className);
7738             return this;
7739         },
7740
7741         /**
7742          * Removes one or more CSS classes from the element.
7743          * @param {String/Array} className The CSS class to remove, or an array of classes
7744          * @return {Roo.Element} this
7745          */
7746         removeClass : function(className){
7747             
7748             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7749             if(!className || !cn){
7750                 return this;
7751             }
7752             if(className instanceof Array){
7753                 for(var i = 0, len = className.length; i < len; i++) {
7754                     this.removeClass(className[i]);
7755                 }
7756             }else{
7757                 if(this.hasClass(className)){
7758                     var re = this.classReCache[className];
7759                     if (!re) {
7760                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7761                        this.classReCache[className] = re;
7762                     }
7763                     if (this.dom instanceof SVGElement) {
7764                         this.dom.className.baseVal = cn.replace(re, " ");
7765                     } else {
7766                         this.dom.className = cn.replace(re, " ");
7767                     }
7768                 }
7769             }
7770             return this;
7771         },
7772
7773         // private
7774         classReCache: {},
7775
7776         /**
7777          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7778          * @param {String} className The CSS class to toggle
7779          * @return {Roo.Element} this
7780          */
7781         toggleClass : function(className){
7782             if(this.hasClass(className)){
7783                 this.removeClass(className);
7784             }else{
7785                 this.addClass(className);
7786             }
7787             return this;
7788         },
7789
7790         /**
7791          * Checks if the specified CSS class exists on this element's DOM node.
7792          * @param {String} className The CSS class to check for
7793          * @return {Boolean} True if the class exists, else false
7794          */
7795         hasClass : function(className){
7796             if (this.dom instanceof SVGElement) {
7797                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7798             } 
7799             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7800         },
7801
7802         /**
7803          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7804          * @param {String} oldClassName The CSS class to replace
7805          * @param {String} newClassName The replacement CSS class
7806          * @return {Roo.Element} this
7807          */
7808         replaceClass : function(oldClassName, newClassName){
7809             this.removeClass(oldClassName);
7810             this.addClass(newClassName);
7811             return this;
7812         },
7813
7814         /**
7815          * Returns an object with properties matching the styles requested.
7816          * For example, el.getStyles('color', 'font-size', 'width') might return
7817          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7818          * @param {String} style1 A style name
7819          * @param {String} style2 A style name
7820          * @param {String} etc.
7821          * @return {Object} The style object
7822          */
7823         getStyles : function(){
7824             var a = arguments, len = a.length, r = {};
7825             for(var i = 0; i < len; i++){
7826                 r[a[i]] = this.getStyle(a[i]);
7827             }
7828             return r;
7829         },
7830
7831         /**
7832          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7833          * @param {String} property The style property whose value is returned.
7834          * @return {String} The current value of the style property for this element.
7835          */
7836         getStyle : function(){
7837             return view && view.getComputedStyle ?
7838                 function(prop){
7839                     var el = this.dom, v, cs, camel;
7840                     if(prop == 'float'){
7841                         prop = "cssFloat";
7842                     }
7843                     if(el.style && (v = el.style[prop])){
7844                         return v;
7845                     }
7846                     if(cs = view.getComputedStyle(el, "")){
7847                         if(!(camel = propCache[prop])){
7848                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7849                         }
7850                         return cs[camel];
7851                     }
7852                     return null;
7853                 } :
7854                 function(prop){
7855                     var el = this.dom, v, cs, camel;
7856                     if(prop == 'opacity'){
7857                         if(typeof el.style.filter == 'string'){
7858                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7859                             if(m){
7860                                 var fv = parseFloat(m[1]);
7861                                 if(!isNaN(fv)){
7862                                     return fv ? fv / 100 : 0;
7863                                 }
7864                             }
7865                         }
7866                         return 1;
7867                     }else if(prop == 'float'){
7868                         prop = "styleFloat";
7869                     }
7870                     if(!(camel = propCache[prop])){
7871                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7872                     }
7873                     if(v = el.style[camel]){
7874                         return v;
7875                     }
7876                     if(cs = el.currentStyle){
7877                         return cs[camel];
7878                     }
7879                     return null;
7880                 };
7881         }(),
7882
7883         /**
7884          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7885          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7886          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7887          * @return {Roo.Element} this
7888          */
7889         setStyle : function(prop, value){
7890             if(typeof prop == "string"){
7891                 
7892                 if (prop == 'float') {
7893                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7894                     return this;
7895                 }
7896                 
7897                 var camel;
7898                 if(!(camel = propCache[prop])){
7899                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7900                 }
7901                 
7902                 if(camel == 'opacity') {
7903                     this.setOpacity(value);
7904                 }else{
7905                     this.dom.style[camel] = value;
7906                 }
7907             }else{
7908                 for(var style in prop){
7909                     if(typeof prop[style] != "function"){
7910                        this.setStyle(style, prop[style]);
7911                     }
7912                 }
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * More flexible version of {@link #setStyle} for setting style properties.
7919          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7920          * a function which returns such a specification.
7921          * @return {Roo.Element} this
7922          */
7923         applyStyles : function(style){
7924             Roo.DomHelper.applyStyles(this.dom, style);
7925             return this;
7926         },
7927
7928         /**
7929           * 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).
7930           * @return {Number} The X position of the element
7931           */
7932         getX : function(){
7933             return D.getX(this.dom);
7934         },
7935
7936         /**
7937           * 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).
7938           * @return {Number} The Y position of the element
7939           */
7940         getY : function(){
7941             return D.getY(this.dom);
7942         },
7943
7944         /**
7945           * 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).
7946           * @return {Array} The XY position of the element
7947           */
7948         getXY : function(){
7949             return D.getXY(this.dom);
7950         },
7951
7952         /**
7953          * 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).
7954          * @param {Number} The X position of the element
7955          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7956          * @return {Roo.Element} this
7957          */
7958         setX : function(x, animate){
7959             if(!animate || !A){
7960                 D.setX(this.dom, x);
7961             }else{
7962                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7963             }
7964             return this;
7965         },
7966
7967         /**
7968          * 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).
7969          * @param {Number} The Y position of the element
7970          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973         setY : function(y, animate){
7974             if(!animate || !A){
7975                 D.setY(this.dom, y);
7976             }else{
7977                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7978             }
7979             return this;
7980         },
7981
7982         /**
7983          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7984          * @param {String} left The left CSS property value
7985          * @return {Roo.Element} this
7986          */
7987         setLeft : function(left){
7988             this.setStyle("left", this.addUnits(left));
7989             return this;
7990         },
7991
7992         /**
7993          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7994          * @param {String} top The top CSS property value
7995          * @return {Roo.Element} this
7996          */
7997         setTop : function(top){
7998             this.setStyle("top", this.addUnits(top));
7999             return this;
8000         },
8001
8002         /**
8003          * Sets the element's CSS right style.
8004          * @param {String} right The right CSS property value
8005          * @return {Roo.Element} this
8006          */
8007         setRight : function(right){
8008             this.setStyle("right", this.addUnits(right));
8009             return this;
8010         },
8011
8012         /**
8013          * Sets the element's CSS bottom style.
8014          * @param {String} bottom The bottom CSS property value
8015          * @return {Roo.Element} this
8016          */
8017         setBottom : function(bottom){
8018             this.setStyle("bottom", this.addUnits(bottom));
8019             return this;
8020         },
8021
8022         /**
8023          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8024          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8025          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8026          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setXY : function(pos, animate){
8030             if(!animate || !A){
8031                 D.setXY(this.dom, pos);
8032             }else{
8033                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8034             }
8035             return this;
8036         },
8037
8038         /**
8039          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8040          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8041          * @param {Number} x X value for new position (coordinates are page-based)
8042          * @param {Number} y Y value for new position (coordinates are page-based)
8043          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8044          * @return {Roo.Element} this
8045          */
8046         setLocation : function(x, y, animate){
8047             this.setXY([x, y], this.preanim(arguments, 2));
8048             return this;
8049         },
8050
8051         /**
8052          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8053          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8054          * @param {Number} x X value for new position (coordinates are page-based)
8055          * @param {Number} y Y value for new position (coordinates are page-based)
8056          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8057          * @return {Roo.Element} this
8058          */
8059         moveTo : function(x, y, animate){
8060             this.setXY([x, y], this.preanim(arguments, 2));
8061             return this;
8062         },
8063
8064         /**
8065          * Returns the region of the given element.
8066          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8067          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8068          */
8069         getRegion : function(){
8070             return D.getRegion(this.dom);
8071         },
8072
8073         /**
8074          * Returns the offset height of the element
8075          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8076          * @return {Number} The element's height
8077          */
8078         getHeight : function(contentHeight){
8079             var h = this.dom.offsetHeight || 0;
8080             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8081         },
8082
8083         /**
8084          * Returns the offset width of the element
8085          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8086          * @return {Number} The element's width
8087          */
8088         getWidth : function(contentWidth){
8089             var w = this.dom.offsetWidth || 0;
8090             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8091         },
8092
8093         /**
8094          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8095          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8096          * if a height has not been set using CSS.
8097          * @return {Number}
8098          */
8099         getComputedHeight : function(){
8100             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8101             if(!h){
8102                 h = parseInt(this.getStyle('height'), 10) || 0;
8103                 if(!this.isBorderBox()){
8104                     h += this.getFrameWidth('tb');
8105                 }
8106             }
8107             return h;
8108         },
8109
8110         /**
8111          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8112          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8113          * if a width has not been set using CSS.
8114          * @return {Number}
8115          */
8116         getComputedWidth : function(){
8117             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8118             if(!w){
8119                 w = parseInt(this.getStyle('width'), 10) || 0;
8120                 if(!this.isBorderBox()){
8121                     w += this.getFrameWidth('lr');
8122                 }
8123             }
8124             return w;
8125         },
8126
8127         /**
8128          * Returns the size of the element.
8129          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8130          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8131          */
8132         getSize : function(contentSize){
8133             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8134         },
8135
8136         /**
8137          * Returns the width and height of the viewport.
8138          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8139          */
8140         getViewSize : function(){
8141             var d = this.dom, doc = document, aw = 0, ah = 0;
8142             if(d == doc || d == doc.body){
8143                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8144             }else{
8145                 return {
8146                     width : d.clientWidth,
8147                     height: d.clientHeight
8148                 };
8149             }
8150         },
8151
8152         /**
8153          * Returns the value of the "value" attribute
8154          * @param {Boolean} asNumber true to parse the value as a number
8155          * @return {String/Number}
8156          */
8157         getValue : function(asNumber){
8158             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8159         },
8160
8161         // private
8162         adjustWidth : function(width){
8163             if(typeof width == "number"){
8164                 if(this.autoBoxAdjust && !this.isBorderBox()){
8165                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8166                 }
8167                 if(width < 0){
8168                     width = 0;
8169                 }
8170             }
8171             return width;
8172         },
8173
8174         // private
8175         adjustHeight : function(height){
8176             if(typeof height == "number"){
8177                if(this.autoBoxAdjust && !this.isBorderBox()){
8178                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8179                }
8180                if(height < 0){
8181                    height = 0;
8182                }
8183             }
8184             return height;
8185         },
8186
8187         /**
8188          * Set the width of the element
8189          * @param {Number} width The new width
8190          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8191          * @return {Roo.Element} this
8192          */
8193         setWidth : function(width, animate){
8194             width = this.adjustWidth(width);
8195             if(!animate || !A){
8196                 this.dom.style.width = this.addUnits(width);
8197             }else{
8198                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8199             }
8200             return this;
8201         },
8202
8203         /**
8204          * Set the height of the element
8205          * @param {Number} height The new height
8206          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8207          * @return {Roo.Element} this
8208          */
8209          setHeight : function(height, animate){
8210             height = this.adjustHeight(height);
8211             if(!animate || !A){
8212                 this.dom.style.height = this.addUnits(height);
8213             }else{
8214                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8215             }
8216             return this;
8217         },
8218
8219         /**
8220          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8221          * @param {Number} width The new width
8222          * @param {Number} height The new height
8223          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8224          * @return {Roo.Element} this
8225          */
8226          setSize : function(width, height, animate){
8227             if(typeof width == "object"){ // in case of object from getSize()
8228                 height = width.height; width = width.width;
8229             }
8230             width = this.adjustWidth(width); height = this.adjustHeight(height);
8231             if(!animate || !A){
8232                 this.dom.style.width = this.addUnits(width);
8233                 this.dom.style.height = this.addUnits(height);
8234             }else{
8235                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8236             }
8237             return this;
8238         },
8239
8240         /**
8241          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8242          * @param {Number} x X value for new position (coordinates are page-based)
8243          * @param {Number} y Y value for new position (coordinates are page-based)
8244          * @param {Number} width The new width
8245          * @param {Number} height The new height
8246          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8247          * @return {Roo.Element} this
8248          */
8249         setBounds : function(x, y, width, height, animate){
8250             if(!animate || !A){
8251                 this.setSize(width, height);
8252                 this.setLocation(x, y);
8253             }else{
8254                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8255                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8256                               this.preanim(arguments, 4), 'motion');
8257             }
8258             return this;
8259         },
8260
8261         /**
8262          * 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.
8263          * @param {Roo.lib.Region} region The region to fill
8264          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8265          * @return {Roo.Element} this
8266          */
8267         setRegion : function(region, animate){
8268             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8269             return this;
8270         },
8271
8272         /**
8273          * Appends an event handler
8274          *
8275          * @param {String}   eventName     The type of event to append
8276          * @param {Function} fn        The method the event invokes
8277          * @param {Object} scope       (optional) The scope (this object) of the fn
8278          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8279          */
8280         addListener : function(eventName, fn, scope, options)
8281         {
8282             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8283                 this.addListener('touchstart', this.onTapHandler, this);
8284             }
8285             
8286             // we need to handle a special case where dom element is a svg element.
8287             // in this case we do not actua
8288             if (!this.dom) {
8289                 return;
8290             }
8291             
8292             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8293                 if (typeof(this.listeners[eventName]) == 'undefined') {
8294                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8295                 }
8296                 this.listeners[eventName].addListener(fn, scope, options);
8297                 return;
8298             }
8299             
8300                 
8301             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8302             
8303             
8304         },
8305         tapedTwice : false,
8306         onTapHandler : function(event)
8307         {
8308             if(!this.tapedTwice) {
8309                 this.tapedTwice = true;
8310                 var s = this;
8311                 setTimeout( function() {
8312                     s.tapedTwice = false;
8313                 }, 300 );
8314                 return;
8315             }
8316             event.preventDefault();
8317             var revent = new MouseEvent('dblclick',  {
8318                 view: window,
8319                 bubbles: true,
8320                 cancelable: true
8321             });
8322              
8323             this.dom.dispatchEvent(revent);
8324             //action on double tap goes below
8325              
8326         }, 
8327  
8328         /**
8329          * Removes an event handler from this element
8330          * @param {String} eventName the type of event to remove
8331          * @param {Function} fn the method the event invokes
8332          * @param {Function} scope (needed for svg fake listeners)
8333          * @return {Roo.Element} this
8334          */
8335         removeListener : function(eventName, fn, scope){
8336             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8337             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8338                 return this;
8339             }
8340             this.listeners[eventName].removeListener(fn, scope);
8341             return this;
8342         },
8343
8344         /**
8345          * Removes all previous added listeners from this element
8346          * @return {Roo.Element} this
8347          */
8348         removeAllListeners : function(){
8349             E.purgeElement(this.dom);
8350             this.listeners = {};
8351             return this;
8352         },
8353
8354         relayEvent : function(eventName, observable){
8355             this.on(eventName, function(e){
8356                 observable.fireEvent(eventName, e);
8357             });
8358         },
8359
8360         
8361         /**
8362          * Set the opacity of the element
8363          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367          setOpacity : function(opacity, animate){
8368             if(!animate || !A){
8369                 var s = this.dom.style;
8370                 if(Roo.isIE){
8371                     s.zoom = 1;
8372                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8373                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8374                 }else{
8375                     s.opacity = opacity;
8376                 }
8377             }else{
8378                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8379             }
8380             return this;
8381         },
8382
8383         /**
8384          * Gets the left X coordinate
8385          * @param {Boolean} local True to get the local css position instead of page coordinate
8386          * @return {Number}
8387          */
8388         getLeft : function(local){
8389             if(!local){
8390                 return this.getX();
8391             }else{
8392                 return parseInt(this.getStyle("left"), 10) || 0;
8393             }
8394         },
8395
8396         /**
8397          * Gets the right X coordinate of the element (element X position + element width)
8398          * @param {Boolean} local True to get the local css position instead of page coordinate
8399          * @return {Number}
8400          */
8401         getRight : function(local){
8402             if(!local){
8403                 return this.getX() + this.getWidth();
8404             }else{
8405                 return (this.getLeft(true) + this.getWidth()) || 0;
8406             }
8407         },
8408
8409         /**
8410          * Gets the top Y coordinate
8411          * @param {Boolean} local True to get the local css position instead of page coordinate
8412          * @return {Number}
8413          */
8414         getTop : function(local) {
8415             if(!local){
8416                 return this.getY();
8417             }else{
8418                 return parseInt(this.getStyle("top"), 10) || 0;
8419             }
8420         },
8421
8422         /**
8423          * Gets the bottom Y coordinate of the element (element Y position + element height)
8424          * @param {Boolean} local True to get the local css position instead of page coordinate
8425          * @return {Number}
8426          */
8427         getBottom : function(local){
8428             if(!local){
8429                 return this.getY() + this.getHeight();
8430             }else{
8431                 return (this.getTop(true) + this.getHeight()) || 0;
8432             }
8433         },
8434
8435         /**
8436         * Initializes positioning on this element. If a desired position is not passed, it will make the
8437         * the element positioned relative IF it is not already positioned.
8438         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8439         * @param {Number} zIndex (optional) The zIndex to apply
8440         * @param {Number} x (optional) Set the page X position
8441         * @param {Number} y (optional) Set the page Y position
8442         */
8443         position : function(pos, zIndex, x, y){
8444             if(!pos){
8445                if(this.getStyle('position') == 'static'){
8446                    this.setStyle('position', 'relative');
8447                }
8448             }else{
8449                 this.setStyle("position", pos);
8450             }
8451             if(zIndex){
8452                 this.setStyle("z-index", zIndex);
8453             }
8454             if(x !== undefined && y !== undefined){
8455                 this.setXY([x, y]);
8456             }else if(x !== undefined){
8457                 this.setX(x);
8458             }else if(y !== undefined){
8459                 this.setY(y);
8460             }
8461         },
8462
8463         /**
8464         * Clear positioning back to the default when the document was loaded
8465         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8466         * @return {Roo.Element} this
8467          */
8468         clearPositioning : function(value){
8469             value = value ||'';
8470             this.setStyle({
8471                 "left": value,
8472                 "right": value,
8473                 "top": value,
8474                 "bottom": value,
8475                 "z-index": "",
8476                 "position" : "static"
8477             });
8478             return this;
8479         },
8480
8481         /**
8482         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8483         * snapshot before performing an update and then restoring the element.
8484         * @return {Object}
8485         */
8486         getPositioning : function(){
8487             var l = this.getStyle("left");
8488             var t = this.getStyle("top");
8489             return {
8490                 "position" : this.getStyle("position"),
8491                 "left" : l,
8492                 "right" : l ? "" : this.getStyle("right"),
8493                 "top" : t,
8494                 "bottom" : t ? "" : this.getStyle("bottom"),
8495                 "z-index" : this.getStyle("z-index")
8496             };
8497         },
8498
8499         /**
8500          * Gets the width of the border(s) for the specified side(s)
8501          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8502          * passing lr would get the border (l)eft width + the border (r)ight width.
8503          * @return {Number} The width of the sides passed added together
8504          */
8505         getBorderWidth : function(side){
8506             return this.addStyles(side, El.borders);
8507         },
8508
8509         /**
8510          * Gets the width of the padding(s) for the specified side(s)
8511          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8512          * passing lr would get the padding (l)eft + the padding (r)ight.
8513          * @return {Number} The padding of the sides passed added together
8514          */
8515         getPadding : function(side){
8516             return this.addStyles(side, El.paddings);
8517         },
8518
8519         /**
8520         * Set positioning with an object returned by getPositioning().
8521         * @param {Object} posCfg
8522         * @return {Roo.Element} this
8523          */
8524         setPositioning : function(pc){
8525             this.applyStyles(pc);
8526             if(pc.right == "auto"){
8527                 this.dom.style.right = "";
8528             }
8529             if(pc.bottom == "auto"){
8530                 this.dom.style.bottom = "";
8531             }
8532             return this;
8533         },
8534
8535         // private
8536         fixDisplay : function(){
8537             if(this.getStyle("display") == "none"){
8538                 this.setStyle("visibility", "hidden");
8539                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8540                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8541                     this.setStyle("display", "block");
8542                 }
8543             }
8544         },
8545
8546         /**
8547          * Quick set left and top adding default units
8548          * @param {String} left The left CSS property value
8549          * @param {String} top The top CSS property value
8550          * @return {Roo.Element} this
8551          */
8552          setLeftTop : function(left, top){
8553             this.dom.style.left = this.addUnits(left);
8554             this.dom.style.top = this.addUnits(top);
8555             return this;
8556         },
8557
8558         /**
8559          * Move this element relative to its current position.
8560          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8561          * @param {Number} distance How far to move the element in pixels
8562          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8563          * @return {Roo.Element} this
8564          */
8565          move : function(direction, distance, animate){
8566             var xy = this.getXY();
8567             direction = direction.toLowerCase();
8568             switch(direction){
8569                 case "l":
8570                 case "left":
8571                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8572                     break;
8573                case "r":
8574                case "right":
8575                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8576                     break;
8577                case "t":
8578                case "top":
8579                case "up":
8580                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8581                     break;
8582                case "b":
8583                case "bottom":
8584                case "down":
8585                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8586                     break;
8587             }
8588             return this;
8589         },
8590
8591         /**
8592          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8593          * @return {Roo.Element} this
8594          */
8595         clip : function(){
8596             if(!this.isClipped){
8597                this.isClipped = true;
8598                this.originalClip = {
8599                    "o": this.getStyle("overflow"),
8600                    "x": this.getStyle("overflow-x"),
8601                    "y": this.getStyle("overflow-y")
8602                };
8603                this.setStyle("overflow", "hidden");
8604                this.setStyle("overflow-x", "hidden");
8605                this.setStyle("overflow-y", "hidden");
8606             }
8607             return this;
8608         },
8609
8610         /**
8611          *  Return clipping (overflow) to original clipping before clip() was called
8612          * @return {Roo.Element} this
8613          */
8614         unclip : function(){
8615             if(this.isClipped){
8616                 this.isClipped = false;
8617                 var o = this.originalClip;
8618                 if(o.o){this.setStyle("overflow", o.o);}
8619                 if(o.x){this.setStyle("overflow-x", o.x);}
8620                 if(o.y){this.setStyle("overflow-y", o.y);}
8621             }
8622             return this;
8623         },
8624
8625
8626         /**
8627          * Gets the x,y coordinates specified by the anchor position on the element.
8628          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8629          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8630          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8631          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8632          * @return {Array} [x, y] An array containing the element's x and y coordinates
8633          */
8634         getAnchorXY : function(anchor, local, s){
8635             //Passing a different size is useful for pre-calculating anchors,
8636             //especially for anchored animations that change the el size.
8637
8638             var w, h, vp = false;
8639             if(!s){
8640                 var d = this.dom;
8641                 if(d == document.body || d == document){
8642                     vp = true;
8643                     w = D.getViewWidth(); h = D.getViewHeight();
8644                 }else{
8645                     w = this.getWidth(); h = this.getHeight();
8646                 }
8647             }else{
8648                 w = s.width;  h = s.height;
8649             }
8650             var x = 0, y = 0, r = Math.round;
8651             switch((anchor || "tl").toLowerCase()){
8652                 case "c":
8653                     x = r(w*.5);
8654                     y = r(h*.5);
8655                 break;
8656                 case "t":
8657                     x = r(w*.5);
8658                     y = 0;
8659                 break;
8660                 case "l":
8661                     x = 0;
8662                     y = r(h*.5);
8663                 break;
8664                 case "r":
8665                     x = w;
8666                     y = r(h*.5);
8667                 break;
8668                 case "b":
8669                     x = r(w*.5);
8670                     y = h;
8671                 break;
8672                 case "tl":
8673                     x = 0;
8674                     y = 0;
8675                 break;
8676                 case "bl":
8677                     x = 0;
8678                     y = h;
8679                 break;
8680                 case "br":
8681                     x = w;
8682                     y = h;
8683                 break;
8684                 case "tr":
8685                     x = w;
8686                     y = 0;
8687                 break;
8688             }
8689             if(local === true){
8690                 return [x, y];
8691             }
8692             if(vp){
8693                 var sc = this.getScroll();
8694                 return [x + sc.left, y + sc.top];
8695             }
8696             //Add the element's offset xy
8697             var o = this.getXY();
8698             return [x+o[0], y+o[1]];
8699         },
8700
8701         /**
8702          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8703          * supported position values.
8704          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8705          * @param {String} position The position to align to.
8706          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8707          * @return {Array} [x, y]
8708          */
8709         getAlignToXY : function(el, p, o)
8710         {
8711             el = Roo.get(el);
8712             var d = this.dom;
8713             if(!el.dom){
8714                 throw "Element.alignTo with an element that doesn't exist";
8715             }
8716             var c = false; //constrain to viewport
8717             var p1 = "", p2 = "";
8718             o = o || [0,0];
8719
8720             if(!p){
8721                 p = "tl-bl";
8722             }else if(p == "?"){
8723                 p = "tl-bl?";
8724             }else if(p.indexOf("-") == -1){
8725                 p = "tl-" + p;
8726             }
8727             p = p.toLowerCase();
8728             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8729             if(!m){
8730                throw "Element.alignTo with an invalid alignment " + p;
8731             }
8732             p1 = m[1]; p2 = m[2]; c = !!m[3];
8733
8734             //Subtract the aligned el's internal xy from the target's offset xy
8735             //plus custom offset to get the aligned el's new offset xy
8736             var a1 = this.getAnchorXY(p1, true);
8737             var a2 = el.getAnchorXY(p2, false);
8738             var x = a2[0] - a1[0] + o[0];
8739             var y = a2[1] - a1[1] + o[1];
8740             if(c){
8741                 //constrain the aligned el to viewport if necessary
8742                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8743                 // 5px of margin for ie
8744                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8745
8746                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8747                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8748                 //otherwise swap the aligned el to the opposite border of the target.
8749                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8750                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8751                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8752                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8753
8754                var doc = document;
8755                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8756                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8757
8758                if((x+w) > dw + scrollX){
8759                     x = swapX ? r.left-w : dw+scrollX-w;
8760                 }
8761                if(x < scrollX){
8762                    x = swapX ? r.right : scrollX;
8763                }
8764                if((y+h) > dh + scrollY){
8765                     y = swapY ? r.top-h : dh+scrollY-h;
8766                 }
8767                if (y < scrollY){
8768                    y = swapY ? r.bottom : scrollY;
8769                }
8770             }
8771             return [x,y];
8772         },
8773
8774         // private
8775         getConstrainToXY : function(){
8776             var os = {top:0, left:0, bottom:0, right: 0};
8777
8778             return function(el, local, offsets, proposedXY){
8779                 el = Roo.get(el);
8780                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8781
8782                 var vw, vh, vx = 0, vy = 0;
8783                 if(el.dom == document.body || el.dom == document){
8784                     vw = Roo.lib.Dom.getViewWidth();
8785                     vh = Roo.lib.Dom.getViewHeight();
8786                 }else{
8787                     vw = el.dom.clientWidth;
8788                     vh = el.dom.clientHeight;
8789                     if(!local){
8790                         var vxy = el.getXY();
8791                         vx = vxy[0];
8792                         vy = vxy[1];
8793                     }
8794                 }
8795
8796                 var s = el.getScroll();
8797
8798                 vx += offsets.left + s.left;
8799                 vy += offsets.top + s.top;
8800
8801                 vw -= offsets.right;
8802                 vh -= offsets.bottom;
8803
8804                 var vr = vx+vw;
8805                 var vb = vy+vh;
8806
8807                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8808                 var x = xy[0], y = xy[1];
8809                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8810
8811                 // only move it if it needs it
8812                 var moved = false;
8813
8814                 // first validate right/bottom
8815                 if((x + w) > vr){
8816                     x = vr - w;
8817                     moved = true;
8818                 }
8819                 if((y + h) > vb){
8820                     y = vb - h;
8821                     moved = true;
8822                 }
8823                 // then make sure top/left isn't negative
8824                 if(x < vx){
8825                     x = vx;
8826                     moved = true;
8827                 }
8828                 if(y < vy){
8829                     y = vy;
8830                     moved = true;
8831                 }
8832                 return moved ? [x, y] : false;
8833             };
8834         }(),
8835
8836         // private
8837         adjustForConstraints : function(xy, parent, offsets){
8838             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8839         },
8840
8841         /**
8842          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8843          * document it aligns it to the viewport.
8844          * The position parameter is optional, and can be specified in any one of the following formats:
8845          * <ul>
8846          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8847          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8848          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8849          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8850          *   <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
8851          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8852          * </ul>
8853          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8854          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8855          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8856          * that specified in order to enforce the viewport constraints.
8857          * Following are all of the supported anchor positions:
8858     <pre>
8859     Value  Description
8860     -----  -----------------------------
8861     tl     The top left corner (default)
8862     t      The center of the top edge
8863     tr     The top right corner
8864     l      The center of the left edge
8865     c      In the center of the element
8866     r      The center of the right edge
8867     bl     The bottom left corner
8868     b      The center of the bottom edge
8869     br     The bottom right corner
8870     </pre>
8871     Example Usage:
8872     <pre><code>
8873     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8874     el.alignTo("other-el");
8875
8876     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8877     el.alignTo("other-el", "tr?");
8878
8879     // align the bottom right corner of el with the center left edge of other-el
8880     el.alignTo("other-el", "br-l?");
8881
8882     // align the center of el with the bottom left corner of other-el and
8883     // adjust the x position by -6 pixels (and the y position by 0)
8884     el.alignTo("other-el", "c-bl", [-6, 0]);
8885     </code></pre>
8886          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8887          * @param {String} position The position to align to.
8888          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8889          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8890          * @return {Roo.Element} this
8891          */
8892         alignTo : function(element, position, offsets, animate){
8893             var xy = this.getAlignToXY(element, position, offsets);
8894             this.setXY(xy, this.preanim(arguments, 3));
8895             return this;
8896         },
8897
8898         /**
8899          * Anchors an element to another element and realigns it when the window is resized.
8900          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8901          * @param {String} position The position to align to.
8902          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8903          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8904          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8905          * is a number, it is used as the buffer delay (defaults to 50ms).
8906          * @param {Function} callback The function to call after the animation finishes
8907          * @return {Roo.Element} this
8908          */
8909         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8910             var action = function(){
8911                 this.alignTo(el, alignment, offsets, animate);
8912                 Roo.callback(callback, this);
8913             };
8914             Roo.EventManager.onWindowResize(action, this);
8915             var tm = typeof monitorScroll;
8916             if(tm != 'undefined'){
8917                 Roo.EventManager.on(window, 'scroll', action, this,
8918                     {buffer: tm == 'number' ? monitorScroll : 50});
8919             }
8920             action.call(this); // align immediately
8921             return this;
8922         },
8923         /**
8924          * Clears any opacity settings from this element. Required in some cases for IE.
8925          * @return {Roo.Element} this
8926          */
8927         clearOpacity : function(){
8928             if (window.ActiveXObject) {
8929                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8930                     this.dom.style.filter = "";
8931                 }
8932             } else {
8933                 this.dom.style.opacity = "";
8934                 this.dom.style["-moz-opacity"] = "";
8935                 this.dom.style["-khtml-opacity"] = "";
8936             }
8937             return this;
8938         },
8939
8940         /**
8941          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8942          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8943          * @return {Roo.Element} this
8944          */
8945         hide : function(animate){
8946             this.setVisible(false, this.preanim(arguments, 0));
8947             return this;
8948         },
8949
8950         /**
8951         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8952         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8953          * @return {Roo.Element} this
8954          */
8955         show : function(animate){
8956             this.setVisible(true, this.preanim(arguments, 0));
8957             return this;
8958         },
8959
8960         /**
8961          * @private Test if size has a unit, otherwise appends the default
8962          */
8963         addUnits : function(size){
8964             return Roo.Element.addUnits(size, this.defaultUnit);
8965         },
8966
8967         /**
8968          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8969          * @return {Roo.Element} this
8970          */
8971         beginMeasure : function(){
8972             var el = this.dom;
8973             if(el.offsetWidth || el.offsetHeight){
8974                 return this; // offsets work already
8975             }
8976             var changed = [];
8977             var p = this.dom, b = document.body; // start with this element
8978             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8979                 var pe = Roo.get(p);
8980                 if(pe.getStyle('display') == 'none'){
8981                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8982                     p.style.visibility = "hidden";
8983                     p.style.display = "block";
8984                 }
8985                 p = p.parentNode;
8986             }
8987             this._measureChanged = changed;
8988             return this;
8989
8990         },
8991
8992         /**
8993          * Restores displays to before beginMeasure was called
8994          * @return {Roo.Element} this
8995          */
8996         endMeasure : function(){
8997             var changed = this._measureChanged;
8998             if(changed){
8999                 for(var i = 0, len = changed.length; i < len; i++) {
9000                     var r = changed[i];
9001                     r.el.style.visibility = r.visibility;
9002                     r.el.style.display = "none";
9003                 }
9004                 this._measureChanged = null;
9005             }
9006             return this;
9007         },
9008
9009         /**
9010         * Update the innerHTML of this element, optionally searching for and processing scripts
9011         * @param {String} html The new HTML
9012         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9013         * @param {Function} callback For async script loading you can be noticed when the update completes
9014         * @return {Roo.Element} this
9015          */
9016         update : function(html, loadScripts, callback){
9017             if(typeof html == "undefined"){
9018                 html = "";
9019             }
9020             if(loadScripts !== true){
9021                 this.dom.innerHTML = html;
9022                 if(typeof callback == "function"){
9023                     callback();
9024                 }
9025                 return this;
9026             }
9027             var id = Roo.id();
9028             var dom = this.dom;
9029
9030             html += '<span id="' + id + '"></span>';
9031
9032             E.onAvailable(id, function(){
9033                 var hd = document.getElementsByTagName("head")[0];
9034                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9035                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9036                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9037
9038                 var match;
9039                 while(match = re.exec(html)){
9040                     var attrs = match[1];
9041                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9042                     if(srcMatch && srcMatch[2]){
9043                        var s = document.createElement("script");
9044                        s.src = srcMatch[2];
9045                        var typeMatch = attrs.match(typeRe);
9046                        if(typeMatch && typeMatch[2]){
9047                            s.type = typeMatch[2];
9048                        }
9049                        hd.appendChild(s);
9050                     }else if(match[2] && match[2].length > 0){
9051                         if(window.execScript) {
9052                            window.execScript(match[2]);
9053                         } else {
9054                             /**
9055                              * eval:var:id
9056                              * eval:var:dom
9057                              * eval:var:html
9058                              * 
9059                              */
9060                            window.eval(match[2]);
9061                         }
9062                     }
9063                 }
9064                 var el = document.getElementById(id);
9065                 if(el){el.parentNode.removeChild(el);}
9066                 if(typeof callback == "function"){
9067                     callback();
9068                 }
9069             });
9070             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9071             return this;
9072         },
9073
9074         /**
9075          * Direct access to the UpdateManager update() method (takes the same parameters).
9076          * @param {String/Function} url The url for this request or a function to call to get the url
9077          * @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}
9078          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9079          * @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.
9080          * @return {Roo.Element} this
9081          */
9082         load : function(){
9083             var um = this.getUpdateManager();
9084             um.update.apply(um, arguments);
9085             return this;
9086         },
9087
9088         /**
9089         * Gets this element's UpdateManager
9090         * @return {Roo.UpdateManager} The UpdateManager
9091         */
9092         getUpdateManager : function(){
9093             if(!this.updateManager){
9094                 this.updateManager = new Roo.UpdateManager(this);
9095             }
9096             return this.updateManager;
9097         },
9098
9099         /**
9100          * Disables text selection for this element (normalized across browsers)
9101          * @return {Roo.Element} this
9102          */
9103         unselectable : function(){
9104             this.dom.unselectable = "on";
9105             this.swallowEvent("selectstart", true);
9106             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9107             this.addClass("x-unselectable");
9108             return this;
9109         },
9110
9111         /**
9112         * Calculates the x, y to center this element on the screen
9113         * @return {Array} The x, y values [x, y]
9114         */
9115         getCenterXY : function(){
9116             return this.getAlignToXY(document, 'c-c');
9117         },
9118
9119         /**
9120         * Centers the Element in either the viewport, or another Element.
9121         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9122         */
9123         center : function(centerIn){
9124             this.alignTo(centerIn || document, 'c-c');
9125             return this;
9126         },
9127
9128         /**
9129          * Tests various css rules/browsers to determine if this element uses a border box
9130          * @return {Boolean}
9131          */
9132         isBorderBox : function(){
9133             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9134         },
9135
9136         /**
9137          * Return a box {x, y, width, height} that can be used to set another elements
9138          * size/location to match this element.
9139          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9140          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9141          * @return {Object} box An object in the format {x, y, width, height}
9142          */
9143         getBox : function(contentBox, local){
9144             var xy;
9145             if(!local){
9146                 xy = this.getXY();
9147             }else{
9148                 var left = parseInt(this.getStyle("left"), 10) || 0;
9149                 var top = parseInt(this.getStyle("top"), 10) || 0;
9150                 xy = [left, top];
9151             }
9152             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9153             if(!contentBox){
9154                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9155             }else{
9156                 var l = this.getBorderWidth("l")+this.getPadding("l");
9157                 var r = this.getBorderWidth("r")+this.getPadding("r");
9158                 var t = this.getBorderWidth("t")+this.getPadding("t");
9159                 var b = this.getBorderWidth("b")+this.getPadding("b");
9160                 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)};
9161             }
9162             bx.right = bx.x + bx.width;
9163             bx.bottom = bx.y + bx.height;
9164             return bx;
9165         },
9166
9167         /**
9168          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9169          for more information about the sides.
9170          * @param {String} sides
9171          * @return {Number}
9172          */
9173         getFrameWidth : function(sides, onlyContentBox){
9174             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9175         },
9176
9177         /**
9178          * 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.
9179          * @param {Object} box The box to fill {x, y, width, height}
9180          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9181          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9182          * @return {Roo.Element} this
9183          */
9184         setBox : function(box, adjust, animate){
9185             var w = box.width, h = box.height;
9186             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9187                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9188                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9189             }
9190             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9191             return this;
9192         },
9193
9194         /**
9195          * Forces the browser to repaint this element
9196          * @return {Roo.Element} this
9197          */
9198          repaint : function(){
9199             var dom = this.dom;
9200             this.addClass("x-repaint");
9201             setTimeout(function(){
9202                 Roo.get(dom).removeClass("x-repaint");
9203             }, 1);
9204             return this;
9205         },
9206
9207         /**
9208          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9209          * then it returns the calculated width of the sides (see getPadding)
9210          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9211          * @return {Object/Number}
9212          */
9213         getMargins : function(side){
9214             if(!side){
9215                 return {
9216                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9217                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9218                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9219                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9220                 };
9221             }else{
9222                 return this.addStyles(side, El.margins);
9223              }
9224         },
9225
9226         // private
9227         addStyles : function(sides, styles){
9228             var val = 0, v, w;
9229             for(var i = 0, len = sides.length; i < len; i++){
9230                 v = this.getStyle(styles[sides.charAt(i)]);
9231                 if(v){
9232                      w = parseInt(v, 10);
9233                      if(w){ val += w; }
9234                 }
9235             }
9236             return val;
9237         },
9238
9239         /**
9240          * Creates a proxy element of this element
9241          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9242          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9243          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9244          * @return {Roo.Element} The new proxy element
9245          */
9246         createProxy : function(config, renderTo, matchBox){
9247             if(renderTo){
9248                 renderTo = Roo.getDom(renderTo);
9249             }else{
9250                 renderTo = document.body;
9251             }
9252             config = typeof config == "object" ?
9253                 config : {tag : "div", cls: config};
9254             var proxy = Roo.DomHelper.append(renderTo, config, true);
9255             if(matchBox){
9256                proxy.setBox(this.getBox());
9257             }
9258             return proxy;
9259         },
9260
9261         /**
9262          * Puts a mask over this element to disable user interaction. Requires core.css.
9263          * This method can only be applied to elements which accept child nodes.
9264          * @param {String} msg (optional) A message to display in the mask
9265          * @param {String} msgCls (optional) A css class to apply to the msg element
9266          * @return {Element} The mask  element
9267          */
9268         mask : function(msg, msgCls)
9269         {
9270             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9271                 this.setStyle("position", "relative");
9272             }
9273             if(!this._mask){
9274                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9275             }
9276             
9277             this.addClass("x-masked");
9278             this._mask.setDisplayed(true);
9279             
9280             // we wander
9281             var z = 0;
9282             var dom = this.dom;
9283             while (dom && dom.style) {
9284                 if (!isNaN(parseInt(dom.style.zIndex))) {
9285                     z = Math.max(z, parseInt(dom.style.zIndex));
9286                 }
9287                 dom = dom.parentNode;
9288             }
9289             // if we are masking the body - then it hides everything..
9290             if (this.dom == document.body) {
9291                 z = 1000000;
9292                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9293                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9294             }
9295            
9296             if(typeof msg == 'string'){
9297                 if(!this._maskMsg){
9298                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9299                         cls: "roo-el-mask-msg", 
9300                         cn: [
9301                             {
9302                                 tag: 'i',
9303                                 cls: 'fa fa-spinner fa-spin'
9304                             },
9305                             {
9306                                 tag: 'div'
9307                             }   
9308                         ]
9309                     }, true);
9310                 }
9311                 var mm = this._maskMsg;
9312                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9313                 if (mm.dom.lastChild) { // weird IE issue?
9314                     mm.dom.lastChild.innerHTML = msg;
9315                 }
9316                 mm.setDisplayed(true);
9317                 mm.center(this);
9318                 mm.setStyle('z-index', z + 102);
9319             }
9320             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9321                 this._mask.setHeight(this.getHeight());
9322             }
9323             this._mask.setStyle('z-index', z + 100);
9324             
9325             return this._mask;
9326         },
9327
9328         /**
9329          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9330          * it is cached for reuse.
9331          */
9332         unmask : function(removeEl){
9333             if(this._mask){
9334                 if(removeEl === true){
9335                     this._mask.remove();
9336                     delete this._mask;
9337                     if(this._maskMsg){
9338                         this._maskMsg.remove();
9339                         delete this._maskMsg;
9340                     }
9341                 }else{
9342                     this._mask.setDisplayed(false);
9343                     if(this._maskMsg){
9344                         this._maskMsg.setDisplayed(false);
9345                     }
9346                 }
9347             }
9348             this.removeClass("x-masked");
9349         },
9350
9351         /**
9352          * Returns true if this element is masked
9353          * @return {Boolean}
9354          */
9355         isMasked : function(){
9356             return this._mask && this._mask.isVisible();
9357         },
9358
9359         /**
9360          * Creates an iframe shim for this element to keep selects and other windowed objects from
9361          * showing through.
9362          * @return {Roo.Element} The new shim element
9363          */
9364         createShim : function(){
9365             var el = document.createElement('iframe');
9366             el.frameBorder = 'no';
9367             el.className = 'roo-shim';
9368             if(Roo.isIE && Roo.isSecure){
9369                 el.src = Roo.SSL_SECURE_URL;
9370             }
9371             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9372             shim.autoBoxAdjust = false;
9373             return shim;
9374         },
9375
9376         /**
9377          * Removes this element from the DOM and deletes it from the cache
9378          */
9379         remove : function(){
9380             if(this.dom.parentNode){
9381                 this.dom.parentNode.removeChild(this.dom);
9382             }
9383             delete El.cache[this.dom.id];
9384         },
9385
9386         /**
9387          * Sets up event handlers to add and remove a css class when the mouse is over this element
9388          * @param {String} className
9389          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9390          * mouseout events for children elements
9391          * @return {Roo.Element} this
9392          */
9393         addClassOnOver : function(className, preventFlicker){
9394             this.on("mouseover", function(){
9395                 Roo.fly(this, '_internal').addClass(className);
9396             }, this.dom);
9397             var removeFn = function(e){
9398                 if(preventFlicker !== true || !e.within(this, true)){
9399                     Roo.fly(this, '_internal').removeClass(className);
9400                 }
9401             };
9402             this.on("mouseout", removeFn, this.dom);
9403             return this;
9404         },
9405
9406         /**
9407          * Sets up event handlers to add and remove a css class when this element has the focus
9408          * @param {String} className
9409          * @return {Roo.Element} this
9410          */
9411         addClassOnFocus : function(className){
9412             this.on("focus", function(){
9413                 Roo.fly(this, '_internal').addClass(className);
9414             }, this.dom);
9415             this.on("blur", function(){
9416                 Roo.fly(this, '_internal').removeClass(className);
9417             }, this.dom);
9418             return this;
9419         },
9420         /**
9421          * 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)
9422          * @param {String} className
9423          * @return {Roo.Element} this
9424          */
9425         addClassOnClick : function(className){
9426             var dom = this.dom;
9427             this.on("mousedown", function(){
9428                 Roo.fly(dom, '_internal').addClass(className);
9429                 var d = Roo.get(document);
9430                 var fn = function(){
9431                     Roo.fly(dom, '_internal').removeClass(className);
9432                     d.removeListener("mouseup", fn);
9433                 };
9434                 d.on("mouseup", fn);
9435             });
9436             return this;
9437         },
9438
9439         /**
9440          * Stops the specified event from bubbling and optionally prevents the default action
9441          * @param {String} eventName
9442          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9443          * @return {Roo.Element} this
9444          */
9445         swallowEvent : function(eventName, preventDefault){
9446             var fn = function(e){
9447                 e.stopPropagation();
9448                 if(preventDefault){
9449                     e.preventDefault();
9450                 }
9451             };
9452             if(eventName instanceof Array){
9453                 for(var i = 0, len = eventName.length; i < len; i++){
9454                      this.on(eventName[i], fn);
9455                 }
9456                 return this;
9457             }
9458             this.on(eventName, fn);
9459             return this;
9460         },
9461
9462         /**
9463          * @private
9464          */
9465         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9466
9467         /**
9468          * Sizes this element to its parent element's dimensions performing
9469          * neccessary box adjustments.
9470          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9471          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9472          * @return {Roo.Element} this
9473          */
9474         fitToParent : function(monitorResize, targetParent) {
9475           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9476           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9477           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9478             return this;
9479           }
9480           var p = Roo.get(targetParent || this.dom.parentNode);
9481           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9482           if (monitorResize === true) {
9483             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9484             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9485           }
9486           return this;
9487         },
9488
9489         /**
9490          * Gets the next sibling, skipping text nodes
9491          * @return {HTMLElement} The next sibling or null
9492          */
9493         getNextSibling : function(){
9494             var n = this.dom.nextSibling;
9495             while(n && n.nodeType != 1){
9496                 n = n.nextSibling;
9497             }
9498             return n;
9499         },
9500
9501         /**
9502          * Gets the previous sibling, skipping text nodes
9503          * @return {HTMLElement} The previous sibling or null
9504          */
9505         getPrevSibling : function(){
9506             var n = this.dom.previousSibling;
9507             while(n && n.nodeType != 1){
9508                 n = n.previousSibling;
9509             }
9510             return n;
9511         },
9512
9513
9514         /**
9515          * Appends the passed element(s) to this element
9516          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9517          * @return {Roo.Element} this
9518          */
9519         appendChild: function(el){
9520             el = Roo.get(el);
9521             el.appendTo(this);
9522             return this;
9523         },
9524
9525         /**
9526          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9527          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9528          * automatically generated with the specified attributes.
9529          * @param {HTMLElement} insertBefore (optional) a child element of this element
9530          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9531          * @return {Roo.Element} The new child element
9532          */
9533         createChild: function(config, insertBefore, returnDom){
9534             config = config || {tag:'div'};
9535             if(insertBefore){
9536                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9537             }
9538             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9539         },
9540
9541         /**
9542          * Appends this element to the passed element
9543          * @param {String/HTMLElement/Element} el The new parent element
9544          * @return {Roo.Element} this
9545          */
9546         appendTo: function(el){
9547             el = Roo.getDom(el);
9548             el.appendChild(this.dom);
9549             return this;
9550         },
9551
9552         /**
9553          * Inserts this element before the passed element in the DOM
9554          * @param {String/HTMLElement/Element} el The element to insert before
9555          * @return {Roo.Element} this
9556          */
9557         insertBefore: function(el){
9558             el = Roo.getDom(el);
9559             el.parentNode.insertBefore(this.dom, el);
9560             return this;
9561         },
9562
9563         /**
9564          * Inserts this element after the passed element in the DOM
9565          * @param {String/HTMLElement/Element} el The element to insert after
9566          * @return {Roo.Element} this
9567          */
9568         insertAfter: function(el){
9569             el = Roo.getDom(el);
9570             el.parentNode.insertBefore(this.dom, el.nextSibling);
9571             return this;
9572         },
9573
9574         /**
9575          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9576          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9577          * @return {Roo.Element} The new child
9578          */
9579         insertFirst: function(el, returnDom){
9580             el = el || {};
9581             if(typeof el == 'object' && !el.nodeType){ // dh config
9582                 return this.createChild(el, this.dom.firstChild, returnDom);
9583             }else{
9584                 el = Roo.getDom(el);
9585                 this.dom.insertBefore(el, this.dom.firstChild);
9586                 return !returnDom ? Roo.get(el) : el;
9587             }
9588         },
9589
9590         /**
9591          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9592          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9593          * @param {String} where (optional) 'before' or 'after' defaults to before
9594          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9595          * @return {Roo.Element} the inserted Element
9596          */
9597         insertSibling: function(el, where, returnDom){
9598             where = where ? where.toLowerCase() : 'before';
9599             el = el || {};
9600             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9601
9602             if(typeof el == 'object' && !el.nodeType){ // dh config
9603                 if(where == 'after' && !this.dom.nextSibling){
9604                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9605                 }else{
9606                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9607                 }
9608
9609             }else{
9610                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9611                             where == 'before' ? this.dom : this.dom.nextSibling);
9612                 if(!returnDom){
9613                     rt = Roo.get(rt);
9614                 }
9615             }
9616             return rt;
9617         },
9618
9619         /**
9620          * Creates and wraps this element with another element
9621          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9622          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9623          * @return {HTMLElement/Element} The newly created wrapper element
9624          */
9625         wrap: function(config, returnDom){
9626             if(!config){
9627                 config = {tag: "div"};
9628             }
9629             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9630             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9631             return newEl;
9632         },
9633
9634         /**
9635          * Replaces the passed element with this element
9636          * @param {String/HTMLElement/Element} el The element to replace
9637          * @return {Roo.Element} this
9638          */
9639         replace: function(el){
9640             el = Roo.get(el);
9641             this.insertBefore(el);
9642             el.remove();
9643             return this;
9644         },
9645
9646         /**
9647          * Inserts an html fragment into this element
9648          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9649          * @param {String} html The HTML fragment
9650          * @param {Boolean} returnEl True to return an Roo.Element
9651          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9652          */
9653         insertHtml : function(where, html, returnEl){
9654             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9655             return returnEl ? Roo.get(el) : el;
9656         },
9657
9658         /**
9659          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9660          * @param {Object} o The object with the attributes
9661          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9662          * @return {Roo.Element} this
9663          */
9664         set : function(o, useSet){
9665             var el = this.dom;
9666             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9667             for(var attr in o){
9668                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9669                 if(attr=="cls"){
9670                     el.className = o["cls"];
9671                 }else{
9672                     if(useSet) {
9673                         el.setAttribute(attr, o[attr]);
9674                     } else {
9675                         el[attr] = o[attr];
9676                     }
9677                 }
9678             }
9679             if(o.style){
9680                 Roo.DomHelper.applyStyles(el, o.style);
9681             }
9682             return this;
9683         },
9684
9685         /**
9686          * Convenience method for constructing a KeyMap
9687          * @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:
9688          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9689          * @param {Function} fn The function to call
9690          * @param {Object} scope (optional) The scope of the function
9691          * @return {Roo.KeyMap} The KeyMap created
9692          */
9693         addKeyListener : function(key, fn, scope){
9694             var config;
9695             if(typeof key != "object" || key instanceof Array){
9696                 config = {
9697                     key: key,
9698                     fn: fn,
9699                     scope: scope
9700                 };
9701             }else{
9702                 config = {
9703                     key : key.key,
9704                     shift : key.shift,
9705                     ctrl : key.ctrl,
9706                     alt : key.alt,
9707                     fn: fn,
9708                     scope: scope
9709                 };
9710             }
9711             return new Roo.KeyMap(this, config);
9712         },
9713
9714         /**
9715          * Creates a KeyMap for this element
9716          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9717          * @return {Roo.KeyMap} The KeyMap created
9718          */
9719         addKeyMap : function(config){
9720             return new Roo.KeyMap(this, config);
9721         },
9722
9723         /**
9724          * Returns true if this element is scrollable.
9725          * @return {Boolean}
9726          */
9727          isScrollable : function(){
9728             var dom = this.dom;
9729             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9730         },
9731
9732         /**
9733          * 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().
9734          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9735          * @param {Number} value The new scroll value
9736          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9737          * @return {Element} this
9738          */
9739
9740         scrollTo : function(side, value, animate){
9741             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9742             if(!animate || !A){
9743                 this.dom[prop] = value;
9744             }else{
9745                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9746                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9747             }
9748             return this;
9749         },
9750
9751         /**
9752          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9753          * within this element's scrollable range.
9754          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9755          * @param {Number} distance How far to scroll the element in pixels
9756          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9757          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9758          * was scrolled as far as it could go.
9759          */
9760          scroll : function(direction, distance, animate){
9761              if(!this.isScrollable()){
9762                  return;
9763              }
9764              var el = this.dom;
9765              var l = el.scrollLeft, t = el.scrollTop;
9766              var w = el.scrollWidth, h = el.scrollHeight;
9767              var cw = el.clientWidth, ch = el.clientHeight;
9768              direction = direction.toLowerCase();
9769              var scrolled = false;
9770              var a = this.preanim(arguments, 2);
9771              switch(direction){
9772                  case "l":
9773                  case "left":
9774                      if(w - l > cw){
9775                          var v = Math.min(l + distance, w-cw);
9776                          this.scrollTo("left", v, a);
9777                          scrolled = true;
9778                      }
9779                      break;
9780                 case "r":
9781                 case "right":
9782                      if(l > 0){
9783                          var v = Math.max(l - distance, 0);
9784                          this.scrollTo("left", v, a);
9785                          scrolled = true;
9786                      }
9787                      break;
9788                 case "t":
9789                 case "top":
9790                 case "up":
9791                      if(t > 0){
9792                          var v = Math.max(t - distance, 0);
9793                          this.scrollTo("top", v, a);
9794                          scrolled = true;
9795                      }
9796                      break;
9797                 case "b":
9798                 case "bottom":
9799                 case "down":
9800                      if(h - t > ch){
9801                          var v = Math.min(t + distance, h-ch);
9802                          this.scrollTo("top", v, a);
9803                          scrolled = true;
9804                      }
9805                      break;
9806              }
9807              return scrolled;
9808         },
9809
9810         /**
9811          * Translates the passed page coordinates into left/top css values for this element
9812          * @param {Number/Array} x The page x or an array containing [x, y]
9813          * @param {Number} y The page y
9814          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9815          */
9816         translatePoints : function(x, y){
9817             if(typeof x == 'object' || x instanceof Array){
9818                 y = x[1]; x = x[0];
9819             }
9820             var p = this.getStyle('position');
9821             var o = this.getXY();
9822
9823             var l = parseInt(this.getStyle('left'), 10);
9824             var t = parseInt(this.getStyle('top'), 10);
9825
9826             if(isNaN(l)){
9827                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9828             }
9829             if(isNaN(t)){
9830                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9831             }
9832
9833             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9834         },
9835
9836         /**
9837          * Returns the current scroll position of the element.
9838          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9839          */
9840         getScroll : function(){
9841             var d = this.dom, doc = document;
9842             if(d == doc || d == doc.body){
9843                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9844                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9845                 return {left: l, top: t};
9846             }else{
9847                 return {left: d.scrollLeft, top: d.scrollTop};
9848             }
9849         },
9850
9851         /**
9852          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9853          * are convert to standard 6 digit hex color.
9854          * @param {String} attr The css attribute
9855          * @param {String} defaultValue The default value to use when a valid color isn't found
9856          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9857          * YUI color anims.
9858          */
9859         getColor : function(attr, defaultValue, prefix){
9860             var v = this.getStyle(attr);
9861             if(!v || v == "transparent" || v == "inherit") {
9862                 return defaultValue;
9863             }
9864             var color = typeof prefix == "undefined" ? "#" : prefix;
9865             if(v.substr(0, 4) == "rgb("){
9866                 var rvs = v.slice(4, v.length -1).split(",");
9867                 for(var i = 0; i < 3; i++){
9868                     var h = parseInt(rvs[i]).toString(16);
9869                     if(h < 16){
9870                         h = "0" + h;
9871                     }
9872                     color += h;
9873                 }
9874             } else {
9875                 if(v.substr(0, 1) == "#"){
9876                     if(v.length == 4) {
9877                         for(var i = 1; i < 4; i++){
9878                             var c = v.charAt(i);
9879                             color +=  c + c;
9880                         }
9881                     }else if(v.length == 7){
9882                         color += v.substr(1);
9883                     }
9884                 }
9885             }
9886             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9887         },
9888
9889         /**
9890          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9891          * gradient background, rounded corners and a 4-way shadow.
9892          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9893          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9894          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9895          * @return {Roo.Element} this
9896          */
9897         boxWrap : function(cls){
9898             cls = cls || 'x-box';
9899             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9900             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9901             return el;
9902         },
9903
9904         /**
9905          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9906          * @param {String} namespace The namespace in which to look for the attribute
9907          * @param {String} name The attribute name
9908          * @return {String} The attribute value
9909          */
9910         getAttributeNS : Roo.isIE ? function(ns, name){
9911             var d = this.dom;
9912             var type = typeof d[ns+":"+name];
9913             if(type != 'undefined' && type != 'unknown'){
9914                 return d[ns+":"+name];
9915             }
9916             return d[name];
9917         } : function(ns, name){
9918             var d = this.dom;
9919             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9920         },
9921         
9922         
9923         /**
9924          * Sets or Returns the value the dom attribute value
9925          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9926          * @param {String} value (optional) The value to set the attribute to
9927          * @return {String} The attribute value
9928          */
9929         attr : function(name){
9930             if (arguments.length > 1) {
9931                 this.dom.setAttribute(name, arguments[1]);
9932                 return arguments[1];
9933             }
9934             if (typeof(name) == 'object') {
9935                 for(var i in name) {
9936                     this.attr(i, name[i]);
9937                 }
9938                 return name;
9939             }
9940             
9941             
9942             if (!this.dom.hasAttribute(name)) {
9943                 return undefined;
9944             }
9945             return this.dom.getAttribute(name);
9946         }
9947         
9948         
9949         
9950     };
9951
9952     var ep = El.prototype;
9953
9954     /**
9955      * Appends an event handler (Shorthand for addListener)
9956      * @param {String}   eventName     The type of event to append
9957      * @param {Function} fn        The method the event invokes
9958      * @param {Object} scope       (optional) The scope (this object) of the fn
9959      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9960      * @method
9961      */
9962     ep.on = ep.addListener;
9963         // backwards compat
9964     ep.mon = ep.addListener;
9965
9966     /**
9967      * Removes an event handler from this element (shorthand for removeListener)
9968      * @param {String} eventName the type of event to remove
9969      * @param {Function} fn the method the event invokes
9970      * @return {Roo.Element} this
9971      * @method
9972      */
9973     ep.un = ep.removeListener;
9974
9975     /**
9976      * true to automatically adjust width and height settings for box-model issues (default to true)
9977      */
9978     ep.autoBoxAdjust = true;
9979
9980     // private
9981     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9982
9983     // private
9984     El.addUnits = function(v, defaultUnit){
9985         if(v === "" || v == "auto"){
9986             return v;
9987         }
9988         if(v === undefined){
9989             return '';
9990         }
9991         if(typeof v == "number" || !El.unitPattern.test(v)){
9992             return v + (defaultUnit || 'px');
9993         }
9994         return v;
9995     };
9996
9997     // special markup used throughout Roo when box wrapping elements
9998     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>';
9999     /**
10000      * Visibility mode constant - Use visibility to hide element
10001      * @static
10002      * @type Number
10003      */
10004     El.VISIBILITY = 1;
10005     /**
10006      * Visibility mode constant - Use display to hide element
10007      * @static
10008      * @type Number
10009      */
10010     El.DISPLAY = 2;
10011
10012     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10013     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10014     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10015
10016
10017
10018     /**
10019      * @private
10020      */
10021     El.cache = {};
10022
10023     var docEl;
10024
10025     /**
10026      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10027      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10028      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10029      * @return {Element} The Element object
10030      * @static
10031      */
10032     El.get = function(el){
10033         var ex, elm, id;
10034         if(!el){ return null; }
10035         if(typeof el == "string"){ // element id
10036             if(!(elm = document.getElementById(el))){
10037                 return null;
10038             }
10039             if(ex = El.cache[el]){
10040                 ex.dom = elm;
10041             }else{
10042                 ex = El.cache[el] = new El(elm);
10043             }
10044             return ex;
10045         }else if(el.tagName){ // dom element
10046             if(!(id = el.id)){
10047                 id = Roo.id(el);
10048             }
10049             if(ex = El.cache[id]){
10050                 ex.dom = el;
10051             }else{
10052                 ex = El.cache[id] = new El(el);
10053             }
10054             return ex;
10055         }else if(el instanceof El){
10056             if(el != docEl){
10057                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10058                                                               // catch case where it hasn't been appended
10059                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10060             }
10061             return el;
10062         }else if(el.isComposite){
10063             return el;
10064         }else if(el instanceof Array){
10065             return El.select(el);
10066         }else if(el == document){
10067             // create a bogus element object representing the document object
10068             if(!docEl){
10069                 var f = function(){};
10070                 f.prototype = El.prototype;
10071                 docEl = new f();
10072                 docEl.dom = document;
10073             }
10074             return docEl;
10075         }
10076         return null;
10077     };
10078
10079     // private
10080     El.uncache = function(el){
10081         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10082             if(a[i]){
10083                 delete El.cache[a[i].id || a[i]];
10084             }
10085         }
10086     };
10087
10088     // private
10089     // Garbage collection - uncache elements/purge listeners on orphaned elements
10090     // so we don't hold a reference and cause the browser to retain them
10091     El.garbageCollect = function(){
10092         if(!Roo.enableGarbageCollector){
10093             clearInterval(El.collectorThread);
10094             return;
10095         }
10096         for(var eid in El.cache){
10097             var el = El.cache[eid], d = el.dom;
10098             // -------------------------------------------------------
10099             // Determining what is garbage:
10100             // -------------------------------------------------------
10101             // !d
10102             // dom node is null, definitely garbage
10103             // -------------------------------------------------------
10104             // !d.parentNode
10105             // no parentNode == direct orphan, definitely garbage
10106             // -------------------------------------------------------
10107             // !d.offsetParent && !document.getElementById(eid)
10108             // display none elements have no offsetParent so we will
10109             // also try to look it up by it's id. However, check
10110             // offsetParent first so we don't do unneeded lookups.
10111             // This enables collection of elements that are not orphans
10112             // directly, but somewhere up the line they have an orphan
10113             // parent.
10114             // -------------------------------------------------------
10115             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10116                 delete El.cache[eid];
10117                 if(d && Roo.enableListenerCollection){
10118                     E.purgeElement(d);
10119                 }
10120             }
10121         }
10122     }
10123     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10124
10125
10126     // dom is optional
10127     El.Flyweight = function(dom){
10128         this.dom = dom;
10129     };
10130     El.Flyweight.prototype = El.prototype;
10131
10132     El._flyweights = {};
10133     /**
10134      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10135      * the dom node can be overwritten by other code.
10136      * @param {String/HTMLElement} el The dom node or id
10137      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10138      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10139      * @static
10140      * @return {Element} The shared Element object
10141      */
10142     El.fly = function(el, named){
10143         named = named || '_global';
10144         el = Roo.getDom(el);
10145         if(!el){
10146             return null;
10147         }
10148         if(!El._flyweights[named]){
10149             El._flyweights[named] = new El.Flyweight();
10150         }
10151         El._flyweights[named].dom = el;
10152         return El._flyweights[named];
10153     };
10154
10155     /**
10156      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10157      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10158      * Shorthand of {@link Roo.Element#get}
10159      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10160      * @return {Element} The Element object
10161      * @member Roo
10162      * @method get
10163      */
10164     Roo.get = El.get;
10165     /**
10166      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10167      * the dom node can be overwritten by other code.
10168      * Shorthand of {@link Roo.Element#fly}
10169      * @param {String/HTMLElement} el The dom node or id
10170      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10171      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10172      * @static
10173      * @return {Element} The shared Element object
10174      * @member Roo
10175      * @method fly
10176      */
10177     Roo.fly = El.fly;
10178
10179     // speedy lookup for elements never to box adjust
10180     var noBoxAdjust = Roo.isStrict ? {
10181         select:1
10182     } : {
10183         input:1, select:1, textarea:1
10184     };
10185     if(Roo.isIE || Roo.isGecko){
10186         noBoxAdjust['button'] = 1;
10187     }
10188
10189
10190     Roo.EventManager.on(window, 'unload', function(){
10191         delete El.cache;
10192         delete El._flyweights;
10193     });
10194 })();
10195
10196
10197
10198
10199 if(Roo.DomQuery){
10200     Roo.Element.selectorFunction = Roo.DomQuery.select;
10201 }
10202
10203 Roo.Element.select = function(selector, unique, root){
10204     var els;
10205     if(typeof selector == "string"){
10206         els = Roo.Element.selectorFunction(selector, root);
10207     }else if(selector.length !== undefined){
10208         els = selector;
10209     }else{
10210         throw "Invalid selector";
10211     }
10212     if(unique === true){
10213         return new Roo.CompositeElement(els);
10214     }else{
10215         return new Roo.CompositeElementLite(els);
10216     }
10217 };
10218 /**
10219  * Selects elements based on the passed CSS selector to enable working on them as 1.
10220  * @param {String/Array} selector The CSS selector or an array of elements
10221  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10222  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10223  * @return {CompositeElementLite/CompositeElement}
10224  * @member Roo
10225  * @method select
10226  */
10227 Roo.select = Roo.Element.select;
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242 /*
10243  * Based on:
10244  * Ext JS Library 1.1.1
10245  * Copyright(c) 2006-2007, Ext JS, LLC.
10246  *
10247  * Originally Released Under LGPL - original licence link has changed is not relivant.
10248  *
10249  * Fork - LGPL
10250  * <script type="text/javascript">
10251  */
10252
10253
10254
10255 //Notifies Element that fx methods are available
10256 Roo.enableFx = true;
10257
10258 /**
10259  * @class Roo.Fx
10260  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10261  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10262  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10263  * Element effects to work.</p><br/>
10264  *
10265  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10266  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10267  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10268  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10269  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10270  * expected results and should be done with care.</p><br/>
10271  *
10272  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10273  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10274 <pre>
10275 Value  Description
10276 -----  -----------------------------
10277 tl     The top left corner
10278 t      The center of the top edge
10279 tr     The top right corner
10280 l      The center of the left edge
10281 r      The center of the right edge
10282 bl     The bottom left corner
10283 b      The center of the bottom edge
10284 br     The bottom right corner
10285 </pre>
10286  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10287  * below are common options that can be passed to any Fx method.</b>
10288  * @cfg {Function} callback A function called when the effect is finished
10289  * @cfg {Object} scope The scope of the effect function
10290  * @cfg {String} easing A valid Easing value for the effect
10291  * @cfg {String} afterCls A css class to apply after the effect
10292  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10293  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10294  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10295  * effects that end with the element being visually hidden, ignored otherwise)
10296  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10297  * a function which returns such a specification that will be applied to the Element after the effect finishes
10298  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10299  * @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
10300  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10301  */
10302 Roo.Fx = {
10303         /**
10304          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10305          * origin for the slide effect.  This function automatically handles wrapping the element with
10306          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10307          * Usage:
10308          *<pre><code>
10309 // default: slide the element in from the top
10310 el.slideIn();
10311
10312 // custom: slide the element in from the right with a 2-second duration
10313 el.slideIn('r', { duration: 2 });
10314
10315 // common config options shown with default values
10316 el.slideIn('t', {
10317     easing: 'easeOut',
10318     duration: .5
10319 });
10320 </code></pre>
10321          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10322          * @param {Object} options (optional) Object literal with any of the Fx config options
10323          * @return {Roo.Element} The Element
10324          */
10325     slideIn : function(anchor, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330
10331             anchor = anchor || "t";
10332
10333             // fix display to visibility
10334             this.fixDisplay();
10335
10336             // restore values after effect
10337             var r = this.getFxRestore();
10338             var b = this.getBox();
10339             // fixed size for slide
10340             this.setSize(b);
10341
10342             // wrap if needed
10343             var wrap = this.fxWrap(r.pos, o, "hidden");
10344
10345             var st = this.dom.style;
10346             st.visibility = "visible";
10347             st.position = "absolute";
10348
10349             // clear out temp styles after slide and unwrap
10350             var after = function(){
10351                 el.fxUnwrap(wrap, r.pos, o);
10352                 st.width = r.width;
10353                 st.height = r.height;
10354                 el.afterFx(o);
10355             };
10356             // time to calc the positions
10357             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10358
10359             switch(anchor.toLowerCase()){
10360                 case "t":
10361                     wrap.setSize(b.width, 0);
10362                     st.left = st.bottom = "0";
10363                     a = {height: bh};
10364                 break;
10365                 case "l":
10366                     wrap.setSize(0, b.height);
10367                     st.right = st.top = "0";
10368                     a = {width: bw};
10369                 break;
10370                 case "r":
10371                     wrap.setSize(0, b.height);
10372                     wrap.setX(b.right);
10373                     st.left = st.top = "0";
10374                     a = {width: bw, points: pt};
10375                 break;
10376                 case "b":
10377                     wrap.setSize(b.width, 0);
10378                     wrap.setY(b.bottom);
10379                     st.left = st.top = "0";
10380                     a = {height: bh, points: pt};
10381                 break;
10382                 case "tl":
10383                     wrap.setSize(0, 0);
10384                     st.right = st.bottom = "0";
10385                     a = {width: bw, height: bh};
10386                 break;
10387                 case "bl":
10388                     wrap.setSize(0, 0);
10389                     wrap.setY(b.y+b.height);
10390                     st.right = st.top = "0";
10391                     a = {width: bw, height: bh, points: pt};
10392                 break;
10393                 case "br":
10394                     wrap.setSize(0, 0);
10395                     wrap.setXY([b.right, b.bottom]);
10396                     st.left = st.top = "0";
10397                     a = {width: bw, height: bh, points: pt};
10398                 break;
10399                 case "tr":
10400                     wrap.setSize(0, 0);
10401                     wrap.setX(b.x+b.width);
10402                     st.left = st.bottom = "0";
10403                     a = {width: bw, height: bh, points: pt};
10404                 break;
10405             }
10406             this.dom.style.visibility = "visible";
10407             wrap.show();
10408
10409             arguments.callee.anim = wrap.fxanim(a,
10410                 o,
10411                 'motion',
10412                 .5,
10413                 'easeOut', after);
10414         });
10415         return this;
10416     },
10417     
10418         /**
10419          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10420          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10421          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10422          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10423          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10424          * Usage:
10425          *<pre><code>
10426 // default: slide the element out to the top
10427 el.slideOut();
10428
10429 // custom: slide the element out to the right with a 2-second duration
10430 el.slideOut('r', { duration: 2 });
10431
10432 // common config options shown with default values
10433 el.slideOut('t', {
10434     easing: 'easeOut',
10435     duration: .5,
10436     remove: false,
10437     useDisplay: false
10438 });
10439 </code></pre>
10440          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10441          * @param {Object} options (optional) Object literal with any of the Fx config options
10442          * @return {Roo.Element} The Element
10443          */
10444     slideOut : function(anchor, o){
10445         var el = this.getFxEl();
10446         o = o || {};
10447
10448         el.queueFx(o, function(){
10449
10450             anchor = anchor || "t";
10451
10452             // restore values after effect
10453             var r = this.getFxRestore();
10454             
10455             var b = this.getBox();
10456             // fixed size for slide
10457             this.setSize(b);
10458
10459             // wrap if needed
10460             var wrap = this.fxWrap(r.pos, o, "visible");
10461
10462             var st = this.dom.style;
10463             st.visibility = "visible";
10464             st.position = "absolute";
10465
10466             wrap.setSize(b);
10467
10468             var after = function(){
10469                 if(o.useDisplay){
10470                     el.setDisplayed(false);
10471                 }else{
10472                     el.hide();
10473                 }
10474
10475                 el.fxUnwrap(wrap, r.pos, o);
10476
10477                 st.width = r.width;
10478                 st.height = r.height;
10479
10480                 el.afterFx(o);
10481             };
10482
10483             var a, zero = {to: 0};
10484             switch(anchor.toLowerCase()){
10485                 case "t":
10486                     st.left = st.bottom = "0";
10487                     a = {height: zero};
10488                 break;
10489                 case "l":
10490                     st.right = st.top = "0";
10491                     a = {width: zero};
10492                 break;
10493                 case "r":
10494                     st.left = st.top = "0";
10495                     a = {width: zero, points: {to:[b.right, b.y]}};
10496                 break;
10497                 case "b":
10498                     st.left = st.top = "0";
10499                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10500                 break;
10501                 case "tl":
10502                     st.right = st.bottom = "0";
10503                     a = {width: zero, height: zero};
10504                 break;
10505                 case "bl":
10506                     st.right = st.top = "0";
10507                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10508                 break;
10509                 case "br":
10510                     st.left = st.top = "0";
10511                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10512                 break;
10513                 case "tr":
10514                     st.left = st.bottom = "0";
10515                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10516                 break;
10517             }
10518
10519             arguments.callee.anim = wrap.fxanim(a,
10520                 o,
10521                 'motion',
10522                 .5,
10523                 "easeOut", after);
10524         });
10525         return this;
10526     },
10527
10528         /**
10529          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10530          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10531          * The element must be removed from the DOM using the 'remove' config option if desired.
10532          * Usage:
10533          *<pre><code>
10534 // default
10535 el.puff();
10536
10537 // common config options shown with default values
10538 el.puff({
10539     easing: 'easeOut',
10540     duration: .5,
10541     remove: false,
10542     useDisplay: false
10543 });
10544 </code></pre>
10545          * @param {Object} options (optional) Object literal with any of the Fx config options
10546          * @return {Roo.Element} The Element
10547          */
10548     puff : function(o){
10549         var el = this.getFxEl();
10550         o = o || {};
10551
10552         el.queueFx(o, function(){
10553             this.clearOpacity();
10554             this.show();
10555
10556             // restore values after effect
10557             var r = this.getFxRestore();
10558             var st = this.dom.style;
10559
10560             var after = function(){
10561                 if(o.useDisplay){
10562                     el.setDisplayed(false);
10563                 }else{
10564                     el.hide();
10565                 }
10566
10567                 el.clearOpacity();
10568
10569                 el.setPositioning(r.pos);
10570                 st.width = r.width;
10571                 st.height = r.height;
10572                 st.fontSize = '';
10573                 el.afterFx(o);
10574             };
10575
10576             var width = this.getWidth();
10577             var height = this.getHeight();
10578
10579             arguments.callee.anim = this.fxanim({
10580                     width : {to: this.adjustWidth(width * 2)},
10581                     height : {to: this.adjustHeight(height * 2)},
10582                     points : {by: [-(width * .5), -(height * .5)]},
10583                     opacity : {to: 0},
10584                     fontSize: {to:200, unit: "%"}
10585                 },
10586                 o,
10587                 'motion',
10588                 .5,
10589                 "easeOut", after);
10590         });
10591         return this;
10592     },
10593
10594         /**
10595          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10596          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10597          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10598          * Usage:
10599          *<pre><code>
10600 // default
10601 el.switchOff();
10602
10603 // all config options shown with default values
10604 el.switchOff({
10605     easing: 'easeIn',
10606     duration: .3,
10607     remove: false,
10608     useDisplay: false
10609 });
10610 </code></pre>
10611          * @param {Object} options (optional) Object literal with any of the Fx config options
10612          * @return {Roo.Element} The Element
10613          */
10614     switchOff : function(o){
10615         var el = this.getFxEl();
10616         o = o || {};
10617
10618         el.queueFx(o, function(){
10619             this.clearOpacity();
10620             this.clip();
10621
10622             // restore values after effect
10623             var r = this.getFxRestore();
10624             var st = this.dom.style;
10625
10626             var after = function(){
10627                 if(o.useDisplay){
10628                     el.setDisplayed(false);
10629                 }else{
10630                     el.hide();
10631                 }
10632
10633                 el.clearOpacity();
10634                 el.setPositioning(r.pos);
10635                 st.width = r.width;
10636                 st.height = r.height;
10637
10638                 el.afterFx(o);
10639             };
10640
10641             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10642                 this.clearOpacity();
10643                 (function(){
10644                     this.fxanim({
10645                         height:{to:1},
10646                         points:{by:[0, this.getHeight() * .5]}
10647                     }, o, 'motion', 0.3, 'easeIn', after);
10648                 }).defer(100, this);
10649             });
10650         });
10651         return this;
10652     },
10653
10654     /**
10655      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10656      * changed using the "attr" config option) and then fading back to the original color. If no original
10657      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10658      * Usage:
10659 <pre><code>
10660 // default: highlight background to yellow
10661 el.highlight();
10662
10663 // custom: highlight foreground text to blue for 2 seconds
10664 el.highlight("0000ff", { attr: 'color', duration: 2 });
10665
10666 // common config options shown with default values
10667 el.highlight("ffff9c", {
10668     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10669     endColor: (current color) or "ffffff",
10670     easing: 'easeIn',
10671     duration: 1
10672 });
10673 </code></pre>
10674      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10675      * @param {Object} options (optional) Object literal with any of the Fx config options
10676      * @return {Roo.Element} The Element
10677      */ 
10678     highlight : function(color, o){
10679         var el = this.getFxEl();
10680         o = o || {};
10681
10682         el.queueFx(o, function(){
10683             color = color || "ffff9c";
10684             attr = o.attr || "backgroundColor";
10685
10686             this.clearOpacity();
10687             this.show();
10688
10689             var origColor = this.getColor(attr);
10690             var restoreColor = this.dom.style[attr];
10691             endColor = (o.endColor || origColor) || "ffffff";
10692
10693             var after = function(){
10694                 el.dom.style[attr] = restoreColor;
10695                 el.afterFx(o);
10696             };
10697
10698             var a = {};
10699             a[attr] = {from: color, to: endColor};
10700             arguments.callee.anim = this.fxanim(a,
10701                 o,
10702                 'color',
10703                 1,
10704                 'easeIn', after);
10705         });
10706         return this;
10707     },
10708
10709    /**
10710     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10711     * Usage:
10712 <pre><code>
10713 // default: a single light blue ripple
10714 el.frame();
10715
10716 // custom: 3 red ripples lasting 3 seconds total
10717 el.frame("ff0000", 3, { duration: 3 });
10718
10719 // common config options shown with default values
10720 el.frame("C3DAF9", 1, {
10721     duration: 1 //duration of entire animation (not each individual ripple)
10722     // Note: Easing is not configurable and will be ignored if included
10723 });
10724 </code></pre>
10725     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10726     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10727     * @param {Object} options (optional) Object literal with any of the Fx config options
10728     * @return {Roo.Element} The Element
10729     */
10730     frame : function(color, count, o){
10731         var el = this.getFxEl();
10732         o = o || {};
10733
10734         el.queueFx(o, function(){
10735             color = color || "#C3DAF9";
10736             if(color.length == 6){
10737                 color = "#" + color;
10738             }
10739             count = count || 1;
10740             duration = o.duration || 1;
10741             this.show();
10742
10743             var b = this.getBox();
10744             var animFn = function(){
10745                 var proxy = this.createProxy({
10746
10747                      style:{
10748                         visbility:"hidden",
10749                         position:"absolute",
10750                         "z-index":"35000", // yee haw
10751                         border:"0px solid " + color
10752                      }
10753                   });
10754                 var scale = Roo.isBorderBox ? 2 : 1;
10755                 proxy.animate({
10756                     top:{from:b.y, to:b.y - 20},
10757                     left:{from:b.x, to:b.x - 20},
10758                     borderWidth:{from:0, to:10},
10759                     opacity:{from:1, to:0},
10760                     height:{from:b.height, to:(b.height + (20*scale))},
10761                     width:{from:b.width, to:(b.width + (20*scale))}
10762                 }, duration, function(){
10763                     proxy.remove();
10764                 });
10765                 if(--count > 0){
10766                      animFn.defer((duration/2)*1000, this);
10767                 }else{
10768                     el.afterFx(o);
10769                 }
10770             };
10771             animFn.call(this);
10772         });
10773         return this;
10774     },
10775
10776    /**
10777     * Creates a pause before any subsequent queued effects begin.  If there are
10778     * no effects queued after the pause it will have no effect.
10779     * Usage:
10780 <pre><code>
10781 el.pause(1);
10782 </code></pre>
10783     * @param {Number} seconds The length of time to pause (in seconds)
10784     * @return {Roo.Element} The Element
10785     */
10786     pause : function(seconds){
10787         var el = this.getFxEl();
10788         var o = {};
10789
10790         el.queueFx(o, function(){
10791             setTimeout(function(){
10792                 el.afterFx(o);
10793             }, seconds * 1000);
10794         });
10795         return this;
10796     },
10797
10798    /**
10799     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10800     * using the "endOpacity" config option.
10801     * Usage:
10802 <pre><code>
10803 // default: fade in from opacity 0 to 100%
10804 el.fadeIn();
10805
10806 // custom: fade in from opacity 0 to 75% over 2 seconds
10807 el.fadeIn({ endOpacity: .75, duration: 2});
10808
10809 // common config options shown with default values
10810 el.fadeIn({
10811     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10812     easing: 'easeOut',
10813     duration: .5
10814 });
10815 </code></pre>
10816     * @param {Object} options (optional) Object literal with any of the Fx config options
10817     * @return {Roo.Element} The Element
10818     */
10819     fadeIn : function(o){
10820         var el = this.getFxEl();
10821         o = o || {};
10822         el.queueFx(o, function(){
10823             this.setOpacity(0);
10824             this.fixDisplay();
10825             this.dom.style.visibility = 'visible';
10826             var to = o.endOpacity || 1;
10827             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10828                 o, null, .5, "easeOut", function(){
10829                 if(to == 1){
10830                     this.clearOpacity();
10831                 }
10832                 el.afterFx(o);
10833             });
10834         });
10835         return this;
10836     },
10837
10838    /**
10839     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10840     * using the "endOpacity" config option.
10841     * Usage:
10842 <pre><code>
10843 // default: fade out from the element's current opacity to 0
10844 el.fadeOut();
10845
10846 // custom: fade out from the element's current opacity to 25% over 2 seconds
10847 el.fadeOut({ endOpacity: .25, duration: 2});
10848
10849 // common config options shown with default values
10850 el.fadeOut({
10851     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10852     easing: 'easeOut',
10853     duration: .5
10854     remove: false,
10855     useDisplay: false
10856 });
10857 </code></pre>
10858     * @param {Object} options (optional) Object literal with any of the Fx config options
10859     * @return {Roo.Element} The Element
10860     */
10861     fadeOut : function(o){
10862         var el = this.getFxEl();
10863         o = o || {};
10864         el.queueFx(o, function(){
10865             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10866                 o, null, .5, "easeOut", function(){
10867                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10868                      this.dom.style.display = "none";
10869                 }else{
10870                      this.dom.style.visibility = "hidden";
10871                 }
10872                 this.clearOpacity();
10873                 el.afterFx(o);
10874             });
10875         });
10876         return this;
10877     },
10878
10879    /**
10880     * Animates the transition of an element's dimensions from a starting height/width
10881     * to an ending height/width.
10882     * Usage:
10883 <pre><code>
10884 // change height and width to 100x100 pixels
10885 el.scale(100, 100);
10886
10887 // common config options shown with default values.  The height and width will default to
10888 // the element's existing values if passed as null.
10889 el.scale(
10890     [element's width],
10891     [element's height], {
10892     easing: 'easeOut',
10893     duration: .35
10894 });
10895 </code></pre>
10896     * @param {Number} width  The new width (pass undefined to keep the original width)
10897     * @param {Number} height  The new height (pass undefined to keep the original height)
10898     * @param {Object} options (optional) Object literal with any of the Fx config options
10899     * @return {Roo.Element} The Element
10900     */
10901     scale : function(w, h, o){
10902         this.shift(Roo.apply({}, o, {
10903             width: w,
10904             height: h
10905         }));
10906         return this;
10907     },
10908
10909    /**
10910     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10911     * Any of these properties not specified in the config object will not be changed.  This effect 
10912     * requires that at least one new dimension, position or opacity setting must be passed in on
10913     * the config object in order for the function to have any effect.
10914     * Usage:
10915 <pre><code>
10916 // slide the element horizontally to x position 200 while changing the height and opacity
10917 el.shift({ x: 200, height: 50, opacity: .8 });
10918
10919 // common config options shown with default values.
10920 el.shift({
10921     width: [element's width],
10922     height: [element's height],
10923     x: [element's x position],
10924     y: [element's y position],
10925     opacity: [element's opacity],
10926     easing: 'easeOut',
10927     duration: .35
10928 });
10929 </code></pre>
10930     * @param {Object} options  Object literal with any of the Fx config options
10931     * @return {Roo.Element} The Element
10932     */
10933     shift : function(o){
10934         var el = this.getFxEl();
10935         o = o || {};
10936         el.queueFx(o, function(){
10937             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10938             if(w !== undefined){
10939                 a.width = {to: this.adjustWidth(w)};
10940             }
10941             if(h !== undefined){
10942                 a.height = {to: this.adjustHeight(h)};
10943             }
10944             if(x !== undefined || y !== undefined){
10945                 a.points = {to: [
10946                     x !== undefined ? x : this.getX(),
10947                     y !== undefined ? y : this.getY()
10948                 ]};
10949             }
10950             if(op !== undefined){
10951                 a.opacity = {to: op};
10952             }
10953             if(o.xy !== undefined){
10954                 a.points = {to: o.xy};
10955             }
10956             arguments.callee.anim = this.fxanim(a,
10957                 o, 'motion', .35, "easeOut", function(){
10958                 el.afterFx(o);
10959             });
10960         });
10961         return this;
10962     },
10963
10964         /**
10965          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10966          * ending point of the effect.
10967          * Usage:
10968          *<pre><code>
10969 // default: slide the element downward while fading out
10970 el.ghost();
10971
10972 // custom: slide the element out to the right with a 2-second duration
10973 el.ghost('r', { duration: 2 });
10974
10975 // common config options shown with default values
10976 el.ghost('b', {
10977     easing: 'easeOut',
10978     duration: .5
10979     remove: false,
10980     useDisplay: false
10981 });
10982 </code></pre>
10983          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10984          * @param {Object} options (optional) Object literal with any of the Fx config options
10985          * @return {Roo.Element} The Element
10986          */
10987     ghost : function(anchor, o){
10988         var el = this.getFxEl();
10989         o = o || {};
10990
10991         el.queueFx(o, function(){
10992             anchor = anchor || "b";
10993
10994             // restore values after effect
10995             var r = this.getFxRestore();
10996             var w = this.getWidth(),
10997                 h = this.getHeight();
10998
10999             var st = this.dom.style;
11000
11001             var after = function(){
11002                 if(o.useDisplay){
11003                     el.setDisplayed(false);
11004                 }else{
11005                     el.hide();
11006                 }
11007
11008                 el.clearOpacity();
11009                 el.setPositioning(r.pos);
11010                 st.width = r.width;
11011                 st.height = r.height;
11012
11013                 el.afterFx(o);
11014             };
11015
11016             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11017             switch(anchor.toLowerCase()){
11018                 case "t":
11019                     pt.by = [0, -h];
11020                 break;
11021                 case "l":
11022                     pt.by = [-w, 0];
11023                 break;
11024                 case "r":
11025                     pt.by = [w, 0];
11026                 break;
11027                 case "b":
11028                     pt.by = [0, h];
11029                 break;
11030                 case "tl":
11031                     pt.by = [-w, -h];
11032                 break;
11033                 case "bl":
11034                     pt.by = [-w, h];
11035                 break;
11036                 case "br":
11037                     pt.by = [w, h];
11038                 break;
11039                 case "tr":
11040                     pt.by = [w, -h];
11041                 break;
11042             }
11043
11044             arguments.callee.anim = this.fxanim(a,
11045                 o,
11046                 'motion',
11047                 .5,
11048                 "easeOut", after);
11049         });
11050         return this;
11051     },
11052
11053         /**
11054          * Ensures that all effects queued after syncFx is called on the element are
11055          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11056          * @return {Roo.Element} The Element
11057          */
11058     syncFx : function(){
11059         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11060             block : false,
11061             concurrent : true,
11062             stopFx : false
11063         });
11064         return this;
11065     },
11066
11067         /**
11068          * Ensures that all effects queued after sequenceFx is called on the element are
11069          * run in sequence.  This is the opposite of {@link #syncFx}.
11070          * @return {Roo.Element} The Element
11071          */
11072     sequenceFx : function(){
11073         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11074             block : false,
11075             concurrent : false,
11076             stopFx : false
11077         });
11078         return this;
11079     },
11080
11081         /* @private */
11082     nextFx : function(){
11083         var ef = this.fxQueue[0];
11084         if(ef){
11085             ef.call(this);
11086         }
11087     },
11088
11089         /**
11090          * Returns true if the element has any effects actively running or queued, else returns false.
11091          * @return {Boolean} True if element has active effects, else false
11092          */
11093     hasActiveFx : function(){
11094         return this.fxQueue && this.fxQueue[0];
11095     },
11096
11097         /**
11098          * Stops any running effects and clears the element's internal effects queue if it contains
11099          * any additional effects that haven't started yet.
11100          * @return {Roo.Element} The Element
11101          */
11102     stopFx : function(){
11103         if(this.hasActiveFx()){
11104             var cur = this.fxQueue[0];
11105             if(cur && cur.anim && cur.anim.isAnimated()){
11106                 this.fxQueue = [cur]; // clear out others
11107                 cur.anim.stop(true);
11108             }
11109         }
11110         return this;
11111     },
11112
11113         /* @private */
11114     beforeFx : function(o){
11115         if(this.hasActiveFx() && !o.concurrent){
11116            if(o.stopFx){
11117                this.stopFx();
11118                return true;
11119            }
11120            return false;
11121         }
11122         return true;
11123     },
11124
11125         /**
11126          * Returns true if the element is currently blocking so that no other effect can be queued
11127          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11128          * used to ensure that an effect initiated by a user action runs to completion prior to the
11129          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11130          * @return {Boolean} True if blocking, else false
11131          */
11132     hasFxBlock : function(){
11133         var q = this.fxQueue;
11134         return q && q[0] && q[0].block;
11135     },
11136
11137         /* @private */
11138     queueFx : function(o, fn){
11139         if(!this.fxQueue){
11140             this.fxQueue = [];
11141         }
11142         if(!this.hasFxBlock()){
11143             Roo.applyIf(o, this.fxDefaults);
11144             if(!o.concurrent){
11145                 var run = this.beforeFx(o);
11146                 fn.block = o.block;
11147                 this.fxQueue.push(fn);
11148                 if(run){
11149                     this.nextFx();
11150                 }
11151             }else{
11152                 fn.call(this);
11153             }
11154         }
11155         return this;
11156     },
11157
11158         /* @private */
11159     fxWrap : function(pos, o, vis){
11160         var wrap;
11161         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11162             var wrapXY;
11163             if(o.fixPosition){
11164                 wrapXY = this.getXY();
11165             }
11166             var div = document.createElement("div");
11167             div.style.visibility = vis;
11168             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11169             wrap.setPositioning(pos);
11170             if(wrap.getStyle("position") == "static"){
11171                 wrap.position("relative");
11172             }
11173             this.clearPositioning('auto');
11174             wrap.clip();
11175             wrap.dom.appendChild(this.dom);
11176             if(wrapXY){
11177                 wrap.setXY(wrapXY);
11178             }
11179         }
11180         return wrap;
11181     },
11182
11183         /* @private */
11184     fxUnwrap : function(wrap, pos, o){
11185         this.clearPositioning();
11186         this.setPositioning(pos);
11187         if(!o.wrap){
11188             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11189             wrap.remove();
11190         }
11191     },
11192
11193         /* @private */
11194     getFxRestore : function(){
11195         var st = this.dom.style;
11196         return {pos: this.getPositioning(), width: st.width, height : st.height};
11197     },
11198
11199         /* @private */
11200     afterFx : function(o){
11201         if(o.afterStyle){
11202             this.applyStyles(o.afterStyle);
11203         }
11204         if(o.afterCls){
11205             this.addClass(o.afterCls);
11206         }
11207         if(o.remove === true){
11208             this.remove();
11209         }
11210         Roo.callback(o.callback, o.scope, [this]);
11211         if(!o.concurrent){
11212             this.fxQueue.shift();
11213             this.nextFx();
11214         }
11215     },
11216
11217         /* @private */
11218     getFxEl : function(){ // support for composite element fx
11219         return Roo.get(this.dom);
11220     },
11221
11222         /* @private */
11223     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11224         animType = animType || 'run';
11225         opt = opt || {};
11226         var anim = Roo.lib.Anim[animType](
11227             this.dom, args,
11228             (opt.duration || defaultDur) || .35,
11229             (opt.easing || defaultEase) || 'easeOut',
11230             function(){
11231                 Roo.callback(cb, this);
11232             },
11233             this
11234         );
11235         opt.anim = anim;
11236         return anim;
11237     }
11238 };
11239
11240 // backwords compat
11241 Roo.Fx.resize = Roo.Fx.scale;
11242
11243 //When included, Roo.Fx is automatically applied to Element so that all basic
11244 //effects are available directly via the Element API
11245 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11246  * Based on:
11247  * Ext JS Library 1.1.1
11248  * Copyright(c) 2006-2007, Ext JS, LLC.
11249  *
11250  * Originally Released Under LGPL - original licence link has changed is not relivant.
11251  *
11252  * Fork - LGPL
11253  * <script type="text/javascript">
11254  */
11255
11256
11257 /**
11258  * @class Roo.CompositeElement
11259  * Standard composite class. Creates a Roo.Element for every element in the collection.
11260  * <br><br>
11261  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11262  * actions will be performed on all the elements in this collection.</b>
11263  * <br><br>
11264  * All methods return <i>this</i> and can be chained.
11265  <pre><code>
11266  var els = Roo.select("#some-el div.some-class", true);
11267  // or select directly from an existing element
11268  var el = Roo.get('some-el');
11269  el.select('div.some-class', true);
11270
11271  els.setWidth(100); // all elements become 100 width
11272  els.hide(true); // all elements fade out and hide
11273  // or
11274  els.setWidth(100).hide(true);
11275  </code></pre>
11276  */
11277 Roo.CompositeElement = function(els){
11278     this.elements = [];
11279     this.addElements(els);
11280 };
11281 Roo.CompositeElement.prototype = {
11282     isComposite: true,
11283     addElements : function(els){
11284         if(!els) {
11285             return this;
11286         }
11287         if(typeof els == "string"){
11288             els = Roo.Element.selectorFunction(els);
11289         }
11290         var yels = this.elements;
11291         var index = yels.length-1;
11292         for(var i = 0, len = els.length; i < len; i++) {
11293                 yels[++index] = Roo.get(els[i]);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Clears this composite and adds the elements returned by the passed selector.
11300     * @param {String/Array} els A string CSS selector, an array of elements or an element
11301     * @return {CompositeElement} this
11302     */
11303     fill : function(els){
11304         this.elements = [];
11305         this.add(els);
11306         return this;
11307     },
11308
11309     /**
11310     * Filters this composite to only elements that match the passed selector.
11311     * @param {String} selector A string CSS selector
11312     * @param {Boolean} inverse return inverse filter (not matches)
11313     * @return {CompositeElement} this
11314     */
11315     filter : function(selector, inverse){
11316         var els = [];
11317         inverse = inverse || false;
11318         this.each(function(el){
11319             var match = inverse ? !el.is(selector) : el.is(selector);
11320             if(match){
11321                 els[els.length] = el.dom;
11322             }
11323         });
11324         this.fill(els);
11325         return this;
11326     },
11327
11328     invoke : function(fn, args){
11329         var els = this.elements;
11330         for(var i = 0, len = els.length; i < len; i++) {
11331                 Roo.Element.prototype[fn].apply(els[i], args);
11332         }
11333         return this;
11334     },
11335     /**
11336     * Adds elements to this composite.
11337     * @param {String/Array} els A string CSS selector, an array of elements or an element
11338     * @return {CompositeElement} this
11339     */
11340     add : function(els){
11341         if(typeof els == "string"){
11342             this.addElements(Roo.Element.selectorFunction(els));
11343         }else if(els.length !== undefined){
11344             this.addElements(els);
11345         }else{
11346             this.addElements([els]);
11347         }
11348         return this;
11349     },
11350     /**
11351     * Calls the passed function passing (el, this, index) for each element in this composite.
11352     * @param {Function} fn The function to call
11353     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11354     * @return {CompositeElement} this
11355     */
11356     each : function(fn, scope){
11357         var els = this.elements;
11358         for(var i = 0, len = els.length; i < len; i++){
11359             if(fn.call(scope || els[i], els[i], this, i) === false) {
11360                 break;
11361             }
11362         }
11363         return this;
11364     },
11365
11366     /**
11367      * Returns the Element object at the specified index
11368      * @param {Number} index
11369      * @return {Roo.Element}
11370      */
11371     item : function(index){
11372         return this.elements[index] || null;
11373     },
11374
11375     /**
11376      * Returns the first Element
11377      * @return {Roo.Element}
11378      */
11379     first : function(){
11380         return this.item(0);
11381     },
11382
11383     /**
11384      * Returns the last Element
11385      * @return {Roo.Element}
11386      */
11387     last : function(){
11388         return this.item(this.elements.length-1);
11389     },
11390
11391     /**
11392      * Returns the number of elements in this composite
11393      * @return Number
11394      */
11395     getCount : function(){
11396         return this.elements.length;
11397     },
11398
11399     /**
11400      * Returns true if this composite contains the passed element
11401      * @return Boolean
11402      */
11403     contains : function(el){
11404         return this.indexOf(el) !== -1;
11405     },
11406
11407     /**
11408      * Returns true if this composite contains the passed element
11409      * @return Boolean
11410      */
11411     indexOf : function(el){
11412         return this.elements.indexOf(Roo.get(el));
11413     },
11414
11415
11416     /**
11417     * Removes the specified element(s).
11418     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11419     * or an array of any of those.
11420     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11421     * @return {CompositeElement} this
11422     */
11423     removeElement : function(el, removeDom){
11424         if(el instanceof Array){
11425             for(var i = 0, len = el.length; i < len; i++){
11426                 this.removeElement(el[i]);
11427             }
11428             return this;
11429         }
11430         var index = typeof el == 'number' ? el : this.indexOf(el);
11431         if(index !== -1){
11432             if(removeDom){
11433                 var d = this.elements[index];
11434                 if(d.dom){
11435                     d.remove();
11436                 }else{
11437                     d.parentNode.removeChild(d);
11438                 }
11439             }
11440             this.elements.splice(index, 1);
11441         }
11442         return this;
11443     },
11444
11445     /**
11446     * Replaces the specified element with the passed element.
11447     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11448     * to replace.
11449     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11450     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11451     * @return {CompositeElement} this
11452     */
11453     replaceElement : function(el, replacement, domReplace){
11454         var index = typeof el == 'number' ? el : this.indexOf(el);
11455         if(index !== -1){
11456             if(domReplace){
11457                 this.elements[index].replaceWith(replacement);
11458             }else{
11459                 this.elements.splice(index, 1, Roo.get(replacement))
11460             }
11461         }
11462         return this;
11463     },
11464
11465     /**
11466      * Removes all elements.
11467      */
11468     clear : function(){
11469         this.elements = [];
11470     }
11471 };
11472 (function(){
11473     Roo.CompositeElement.createCall = function(proto, fnName){
11474         if(!proto[fnName]){
11475             proto[fnName] = function(){
11476                 return this.invoke(fnName, arguments);
11477             };
11478         }
11479     };
11480     for(var fnName in Roo.Element.prototype){
11481         if(typeof Roo.Element.prototype[fnName] == "function"){
11482             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11483         }
11484     };
11485 })();
11486 /*
11487  * Based on:
11488  * Ext JS Library 1.1.1
11489  * Copyright(c) 2006-2007, Ext JS, LLC.
11490  *
11491  * Originally Released Under LGPL - original licence link has changed is not relivant.
11492  *
11493  * Fork - LGPL
11494  * <script type="text/javascript">
11495  */
11496
11497 /**
11498  * @class Roo.CompositeElementLite
11499  * @extends Roo.CompositeElement
11500  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11501  <pre><code>
11502  var els = Roo.select("#some-el div.some-class");
11503  // or select directly from an existing element
11504  var el = Roo.get('some-el');
11505  el.select('div.some-class');
11506
11507  els.setWidth(100); // all elements become 100 width
11508  els.hide(true); // all elements fade out and hide
11509  // or
11510  els.setWidth(100).hide(true);
11511  </code></pre><br><br>
11512  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11513  * actions will be performed on all the elements in this collection.</b>
11514  */
11515 Roo.CompositeElementLite = function(els){
11516     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11517     this.el = new Roo.Element.Flyweight();
11518 };
11519 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11520     addElements : function(els){
11521         if(els){
11522             if(els instanceof Array){
11523                 this.elements = this.elements.concat(els);
11524             }else{
11525                 var yels = this.elements;
11526                 var index = yels.length-1;
11527                 for(var i = 0, len = els.length; i < len; i++) {
11528                     yels[++index] = els[i];
11529                 }
11530             }
11531         }
11532         return this;
11533     },
11534     invoke : function(fn, args){
11535         var els = this.elements;
11536         var el = this.el;
11537         for(var i = 0, len = els.length; i < len; i++) {
11538             el.dom = els[i];
11539                 Roo.Element.prototype[fn].apply(el, args);
11540         }
11541         return this;
11542     },
11543     /**
11544      * Returns a flyweight Element of the dom element object at the specified index
11545      * @param {Number} index
11546      * @return {Roo.Element}
11547      */
11548     item : function(index){
11549         if(!this.elements[index]){
11550             return null;
11551         }
11552         this.el.dom = this.elements[index];
11553         return this.el;
11554     },
11555
11556     // fixes scope with flyweight
11557     addListener : function(eventName, handler, scope, opt){
11558         var els = this.elements;
11559         for(var i = 0, len = els.length; i < len; i++) {
11560             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11561         }
11562         return this;
11563     },
11564
11565     /**
11566     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11567     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11568     * a reference to the dom node, use el.dom.</b>
11569     * @param {Function} fn The function to call
11570     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11571     * @return {CompositeElement} this
11572     */
11573     each : function(fn, scope){
11574         var els = this.elements;
11575         var el = this.el;
11576         for(var i = 0, len = els.length; i < len; i++){
11577             el.dom = els[i];
11578                 if(fn.call(scope || el, el, this, i) === false){
11579                 break;
11580             }
11581         }
11582         return this;
11583     },
11584
11585     indexOf : function(el){
11586         return this.elements.indexOf(Roo.getDom(el));
11587     },
11588
11589     replaceElement : function(el, replacement, domReplace){
11590         var index = typeof el == 'number' ? el : this.indexOf(el);
11591         if(index !== -1){
11592             replacement = Roo.getDom(replacement);
11593             if(domReplace){
11594                 var d = this.elements[index];
11595                 d.parentNode.insertBefore(replacement, d);
11596                 d.parentNode.removeChild(d);
11597             }
11598             this.elements.splice(index, 1, replacement);
11599         }
11600         return this;
11601     }
11602 });
11603 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11604
11605 /*
11606  * Based on:
11607  * Ext JS Library 1.1.1
11608  * Copyright(c) 2006-2007, Ext JS, LLC.
11609  *
11610  * Originally Released Under LGPL - original licence link has changed is not relivant.
11611  *
11612  * Fork - LGPL
11613  * <script type="text/javascript">
11614  */
11615
11616  
11617
11618 /**
11619  * @class Roo.data.Connection
11620  * @extends Roo.util.Observable
11621  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11622  * either to a configured URL, or to a URL specified at request time. 
11623  * 
11624  * Requests made by this class are asynchronous, and will return immediately. No data from
11625  * the server will be available to the statement immediately following the {@link #request} call.
11626  * To process returned data, use a callback in the request options object, or an event listener.
11627  * 
11628  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11629  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11630  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11631  * property and, if present, the IFRAME's XML document as the responseXML property.
11632  * 
11633  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11634  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11635  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11636  * standard DOM methods.
11637  * @constructor
11638  * @param {Object} config a configuration object.
11639  */
11640 Roo.data.Connection = function(config){
11641     Roo.apply(this, config);
11642     this.addEvents({
11643         /**
11644          * @event beforerequest
11645          * Fires before a network request is made to retrieve a data object.
11646          * @param {Connection} conn This Connection object.
11647          * @param {Object} options The options config object passed to the {@link #request} method.
11648          */
11649         "beforerequest" : true,
11650         /**
11651          * @event requestcomplete
11652          * Fires if the request was successfully completed.
11653          * @param {Connection} conn This Connection object.
11654          * @param {Object} response The XHR object containing the response data.
11655          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11656          * @param {Object} options The options config object passed to the {@link #request} method.
11657          */
11658         "requestcomplete" : true,
11659         /**
11660          * @event requestexception
11661          * Fires if an error HTTP status was returned from the server.
11662          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11663          * @param {Connection} conn This Connection object.
11664          * @param {Object} response The XHR object containing the response data.
11665          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11666          * @param {Object} options The options config object passed to the {@link #request} method.
11667          */
11668         "requestexception" : true
11669     });
11670     Roo.data.Connection.superclass.constructor.call(this);
11671 };
11672
11673 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11674     /**
11675      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11676      */
11677     /**
11678      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11679      * extra parameters to each request made by this object. (defaults to undefined)
11680      */
11681     /**
11682      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11683      *  to each request made by this object. (defaults to undefined)
11684      */
11685     /**
11686      * @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)
11687      */
11688     /**
11689      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11690      */
11691     timeout : 30000,
11692     /**
11693      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11694      * @type Boolean
11695      */
11696     autoAbort:false,
11697
11698     /**
11699      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11700      * @type Boolean
11701      */
11702     disableCaching: true,
11703
11704     /**
11705      * Sends an HTTP request to a remote server.
11706      * @param {Object} options An object which may contain the following properties:<ul>
11707      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11708      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11709      * request, a url encoded string or a function to call to get either.</li>
11710      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11711      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11712      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11713      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11714      * <li>options {Object} The parameter to the request call.</li>
11715      * <li>success {Boolean} True if the request succeeded.</li>
11716      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11717      * </ul></li>
11718      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11719      * The callback is passed the following parameters:<ul>
11720      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11721      * <li>options {Object} The parameter to the request call.</li>
11722      * </ul></li>
11723      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11724      * The callback is passed the following parameters:<ul>
11725      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11726      * <li>options {Object} The parameter to the request call.</li>
11727      * </ul></li>
11728      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11729      * for the callback function. Defaults to the browser window.</li>
11730      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11731      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11732      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11733      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11734      * params for the post data. Any params will be appended to the URL.</li>
11735      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11736      * </ul>
11737      * @return {Number} transactionId
11738      */
11739     request : function(o){
11740         if(this.fireEvent("beforerequest", this, o) !== false){
11741             var p = o.params;
11742
11743             if(typeof p == "function"){
11744                 p = p.call(o.scope||window, o);
11745             }
11746             if(typeof p == "object"){
11747                 p = Roo.urlEncode(o.params);
11748             }
11749             if(this.extraParams){
11750                 var extras = Roo.urlEncode(this.extraParams);
11751                 p = p ? (p + '&' + extras) : extras;
11752             }
11753
11754             var url = o.url || this.url;
11755             if(typeof url == 'function'){
11756                 url = url.call(o.scope||window, o);
11757             }
11758
11759             if(o.form){
11760                 var form = Roo.getDom(o.form);
11761                 url = url || form.action;
11762
11763                 var enctype = form.getAttribute("enctype");
11764                 
11765                 if (o.formData) {
11766                     return this.doFormDataUpload(o, url);
11767                 }
11768                 
11769                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11770                     return this.doFormUpload(o, p, url);
11771                 }
11772                 var f = Roo.lib.Ajax.serializeForm(form);
11773                 p = p ? (p + '&' + f) : f;
11774             }
11775             
11776             if (!o.form && o.formData) {
11777                 o.formData = o.formData === true ? new FormData() : o.formData;
11778                 for (var k in o.params) {
11779                     o.formData.append(k,o.params[k]);
11780                 }
11781                     
11782                 return this.doFormDataUpload(o, url);
11783             }
11784             
11785
11786             var hs = o.headers;
11787             if(this.defaultHeaders){
11788                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11789                 if(!o.headers){
11790                     o.headers = hs;
11791                 }
11792             }
11793
11794             var cb = {
11795                 success: this.handleResponse,
11796                 failure: this.handleFailure,
11797                 scope: this,
11798                 argument: {options: o},
11799                 timeout : o.timeout || this.timeout
11800             };
11801
11802             var method = o.method||this.method||(p ? "POST" : "GET");
11803
11804             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11805                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11806             }
11807
11808             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11809                 if(o.autoAbort){
11810                     this.abort();
11811                 }
11812             }else if(this.autoAbort !== false){
11813                 this.abort();
11814             }
11815
11816             if((method == 'GET' && p) || o.xmlData){
11817                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11818                 p = '';
11819             }
11820             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11821             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11822             Roo.lib.Ajax.useDefaultHeader == true;
11823             return this.transId;
11824         }else{
11825             Roo.callback(o.callback, o.scope, [o, null, null]);
11826             return null;
11827         }
11828     },
11829
11830     /**
11831      * Determine whether this object has a request outstanding.
11832      * @param {Number} transactionId (Optional) defaults to the last transaction
11833      * @return {Boolean} True if there is an outstanding request.
11834      */
11835     isLoading : function(transId){
11836         if(transId){
11837             return Roo.lib.Ajax.isCallInProgress(transId);
11838         }else{
11839             return this.transId ? true : false;
11840         }
11841     },
11842
11843     /**
11844      * Aborts any outstanding request.
11845      * @param {Number} transactionId (Optional) defaults to the last transaction
11846      */
11847     abort : function(transId){
11848         if(transId || this.isLoading()){
11849             Roo.lib.Ajax.abort(transId || this.transId);
11850         }
11851     },
11852
11853     // private
11854     handleResponse : function(response){
11855         this.transId = false;
11856         var options = response.argument.options;
11857         response.argument = options ? options.argument : null;
11858         this.fireEvent("requestcomplete", this, response, options);
11859         Roo.callback(options.success, options.scope, [response, options]);
11860         Roo.callback(options.callback, options.scope, [options, true, response]);
11861     },
11862
11863     // private
11864     handleFailure : function(response, e){
11865         this.transId = false;
11866         var options = response.argument.options;
11867         response.argument = options ? options.argument : null;
11868         this.fireEvent("requestexception", this, response, options, e);
11869         Roo.callback(options.failure, options.scope, [response, options]);
11870         Roo.callback(options.callback, options.scope, [options, false, response]);
11871     },
11872
11873     // private
11874     doFormUpload : function(o, ps, url){
11875         var id = Roo.id();
11876         var frame = document.createElement('iframe');
11877         frame.id = id;
11878         frame.name = id;
11879         frame.className = 'x-hidden';
11880         if(Roo.isIE){
11881             frame.src = Roo.SSL_SECURE_URL;
11882         }
11883         document.body.appendChild(frame);
11884
11885         if(Roo.isIE){
11886            document.frames[id].name = id;
11887         }
11888
11889         var form = Roo.getDom(o.form);
11890         form.target = id;
11891         form.method = 'POST';
11892         form.enctype = form.encoding = 'multipart/form-data';
11893         if(url){
11894             form.action = url;
11895         }
11896
11897         var hiddens, hd;
11898         if(ps){ // add dynamic params
11899             hiddens = [];
11900             ps = Roo.urlDecode(ps, false);
11901             for(var k in ps){
11902                 if(ps.hasOwnProperty(k)){
11903                     hd = document.createElement('input');
11904                     hd.type = 'hidden';
11905                     hd.name = k;
11906                     hd.value = ps[k];
11907                     form.appendChild(hd);
11908                     hiddens.push(hd);
11909                 }
11910             }
11911         }
11912
11913         function cb(){
11914             var r = {  // bogus response object
11915                 responseText : '',
11916                 responseXML : null
11917             };
11918
11919             r.argument = o ? o.argument : null;
11920
11921             try { //
11922                 var doc;
11923                 if(Roo.isIE){
11924                     doc = frame.contentWindow.document;
11925                 }else {
11926                     doc = (frame.contentDocument || window.frames[id].document);
11927                 }
11928                 if(doc && doc.body){
11929                     r.responseText = doc.body.innerHTML;
11930                 }
11931                 if(doc && doc.XMLDocument){
11932                     r.responseXML = doc.XMLDocument;
11933                 }else {
11934                     r.responseXML = doc;
11935                 }
11936             }
11937             catch(e) {
11938                 // ignore
11939             }
11940
11941             Roo.EventManager.removeListener(frame, 'load', cb, this);
11942
11943             this.fireEvent("requestcomplete", this, r, o);
11944             Roo.callback(o.success, o.scope, [r, o]);
11945             Roo.callback(o.callback, o.scope, [o, true, r]);
11946
11947             setTimeout(function(){document.body.removeChild(frame);}, 100);
11948         }
11949
11950         Roo.EventManager.on(frame, 'load', cb, this);
11951         form.submit();
11952
11953         if(hiddens){ // remove dynamic params
11954             for(var i = 0, len = hiddens.length; i < len; i++){
11955                 form.removeChild(hiddens[i]);
11956             }
11957         }
11958     },
11959     // this is a 'formdata version???'
11960     
11961     
11962     doFormDataUpload : function(o,  url)
11963     {
11964         var formData;
11965         if (o.form) {
11966             var form =  Roo.getDom(o.form);
11967             form.enctype = form.encoding = 'multipart/form-data';
11968             formData = o.formData === true ? new FormData(form) : o.formData;
11969         } else {
11970             formData = o.formData === true ? new FormData() : o.formData;
11971         }
11972         
11973       
11974         var cb = {
11975             success: this.handleResponse,
11976             failure: this.handleFailure,
11977             scope: this,
11978             argument: {options: o},
11979             timeout : o.timeout || this.timeout
11980         };
11981  
11982         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11983             if(o.autoAbort){
11984                 this.abort();
11985             }
11986         }else if(this.autoAbort !== false){
11987             this.abort();
11988         }
11989
11990         //Roo.lib.Ajax.defaultPostHeader = null;
11991         Roo.lib.Ajax.useDefaultHeader = false;
11992         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11993         Roo.lib.Ajax.useDefaultHeader = true;
11994  
11995          
11996     }
11997     
11998 });
11999 /*
12000  * Based on:
12001  * Ext JS Library 1.1.1
12002  * Copyright(c) 2006-2007, Ext JS, LLC.
12003  *
12004  * Originally Released Under LGPL - original licence link has changed is not relivant.
12005  *
12006  * Fork - LGPL
12007  * <script type="text/javascript">
12008  */
12009  
12010 /**
12011  * Global Ajax request class.
12012  * 
12013  * @class Roo.Ajax
12014  * @extends Roo.data.Connection
12015  * @static
12016  * 
12017  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12018  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12019  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12020  * @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)
12021  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12022  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12023  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12024  */
12025 Roo.Ajax = new Roo.data.Connection({
12026     // fix up the docs
12027     /**
12028      * @scope Roo.Ajax
12029      * @type {Boolear} 
12030      */
12031     autoAbort : false,
12032
12033     /**
12034      * Serialize the passed form into a url encoded string
12035      * @scope Roo.Ajax
12036      * @param {String/HTMLElement} form
12037      * @return {String}
12038      */
12039     serializeForm : function(form){
12040         return Roo.lib.Ajax.serializeForm(form);
12041     }
12042 });/*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052
12053  
12054 /**
12055  * @class Roo.UpdateManager
12056  * @extends Roo.util.Observable
12057  * Provides AJAX-style update for Element object.<br><br>
12058  * Usage:<br>
12059  * <pre><code>
12060  * // Get it from a Roo.Element object
12061  * var el = Roo.get("foo");
12062  * var mgr = el.getUpdateManager();
12063  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12064  * ...
12065  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12066  * <br>
12067  * // or directly (returns the same UpdateManager instance)
12068  * var mgr = new Roo.UpdateManager("myElementId");
12069  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12070  * mgr.on("update", myFcnNeedsToKnow);
12071  * <br>
12072    // short handed call directly from the element object
12073    Roo.get("foo").load({
12074         url: "bar.php",
12075         scripts:true,
12076         params: "for=bar",
12077         text: "Loading Foo..."
12078    });
12079  * </code></pre>
12080  * @constructor
12081  * Create new UpdateManager directly.
12082  * @param {String/HTMLElement/Roo.Element} el The element to update
12083  * @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).
12084  */
12085 Roo.UpdateManager = function(el, forceNew){
12086     el = Roo.get(el);
12087     if(!forceNew && el.updateManager){
12088         return el.updateManager;
12089     }
12090     /**
12091      * The Element object
12092      * @type Roo.Element
12093      */
12094     this.el = el;
12095     /**
12096      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12097      * @type String
12098      */
12099     this.defaultUrl = null;
12100
12101     this.addEvents({
12102         /**
12103          * @event beforeupdate
12104          * Fired before an update is made, return false from your handler and the update is cancelled.
12105          * @param {Roo.Element} el
12106          * @param {String/Object/Function} url
12107          * @param {String/Object} params
12108          */
12109         "beforeupdate": true,
12110         /**
12111          * @event update
12112          * Fired after successful update is made.
12113          * @param {Roo.Element} el
12114          * @param {Object} oResponseObject The response Object
12115          */
12116         "update": true,
12117         /**
12118          * @event failure
12119          * Fired on update failure.
12120          * @param {Roo.Element} el
12121          * @param {Object} oResponseObject The response Object
12122          */
12123         "failure": true
12124     });
12125     var d = Roo.UpdateManager.defaults;
12126     /**
12127      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12128      * @type String
12129      */
12130     this.sslBlankUrl = d.sslBlankUrl;
12131     /**
12132      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12133      * @type Boolean
12134      */
12135     this.disableCaching = d.disableCaching;
12136     /**
12137      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12138      * @type String
12139      */
12140     this.indicatorText = d.indicatorText;
12141     /**
12142      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12143      * @type String
12144      */
12145     this.showLoadIndicator = d.showLoadIndicator;
12146     /**
12147      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12148      * @type Number
12149      */
12150     this.timeout = d.timeout;
12151
12152     /**
12153      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12154      * @type Boolean
12155      */
12156     this.loadScripts = d.loadScripts;
12157
12158     /**
12159      * Transaction object of current executing transaction
12160      */
12161     this.transaction = null;
12162
12163     /**
12164      * @private
12165      */
12166     this.autoRefreshProcId = null;
12167     /**
12168      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12169      * @type Function
12170      */
12171     this.refreshDelegate = this.refresh.createDelegate(this);
12172     /**
12173      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12174      * @type Function
12175      */
12176     this.updateDelegate = this.update.createDelegate(this);
12177     /**
12178      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12179      * @type Function
12180      */
12181     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12182     /**
12183      * @private
12184      */
12185     this.successDelegate = this.processSuccess.createDelegate(this);
12186     /**
12187      * @private
12188      */
12189     this.failureDelegate = this.processFailure.createDelegate(this);
12190
12191     if(!this.renderer){
12192      /**
12193       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12194       */
12195     this.renderer = new Roo.UpdateManager.BasicRenderer();
12196     }
12197     
12198     Roo.UpdateManager.superclass.constructor.call(this);
12199 };
12200
12201 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12202     /**
12203      * Get the Element this UpdateManager is bound to
12204      * @return {Roo.Element} The element
12205      */
12206     getEl : function(){
12207         return this.el;
12208     },
12209     /**
12210      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12211      * @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:
12212 <pre><code>
12213 um.update({<br/>
12214     url: "your-url.php",<br/>
12215     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12216     callback: yourFunction,<br/>
12217     scope: yourObject, //(optional scope)  <br/>
12218     discardUrl: false, <br/>
12219     nocache: false,<br/>
12220     text: "Loading...",<br/>
12221     timeout: 30,<br/>
12222     scripts: false<br/>
12223 });
12224 </code></pre>
12225      * The only required property is url. The optional properties nocache, text and scripts
12226      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12227      * @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}
12228      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12229      * @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.
12230      */
12231     update : function(url, params, callback, discardUrl){
12232         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12233             var method = this.method,
12234                 cfg;
12235             if(typeof url == "object"){ // must be config object
12236                 cfg = url;
12237                 url = cfg.url;
12238                 params = params || cfg.params;
12239                 callback = callback || cfg.callback;
12240                 discardUrl = discardUrl || cfg.discardUrl;
12241                 if(callback && cfg.scope){
12242                     callback = callback.createDelegate(cfg.scope);
12243                 }
12244                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12245                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12246                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12247                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12248                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12249             }
12250             this.showLoading();
12251             if(!discardUrl){
12252                 this.defaultUrl = url;
12253             }
12254             if(typeof url == "function"){
12255                 url = url.call(this);
12256             }
12257
12258             method = method || (params ? "POST" : "GET");
12259             if(method == "GET"){
12260                 url = this.prepareUrl(url);
12261             }
12262
12263             var o = Roo.apply(cfg ||{}, {
12264                 url : url,
12265                 params: params,
12266                 success: this.successDelegate,
12267                 failure: this.failureDelegate,
12268                 callback: undefined,
12269                 timeout: (this.timeout*1000),
12270                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12271             });
12272             Roo.log("updated manager called with timeout of " + o.timeout);
12273             this.transaction = Roo.Ajax.request(o);
12274         }
12275     },
12276
12277     /**
12278      * 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.
12279      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12280      * @param {String/HTMLElement} form The form Id or form element
12281      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12282      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12284      */
12285     formUpdate : function(form, url, reset, callback){
12286         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12287             if(typeof url == "function"){
12288                 url = url.call(this);
12289             }
12290             form = Roo.getDom(form);
12291             this.transaction = Roo.Ajax.request({
12292                 form: form,
12293                 url:url,
12294                 success: this.successDelegate,
12295                 failure: this.failureDelegate,
12296                 timeout: (this.timeout*1000),
12297                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12298             });
12299             this.showLoading.defer(1, this);
12300         }
12301     },
12302
12303     /**
12304      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12305      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12306      */
12307     refresh : function(callback){
12308         if(this.defaultUrl == null){
12309             return;
12310         }
12311         this.update(this.defaultUrl, null, callback, true);
12312     },
12313
12314     /**
12315      * Set this element to auto refresh.
12316      * @param {Number} interval How often to update (in seconds).
12317      * @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)
12318      * @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}
12319      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12320      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12321      */
12322     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12323         if(refreshNow){
12324             this.update(url || this.defaultUrl, params, callback, true);
12325         }
12326         if(this.autoRefreshProcId){
12327             clearInterval(this.autoRefreshProcId);
12328         }
12329         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12330     },
12331
12332     /**
12333      * Stop auto refresh on this element.
12334      */
12335      stopAutoRefresh : function(){
12336         if(this.autoRefreshProcId){
12337             clearInterval(this.autoRefreshProcId);
12338             delete this.autoRefreshProcId;
12339         }
12340     },
12341
12342     isAutoRefreshing : function(){
12343        return this.autoRefreshProcId ? true : false;
12344     },
12345     /**
12346      * Called to update the element to "Loading" state. Override to perform custom action.
12347      */
12348     showLoading : function(){
12349         if(this.showLoadIndicator){
12350             this.el.update(this.indicatorText);
12351         }
12352     },
12353
12354     /**
12355      * Adds unique parameter to query string if disableCaching = true
12356      * @private
12357      */
12358     prepareUrl : function(url){
12359         if(this.disableCaching){
12360             var append = "_dc=" + (new Date().getTime());
12361             if(url.indexOf("?") !== -1){
12362                 url += "&" + append;
12363             }else{
12364                 url += "?" + append;
12365             }
12366         }
12367         return url;
12368     },
12369
12370     /**
12371      * @private
12372      */
12373     processSuccess : function(response){
12374         this.transaction = null;
12375         if(response.argument.form && response.argument.reset){
12376             try{ // put in try/catch since some older FF releases had problems with this
12377                 response.argument.form.reset();
12378             }catch(e){}
12379         }
12380         if(this.loadScripts){
12381             this.renderer.render(this.el, response, this,
12382                 this.updateComplete.createDelegate(this, [response]));
12383         }else{
12384             this.renderer.render(this.el, response, this);
12385             this.updateComplete(response);
12386         }
12387     },
12388
12389     updateComplete : function(response){
12390         this.fireEvent("update", this.el, response);
12391         if(typeof response.argument.callback == "function"){
12392             response.argument.callback(this.el, true, response);
12393         }
12394     },
12395
12396     /**
12397      * @private
12398      */
12399     processFailure : function(response){
12400         this.transaction = null;
12401         this.fireEvent("failure", this.el, response);
12402         if(typeof response.argument.callback == "function"){
12403             response.argument.callback(this.el, false, response);
12404         }
12405     },
12406
12407     /**
12408      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12409      * @param {Object} renderer The object implementing the render() method
12410      */
12411     setRenderer : function(renderer){
12412         this.renderer = renderer;
12413     },
12414
12415     getRenderer : function(){
12416        return this.renderer;
12417     },
12418
12419     /**
12420      * Set the defaultUrl used for updates
12421      * @param {String/Function} defaultUrl The url or a function to call to get the url
12422      */
12423     setDefaultUrl : function(defaultUrl){
12424         this.defaultUrl = defaultUrl;
12425     },
12426
12427     /**
12428      * Aborts the executing transaction
12429      */
12430     abort : function(){
12431         if(this.transaction){
12432             Roo.Ajax.abort(this.transaction);
12433         }
12434     },
12435
12436     /**
12437      * Returns true if an update is in progress
12438      * @return {Boolean}
12439      */
12440     isUpdating : function(){
12441         if(this.transaction){
12442             return Roo.Ajax.isLoading(this.transaction);
12443         }
12444         return false;
12445     }
12446 });
12447
12448 /**
12449  * @class Roo.UpdateManager.defaults
12450  * @static (not really - but it helps the doc tool)
12451  * The defaults collection enables customizing the default properties of UpdateManager
12452  */
12453    Roo.UpdateManager.defaults = {
12454        /**
12455          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12456          * @type Number
12457          */
12458          timeout : 30,
12459
12460          /**
12461          * True to process scripts by default (Defaults to false).
12462          * @type Boolean
12463          */
12464         loadScripts : false,
12465
12466         /**
12467         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12468         * @type String
12469         */
12470         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12471         /**
12472          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12473          * @type Boolean
12474          */
12475         disableCaching : false,
12476         /**
12477          * Whether to show indicatorText when loading (Defaults to true).
12478          * @type Boolean
12479          */
12480         showLoadIndicator : true,
12481         /**
12482          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12483          * @type String
12484          */
12485         indicatorText : '<div class="loading-indicator">Loading...</div>'
12486    };
12487
12488 /**
12489  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12490  *Usage:
12491  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12492  * @param {String/HTMLElement/Roo.Element} el The element to update
12493  * @param {String} url The url
12494  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12495  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12496  * @static
12497  * @deprecated
12498  * @member Roo.UpdateManager
12499  */
12500 Roo.UpdateManager.updateElement = function(el, url, params, options){
12501     var um = Roo.get(el, true).getUpdateManager();
12502     Roo.apply(um, options);
12503     um.update(url, params, options ? options.callback : null);
12504 };
12505 // alias for backwards compat
12506 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12507 /**
12508  * @class Roo.UpdateManager.BasicRenderer
12509  * Default Content renderer. Updates the elements innerHTML with the responseText.
12510  */
12511 Roo.UpdateManager.BasicRenderer = function(){};
12512
12513 Roo.UpdateManager.BasicRenderer.prototype = {
12514     /**
12515      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12516      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12517      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12518      * @param {Roo.Element} el The element being rendered
12519      * @param {Object} response The YUI Connect response object
12520      * @param {UpdateManager} updateManager The calling update manager
12521      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12522      */
12523      render : function(el, response, updateManager, callback){
12524         el.update(response.responseText, updateManager.loadScripts, callback);
12525     }
12526 };
12527 /*
12528  * Based on:
12529  * Roo JS
12530  * (c)) Alan Knowles
12531  * Licence : LGPL
12532  */
12533
12534
12535 /**
12536  * @class Roo.DomTemplate
12537  * @extends Roo.Template
12538  * An effort at a dom based template engine..
12539  *
12540  * Similar to XTemplate, except it uses dom parsing to create the template..
12541  *
12542  * Supported features:
12543  *
12544  *  Tags:
12545
12546 <pre><code>
12547       {a_variable} - output encoded.
12548       {a_variable.format:("Y-m-d")} - call a method on the variable
12549       {a_variable:raw} - unencoded output
12550       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12551       {a_variable:this.method_on_template(...)} - call a method on the template object.
12552  
12553 </code></pre>
12554  *  The tpl tag:
12555 <pre><code>
12556         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12557         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12558         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12559         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12560   
12561 </code></pre>
12562  *      
12563  */
12564 Roo.DomTemplate = function()
12565 {
12566      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12567      if (this.html) {
12568         this.compile();
12569      }
12570 };
12571
12572
12573 Roo.extend(Roo.DomTemplate, Roo.Template, {
12574     /**
12575      * id counter for sub templates.
12576      */
12577     id : 0,
12578     /**
12579      * flag to indicate if dom parser is inside a pre,
12580      * it will strip whitespace if not.
12581      */
12582     inPre : false,
12583     
12584     /**
12585      * The various sub templates
12586      */
12587     tpls : false,
12588     
12589     
12590     
12591     /**
12592      *
12593      * basic tag replacing syntax
12594      * WORD:WORD()
12595      *
12596      * // you can fake an object call by doing this
12597      *  x.t:(test,tesT) 
12598      * 
12599      */
12600     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12601     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12602     
12603     iterChild : function (node, method) {
12604         
12605         var oldPre = this.inPre;
12606         if (node.tagName == 'PRE') {
12607             this.inPre = true;
12608         }
12609         for( var i = 0; i < node.childNodes.length; i++) {
12610             method.call(this, node.childNodes[i]);
12611         }
12612         this.inPre = oldPre;
12613     },
12614     
12615     
12616     
12617     /**
12618      * compile the template
12619      *
12620      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12621      *
12622      */
12623     compile: function()
12624     {
12625         var s = this.html;
12626         
12627         // covert the html into DOM...
12628         var doc = false;
12629         var div =false;
12630         try {
12631             doc = document.implementation.createHTMLDocument("");
12632             doc.documentElement.innerHTML =   this.html  ;
12633             div = doc.documentElement;
12634         } catch (e) {
12635             // old IE... - nasty -- it causes all sorts of issues.. with
12636             // images getting pulled from server..
12637             div = document.createElement('div');
12638             div.innerHTML = this.html;
12639         }
12640         //doc.documentElement.innerHTML = htmlBody
12641          
12642         
12643         
12644         this.tpls = [];
12645         var _t = this;
12646         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12647         
12648         var tpls = this.tpls;
12649         
12650         // create a top level template from the snippet..
12651         
12652         //Roo.log(div.innerHTML);
12653         
12654         var tpl = {
12655             uid : 'master',
12656             id : this.id++,
12657             attr : false,
12658             value : false,
12659             body : div.innerHTML,
12660             
12661             forCall : false,
12662             execCall : false,
12663             dom : div,
12664             isTop : true
12665             
12666         };
12667         tpls.unshift(tpl);
12668         
12669         
12670         // compile them...
12671         this.tpls = [];
12672         Roo.each(tpls, function(tp){
12673             this.compileTpl(tp);
12674             this.tpls[tp.id] = tp;
12675         }, this);
12676         
12677         this.master = tpls[0];
12678         return this;
12679         
12680         
12681     },
12682     
12683     compileNode : function(node, istop) {
12684         // test for
12685         //Roo.log(node);
12686         
12687         
12688         // skip anything not a tag..
12689         if (node.nodeType != 1) {
12690             if (node.nodeType == 3 && !this.inPre) {
12691                 // reduce white space..
12692                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12693                 
12694             }
12695             return;
12696         }
12697         
12698         var tpl = {
12699             uid : false,
12700             id : false,
12701             attr : false,
12702             value : false,
12703             body : '',
12704             
12705             forCall : false,
12706             execCall : false,
12707             dom : false,
12708             isTop : istop
12709             
12710             
12711         };
12712         
12713         
12714         switch(true) {
12715             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12716             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12717             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12718             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12719             // no default..
12720         }
12721         
12722         
12723         if (!tpl.attr) {
12724             // just itterate children..
12725             this.iterChild(node,this.compileNode);
12726             return;
12727         }
12728         tpl.uid = this.id++;
12729         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12730         node.removeAttribute('roo-'+ tpl.attr);
12731         if (tpl.attr != 'name') {
12732             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12733             node.parentNode.replaceChild(placeholder,  node);
12734         } else {
12735             
12736             var placeholder =  document.createElement('span');
12737             placeholder.className = 'roo-tpl-' + tpl.value;
12738             node.parentNode.replaceChild(placeholder,  node);
12739         }
12740         
12741         // parent now sees '{domtplXXXX}
12742         this.iterChild(node,this.compileNode);
12743         
12744         // we should now have node body...
12745         var div = document.createElement('div');
12746         div.appendChild(node);
12747         tpl.dom = node;
12748         // this has the unfortunate side effect of converting tagged attributes
12749         // eg. href="{...}" into %7C...%7D
12750         // this has been fixed by searching for those combo's although it's a bit hacky..
12751         
12752         
12753         tpl.body = div.innerHTML;
12754         
12755         
12756          
12757         tpl.id = tpl.uid;
12758         switch(tpl.attr) {
12759             case 'for' :
12760                 switch (tpl.value) {
12761                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12762                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12763                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12764                 }
12765                 break;
12766             
12767             case 'exec':
12768                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12769                 break;
12770             
12771             case 'if':     
12772                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12773                 break;
12774             
12775             case 'name':
12776                 tpl.id  = tpl.value; // replace non characters???
12777                 break;
12778             
12779         }
12780         
12781         
12782         this.tpls.push(tpl);
12783         
12784         
12785         
12786     },
12787     
12788     
12789     
12790     
12791     /**
12792      * Compile a segment of the template into a 'sub-template'
12793      *
12794      * 
12795      * 
12796      *
12797      */
12798     compileTpl : function(tpl)
12799     {
12800         var fm = Roo.util.Format;
12801         var useF = this.disableFormats !== true;
12802         
12803         var sep = Roo.isGecko ? "+\n" : ",\n";
12804         
12805         var undef = function(str) {
12806             Roo.debug && Roo.log("Property not found :"  + str);
12807             return '';
12808         };
12809           
12810         //Roo.log(tpl.body);
12811         
12812         
12813         
12814         var fn = function(m, lbrace, name, format, args)
12815         {
12816             //Roo.log("ARGS");
12817             //Roo.log(arguments);
12818             args = args ? args.replace(/\\'/g,"'") : args;
12819             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12820             if (typeof(format) == 'undefined') {
12821                 format =  'htmlEncode'; 
12822             }
12823             if (format == 'raw' ) {
12824                 format = false;
12825             }
12826             
12827             if(name.substr(0, 6) == 'domtpl'){
12828                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12829             }
12830             
12831             // build an array of options to determine if value is undefined..
12832             
12833             // basically get 'xxxx.yyyy' then do
12834             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12835             //    (function () { Roo.log("Property not found"); return ''; })() :
12836             //    ......
12837             
12838             var udef_ar = [];
12839             var lookfor = '';
12840             Roo.each(name.split('.'), function(st) {
12841                 lookfor += (lookfor.length ? '.': '') + st;
12842                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12843             });
12844             
12845             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12846             
12847             
12848             if(format && useF){
12849                 
12850                 args = args ? ',' + args : "";
12851                  
12852                 if(format.substr(0, 5) != "this."){
12853                     format = "fm." + format + '(';
12854                 }else{
12855                     format = 'this.call("'+ format.substr(5) + '", ';
12856                     args = ", values";
12857                 }
12858                 
12859                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12860             }
12861              
12862             if (args && args.length) {
12863                 // called with xxyx.yuu:(test,test)
12864                 // change to ()
12865                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12866             }
12867             // raw.. - :raw modifier..
12868             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12869             
12870         };
12871         var body;
12872         // branched to use + in gecko and [].join() in others
12873         if(Roo.isGecko){
12874             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12875                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12876                     "';};};";
12877         }else{
12878             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12879             body.push(tpl.body.replace(/(\r\n|\n)/g,
12880                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12881             body.push("'].join('');};};");
12882             body = body.join('');
12883         }
12884         
12885         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12886        
12887         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12888         eval(body);
12889         
12890         return this;
12891     },
12892      
12893     /**
12894      * same as applyTemplate, except it's done to one of the subTemplates
12895      * when using named templates, you can do:
12896      *
12897      * var str = pl.applySubTemplate('your-name', values);
12898      *
12899      * 
12900      * @param {Number} id of the template
12901      * @param {Object} values to apply to template
12902      * @param {Object} parent (normaly the instance of this object)
12903      */
12904     applySubTemplate : function(id, values, parent)
12905     {
12906         
12907         
12908         var t = this.tpls[id];
12909         
12910         
12911         try { 
12912             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12913                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12914                 return '';
12915             }
12916         } catch(e) {
12917             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12918             Roo.log(values);
12919           
12920             return '';
12921         }
12922         try { 
12923             
12924             if(t.execCall && t.execCall.call(this, values, parent)){
12925                 return '';
12926             }
12927         } catch(e) {
12928             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12929             Roo.log(values);
12930             return '';
12931         }
12932         
12933         try {
12934             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12935             parent = t.target ? values : parent;
12936             if(t.forCall && vs instanceof Array){
12937                 var buf = [];
12938                 for(var i = 0, len = vs.length; i < len; i++){
12939                     try {
12940                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12941                     } catch (e) {
12942                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12943                         Roo.log(e.body);
12944                         //Roo.log(t.compiled);
12945                         Roo.log(vs[i]);
12946                     }   
12947                 }
12948                 return buf.join('');
12949             }
12950         } catch (e) {
12951             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12952             Roo.log(values);
12953             return '';
12954         }
12955         try {
12956             return t.compiled.call(this, vs, parent);
12957         } catch (e) {
12958             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12959             Roo.log(e.body);
12960             //Roo.log(t.compiled);
12961             Roo.log(values);
12962             return '';
12963         }
12964     },
12965
12966    
12967
12968     applyTemplate : function(values){
12969         return this.master.compiled.call(this, values, {});
12970         //var s = this.subs;
12971     },
12972
12973     apply : function(){
12974         return this.applyTemplate.apply(this, arguments);
12975     }
12976
12977  });
12978
12979 Roo.DomTemplate.from = function(el){
12980     el = Roo.getDom(el);
12981     return new Roo.Domtemplate(el.value || el.innerHTML);
12982 };/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993 /**
12994  * @class Roo.util.DelayedTask
12995  * Provides a convenient method of performing setTimeout where a new
12996  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12997  * You can use this class to buffer
12998  * the keypress events for a certain number of milliseconds, and perform only if they stop
12999  * for that amount of time.
13000  * @constructor The parameters to this constructor serve as defaults and are not required.
13001  * @param {Function} fn (optional) The default function to timeout
13002  * @param {Object} scope (optional) The default scope of that timeout
13003  * @param {Array} args (optional) The default Array of arguments
13004  */
13005 Roo.util.DelayedTask = function(fn, scope, args){
13006     var id = null, d, t;
13007
13008     var call = function(){
13009         var now = new Date().getTime();
13010         if(now - t >= d){
13011             clearInterval(id);
13012             id = null;
13013             fn.apply(scope, args || []);
13014         }
13015     };
13016     /**
13017      * Cancels any pending timeout and queues a new one
13018      * @param {Number} delay The milliseconds to delay
13019      * @param {Function} newFn (optional) Overrides function passed to constructor
13020      * @param {Object} newScope (optional) Overrides scope passed to constructor
13021      * @param {Array} newArgs (optional) Overrides args passed to constructor
13022      */
13023     this.delay = function(delay, newFn, newScope, newArgs){
13024         if(id && delay != d){
13025             this.cancel();
13026         }
13027         d = delay;
13028         t = new Date().getTime();
13029         fn = newFn || fn;
13030         scope = newScope || scope;
13031         args = newArgs || args;
13032         if(!id){
13033             id = setInterval(call, d);
13034         }
13035     };
13036
13037     /**
13038      * Cancel the last queued timeout
13039      */
13040     this.cancel = function(){
13041         if(id){
13042             clearInterval(id);
13043             id = null;
13044         }
13045     };
13046 };/*
13047  * Based on:
13048  * Ext JS Library 1.1.1
13049  * Copyright(c) 2006-2007, Ext JS, LLC.
13050  *
13051  * Originally Released Under LGPL - original licence link has changed is not relivant.
13052  *
13053  * Fork - LGPL
13054  * <script type="text/javascript">
13055  */
13056  
13057  
13058 Roo.util.TaskRunner = function(interval){
13059     interval = interval || 10;
13060     var tasks = [], removeQueue = [];
13061     var id = 0;
13062     var running = false;
13063
13064     var stopThread = function(){
13065         running = false;
13066         clearInterval(id);
13067         id = 0;
13068     };
13069
13070     var startThread = function(){
13071         if(!running){
13072             running = true;
13073             id = setInterval(runTasks, interval);
13074         }
13075     };
13076
13077     var removeTask = function(task){
13078         removeQueue.push(task);
13079         if(task.onStop){
13080             task.onStop();
13081         }
13082     };
13083
13084     var runTasks = function(){
13085         if(removeQueue.length > 0){
13086             for(var i = 0, len = removeQueue.length; i < len; i++){
13087                 tasks.remove(removeQueue[i]);
13088             }
13089             removeQueue = [];
13090             if(tasks.length < 1){
13091                 stopThread();
13092                 return;
13093             }
13094         }
13095         var now = new Date().getTime();
13096         for(var i = 0, len = tasks.length; i < len; ++i){
13097             var t = tasks[i];
13098             var itime = now - t.taskRunTime;
13099             if(t.interval <= itime){
13100                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13101                 t.taskRunTime = now;
13102                 if(rt === false || t.taskRunCount === t.repeat){
13103                     removeTask(t);
13104                     return;
13105                 }
13106             }
13107             if(t.duration && t.duration <= (now - t.taskStartTime)){
13108                 removeTask(t);
13109             }
13110         }
13111     };
13112
13113     /**
13114      * Queues a new task.
13115      * @param {Object} task
13116      */
13117     this.start = function(task){
13118         tasks.push(task);
13119         task.taskStartTime = new Date().getTime();
13120         task.taskRunTime = 0;
13121         task.taskRunCount = 0;
13122         startThread();
13123         return task;
13124     };
13125
13126     this.stop = function(task){
13127         removeTask(task);
13128         return task;
13129     };
13130
13131     this.stopAll = function(){
13132         stopThread();
13133         for(var i = 0, len = tasks.length; i < len; i++){
13134             if(tasks[i].onStop){
13135                 tasks[i].onStop();
13136             }
13137         }
13138         tasks = [];
13139         removeQueue = [];
13140     };
13141 };
13142
13143 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13144  * Based on:
13145  * Ext JS Library 1.1.1
13146  * Copyright(c) 2006-2007, Ext JS, LLC.
13147  *
13148  * Originally Released Under LGPL - original licence link has changed is not relivant.
13149  *
13150  * Fork - LGPL
13151  * <script type="text/javascript">
13152  */
13153
13154  
13155 /**
13156  * @class Roo.util.MixedCollection
13157  * @extends Roo.util.Observable
13158  * A Collection class that maintains both numeric indexes and keys and exposes events.
13159  * @constructor
13160  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13161  * collection (defaults to false)
13162  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13163  * and return the key value for that item.  This is used when available to look up the key on items that
13164  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13165  * equivalent to providing an implementation for the {@link #getKey} method.
13166  */
13167 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13168     this.items = [];
13169     this.map = {};
13170     this.keys = [];
13171     this.length = 0;
13172     this.addEvents({
13173         /**
13174          * @event clear
13175          * Fires when the collection is cleared.
13176          */
13177         "clear" : true,
13178         /**
13179          * @event add
13180          * Fires when an item is added to the collection.
13181          * @param {Number} index The index at which the item was added.
13182          * @param {Object} o The item added.
13183          * @param {String} key The key associated with the added item.
13184          */
13185         "add" : true,
13186         /**
13187          * @event replace
13188          * Fires when an item is replaced in the collection.
13189          * @param {String} key he key associated with the new added.
13190          * @param {Object} old The item being replaced.
13191          * @param {Object} new The new item.
13192          */
13193         "replace" : true,
13194         /**
13195          * @event remove
13196          * Fires when an item is removed from the collection.
13197          * @param {Object} o The item being removed.
13198          * @param {String} key (optional) The key associated with the removed item.
13199          */
13200         "remove" : true,
13201         "sort" : true
13202     });
13203     this.allowFunctions = allowFunctions === true;
13204     if(keyFn){
13205         this.getKey = keyFn;
13206     }
13207     Roo.util.MixedCollection.superclass.constructor.call(this);
13208 };
13209
13210 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13211     allowFunctions : false,
13212     
13213 /**
13214  * Adds an item to the collection.
13215  * @param {String} key The key to associate with the item
13216  * @param {Object} o The item to add.
13217  * @return {Object} The item added.
13218  */
13219     add : function(key, o){
13220         if(arguments.length == 1){
13221             o = arguments[0];
13222             key = this.getKey(o);
13223         }
13224         if(typeof key == "undefined" || key === null){
13225             this.length++;
13226             this.items.push(o);
13227             this.keys.push(null);
13228         }else{
13229             var old = this.map[key];
13230             if(old){
13231                 return this.replace(key, o);
13232             }
13233             this.length++;
13234             this.items.push(o);
13235             this.map[key] = o;
13236             this.keys.push(key);
13237         }
13238         this.fireEvent("add", this.length-1, o, key);
13239         return o;
13240     },
13241        
13242 /**
13243   * MixedCollection has a generic way to fetch keys if you implement getKey.
13244 <pre><code>
13245 // normal way
13246 var mc = new Roo.util.MixedCollection();
13247 mc.add(someEl.dom.id, someEl);
13248 mc.add(otherEl.dom.id, otherEl);
13249 //and so on
13250
13251 // using getKey
13252 var mc = new Roo.util.MixedCollection();
13253 mc.getKey = function(el){
13254    return el.dom.id;
13255 };
13256 mc.add(someEl);
13257 mc.add(otherEl);
13258
13259 // or via the constructor
13260 var mc = new Roo.util.MixedCollection(false, function(el){
13261    return el.dom.id;
13262 });
13263 mc.add(someEl);
13264 mc.add(otherEl);
13265 </code></pre>
13266  * @param o {Object} The item for which to find the key.
13267  * @return {Object} The key for the passed item.
13268  */
13269     getKey : function(o){
13270          return o.id; 
13271     },
13272    
13273 /**
13274  * Replaces an item in the collection.
13275  * @param {String} key The key associated with the item to replace, or the item to replace.
13276  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13277  * @return {Object}  The new item.
13278  */
13279     replace : function(key, o){
13280         if(arguments.length == 1){
13281             o = arguments[0];
13282             key = this.getKey(o);
13283         }
13284         var old = this.item(key);
13285         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13286              return this.add(key, o);
13287         }
13288         var index = this.indexOfKey(key);
13289         this.items[index] = o;
13290         this.map[key] = o;
13291         this.fireEvent("replace", key, old, o);
13292         return o;
13293     },
13294    
13295 /**
13296  * Adds all elements of an Array or an Object to the collection.
13297  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13298  * an Array of values, each of which are added to the collection.
13299  */
13300     addAll : function(objs){
13301         if(arguments.length > 1 || objs instanceof Array){
13302             var args = arguments.length > 1 ? arguments : objs;
13303             for(var i = 0, len = args.length; i < len; i++){
13304                 this.add(args[i]);
13305             }
13306         }else{
13307             for(var key in objs){
13308                 if(this.allowFunctions || typeof objs[key] != "function"){
13309                     this.add(key, objs[key]);
13310                 }
13311             }
13312         }
13313     },
13314    
13315 /**
13316  * Executes the specified function once for every item in the collection, passing each
13317  * item as the first and only parameter. returning false from the function will stop the iteration.
13318  * @param {Function} fn The function to execute for each item.
13319  * @param {Object} scope (optional) The scope in which to execute the function.
13320  */
13321     each : function(fn, scope){
13322         var items = [].concat(this.items); // each safe for removal
13323         for(var i = 0, len = items.length; i < len; i++){
13324             if(fn.call(scope || items[i], items[i], i, len) === false){
13325                 break;
13326             }
13327         }
13328     },
13329    
13330 /**
13331  * Executes the specified function once for every key in the collection, passing each
13332  * key, and its associated item as the first two parameters.
13333  * @param {Function} fn The function to execute for each item.
13334  * @param {Object} scope (optional) The scope in which to execute the function.
13335  */
13336     eachKey : function(fn, scope){
13337         for(var i = 0, len = this.keys.length; i < len; i++){
13338             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13339         }
13340     },
13341    
13342 /**
13343  * Returns the first item in the collection which elicits a true return value from the
13344  * passed selection function.
13345  * @param {Function} fn The selection function to execute for each item.
13346  * @param {Object} scope (optional) The scope in which to execute the function.
13347  * @return {Object} The first item in the collection which returned true from the selection function.
13348  */
13349     find : function(fn, scope){
13350         for(var i = 0, len = this.items.length; i < len; i++){
13351             if(fn.call(scope || window, this.items[i], this.keys[i])){
13352                 return this.items[i];
13353             }
13354         }
13355         return null;
13356     },
13357    
13358 /**
13359  * Inserts an item at the specified index in the collection.
13360  * @param {Number} index The index to insert the item at.
13361  * @param {String} key The key to associate with the new item, or the item itself.
13362  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13363  * @return {Object} The item inserted.
13364  */
13365     insert : function(index, key, o){
13366         if(arguments.length == 2){
13367             o = arguments[1];
13368             key = this.getKey(o);
13369         }
13370         if(index >= this.length){
13371             return this.add(key, o);
13372         }
13373         this.length++;
13374         this.items.splice(index, 0, o);
13375         if(typeof key != "undefined" && key != null){
13376             this.map[key] = o;
13377         }
13378         this.keys.splice(index, 0, key);
13379         this.fireEvent("add", index, o, key);
13380         return o;
13381     },
13382    
13383 /**
13384  * Removed an item from the collection.
13385  * @param {Object} o The item to remove.
13386  * @return {Object} The item removed.
13387  */
13388     remove : function(o){
13389         return this.removeAt(this.indexOf(o));
13390     },
13391    
13392 /**
13393  * Remove an item from a specified index in the collection.
13394  * @param {Number} index The index within the collection of the item to remove.
13395  */
13396     removeAt : function(index){
13397         if(index < this.length && index >= 0){
13398             this.length--;
13399             var o = this.items[index];
13400             this.items.splice(index, 1);
13401             var key = this.keys[index];
13402             if(typeof key != "undefined"){
13403                 delete this.map[key];
13404             }
13405             this.keys.splice(index, 1);
13406             this.fireEvent("remove", o, key);
13407         }
13408     },
13409    
13410 /**
13411  * Removed an item associated with the passed key fom the collection.
13412  * @param {String} key The key of the item to remove.
13413  */
13414     removeKey : function(key){
13415         return this.removeAt(this.indexOfKey(key));
13416     },
13417    
13418 /**
13419  * Returns the number of items in the collection.
13420  * @return {Number} the number of items in the collection.
13421  */
13422     getCount : function(){
13423         return this.length; 
13424     },
13425    
13426 /**
13427  * Returns index within the collection of the passed Object.
13428  * @param {Object} o The item to find the index of.
13429  * @return {Number} index of the item.
13430  */
13431     indexOf : function(o){
13432         if(!this.items.indexOf){
13433             for(var i = 0, len = this.items.length; i < len; i++){
13434                 if(this.items[i] == o) {
13435                     return i;
13436                 }
13437             }
13438             return -1;
13439         }else{
13440             return this.items.indexOf(o);
13441         }
13442     },
13443    
13444 /**
13445  * Returns index within the collection of the passed key.
13446  * @param {String} key The key to find the index of.
13447  * @return {Number} index of the key.
13448  */
13449     indexOfKey : function(key){
13450         if(!this.keys.indexOf){
13451             for(var i = 0, len = this.keys.length; i < len; i++){
13452                 if(this.keys[i] == key) {
13453                     return i;
13454                 }
13455             }
13456             return -1;
13457         }else{
13458             return this.keys.indexOf(key);
13459         }
13460     },
13461    
13462 /**
13463  * Returns the item associated with the passed key OR index. Key has priority over index.
13464  * @param {String/Number} key The key or index of the item.
13465  * @return {Object} The item associated with the passed key.
13466  */
13467     item : function(key){
13468         if (key === 'length') {
13469             return null;
13470         }
13471         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13472         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13473     },
13474     
13475 /**
13476  * Returns the item at the specified index.
13477  * @param {Number} index The index of the item.
13478  * @return {Object}
13479  */
13480     itemAt : function(index){
13481         return this.items[index];
13482     },
13483     
13484 /**
13485  * Returns the item associated with the passed key.
13486  * @param {String/Number} key The key of the item.
13487  * @return {Object} The item associated with the passed key.
13488  */
13489     key : function(key){
13490         return this.map[key];
13491     },
13492    
13493 /**
13494  * Returns true if the collection contains the passed Object as an item.
13495  * @param {Object} o  The Object to look for in the collection.
13496  * @return {Boolean} True if the collection contains the Object as an item.
13497  */
13498     contains : function(o){
13499         return this.indexOf(o) != -1;
13500     },
13501    
13502 /**
13503  * Returns true if the collection contains the passed Object as a key.
13504  * @param {String} key The key to look for in the collection.
13505  * @return {Boolean} True if the collection contains the Object as a key.
13506  */
13507     containsKey : function(key){
13508         return typeof this.map[key] != "undefined";
13509     },
13510    
13511 /**
13512  * Removes all items from the collection.
13513  */
13514     clear : function(){
13515         this.length = 0;
13516         this.items = [];
13517         this.keys = [];
13518         this.map = {};
13519         this.fireEvent("clear");
13520     },
13521    
13522 /**
13523  * Returns the first item in the collection.
13524  * @return {Object} the first item in the collection..
13525  */
13526     first : function(){
13527         return this.items[0]; 
13528     },
13529    
13530 /**
13531  * Returns the last item in the collection.
13532  * @return {Object} the last item in the collection..
13533  */
13534     last : function(){
13535         return this.items[this.length-1];   
13536     },
13537     
13538     _sort : function(property, dir, fn){
13539         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13540         fn = fn || function(a, b){
13541             return a-b;
13542         };
13543         var c = [], k = this.keys, items = this.items;
13544         for(var i = 0, len = items.length; i < len; i++){
13545             c[c.length] = {key: k[i], value: items[i], index: i};
13546         }
13547         c.sort(function(a, b){
13548             var v = fn(a[property], b[property]) * dsc;
13549             if(v == 0){
13550                 v = (a.index < b.index ? -1 : 1);
13551             }
13552             return v;
13553         });
13554         for(var i = 0, len = c.length; i < len; i++){
13555             items[i] = c[i].value;
13556             k[i] = c[i].key;
13557         }
13558         this.fireEvent("sort", this);
13559     },
13560     
13561     /**
13562      * Sorts this collection with the passed comparison function
13563      * @param {String} direction (optional) "ASC" or "DESC"
13564      * @param {Function} fn (optional) comparison function
13565      */
13566     sort : function(dir, fn){
13567         this._sort("value", dir, fn);
13568     },
13569     
13570     /**
13571      * Sorts this collection by keys
13572      * @param {String} direction (optional) "ASC" or "DESC"
13573      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13574      */
13575     keySort : function(dir, fn){
13576         this._sort("key", dir, fn || function(a, b){
13577             return String(a).toUpperCase()-String(b).toUpperCase();
13578         });
13579     },
13580     
13581     /**
13582      * Returns a range of items in this collection
13583      * @param {Number} startIndex (optional) defaults to 0
13584      * @param {Number} endIndex (optional) default to the last item
13585      * @return {Array} An array of items
13586      */
13587     getRange : function(start, end){
13588         var items = this.items;
13589         if(items.length < 1){
13590             return [];
13591         }
13592         start = start || 0;
13593         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13594         var r = [];
13595         if(start <= end){
13596             for(var i = start; i <= end; i++) {
13597                     r[r.length] = items[i];
13598             }
13599         }else{
13600             for(var i = start; i >= end; i--) {
13601                     r[r.length] = items[i];
13602             }
13603         }
13604         return r;
13605     },
13606         
13607     /**
13608      * Filter the <i>objects</i> in this collection by a specific property. 
13609      * Returns a new collection that has been filtered.
13610      * @param {String} property A property on your objects
13611      * @param {String/RegExp} value Either string that the property values 
13612      * should start with or a RegExp to test against the property
13613      * @return {MixedCollection} The new filtered collection
13614      */
13615     filter : function(property, value){
13616         if(!value.exec){ // not a regex
13617             value = String(value);
13618             if(value.length == 0){
13619                 return this.clone();
13620             }
13621             value = new RegExp("^" + Roo.escapeRe(value), "i");
13622         }
13623         return this.filterBy(function(o){
13624             return o && value.test(o[property]);
13625         });
13626         },
13627     
13628     /**
13629      * Filter by a function. * Returns a new collection that has been filtered.
13630      * The passed function will be called with each 
13631      * object in the collection. If the function returns true, the value is included 
13632      * otherwise it is filtered.
13633      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13634      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13635      * @return {MixedCollection} The new filtered collection
13636      */
13637     filterBy : function(fn, scope){
13638         var r = new Roo.util.MixedCollection();
13639         r.getKey = this.getKey;
13640         var k = this.keys, it = this.items;
13641         for(var i = 0, len = it.length; i < len; i++){
13642             if(fn.call(scope||this, it[i], k[i])){
13643                                 r.add(k[i], it[i]);
13644                         }
13645         }
13646         return r;
13647     },
13648     
13649     /**
13650      * Creates a duplicate of this collection
13651      * @return {MixedCollection}
13652      */
13653     clone : function(){
13654         var r = new Roo.util.MixedCollection();
13655         var k = this.keys, it = this.items;
13656         for(var i = 0, len = it.length; i < len; i++){
13657             r.add(k[i], it[i]);
13658         }
13659         r.getKey = this.getKey;
13660         return r;
13661     }
13662 });
13663 /**
13664  * Returns the item associated with the passed key or index.
13665  * @method
13666  * @param {String/Number} key The key or index of the item.
13667  * @return {Object} The item associated with the passed key.
13668  */
13669 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13670  * Based on:
13671  * Ext JS Library 1.1.1
13672  * Copyright(c) 2006-2007, Ext JS, LLC.
13673  *
13674  * Originally Released Under LGPL - original licence link has changed is not relivant.
13675  *
13676  * Fork - LGPL
13677  * <script type="text/javascript">
13678  */
13679 /**
13680  * @class Roo.util.JSON
13681  * Modified version of Douglas Crockford"s json.js that doesn"t
13682  * mess with the Object prototype 
13683  * http://www.json.org/js.html
13684  * @singleton
13685  */
13686 Roo.util.JSON = new (function(){
13687     var useHasOwn = {}.hasOwnProperty ? true : false;
13688     
13689     // crashes Safari in some instances
13690     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13691     
13692     var pad = function(n) {
13693         return n < 10 ? "0" + n : n;
13694     };
13695     
13696     var m = {
13697         "\b": '\\b',
13698         "\t": '\\t',
13699         "\n": '\\n',
13700         "\f": '\\f',
13701         "\r": '\\r',
13702         '"' : '\\"',
13703         "\\": '\\\\'
13704     };
13705
13706     var encodeString = function(s){
13707         if (/["\\\x00-\x1f]/.test(s)) {
13708             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13709                 var c = m[b];
13710                 if(c){
13711                     return c;
13712                 }
13713                 c = b.charCodeAt();
13714                 return "\\u00" +
13715                     Math.floor(c / 16).toString(16) +
13716                     (c % 16).toString(16);
13717             }) + '"';
13718         }
13719         return '"' + s + '"';
13720     };
13721     
13722     var encodeArray = function(o){
13723         var a = ["["], b, i, l = o.length, v;
13724             for (i = 0; i < l; i += 1) {
13725                 v = o[i];
13726                 switch (typeof v) {
13727                     case "undefined":
13728                     case "function":
13729                     case "unknown":
13730                         break;
13731                     default:
13732                         if (b) {
13733                             a.push(',');
13734                         }
13735                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13736                         b = true;
13737                 }
13738             }
13739             a.push("]");
13740             return a.join("");
13741     };
13742     
13743     var encodeDate = function(o){
13744         return '"' + o.getFullYear() + "-" +
13745                 pad(o.getMonth() + 1) + "-" +
13746                 pad(o.getDate()) + "T" +
13747                 pad(o.getHours()) + ":" +
13748                 pad(o.getMinutes()) + ":" +
13749                 pad(o.getSeconds()) + '"';
13750     };
13751     
13752     /**
13753      * Encodes an Object, Array or other value
13754      * @param {Mixed} o The variable to encode
13755      * @return {String} The JSON string
13756      */
13757     this.encode = function(o)
13758     {
13759         // should this be extended to fully wrap stringify..
13760         
13761         if(typeof o == "undefined" || o === null){
13762             return "null";
13763         }else if(o instanceof Array){
13764             return encodeArray(o);
13765         }else if(o instanceof Date){
13766             return encodeDate(o);
13767         }else if(typeof o == "string"){
13768             return encodeString(o);
13769         }else if(typeof o == "number"){
13770             return isFinite(o) ? String(o) : "null";
13771         }else if(typeof o == "boolean"){
13772             return String(o);
13773         }else {
13774             var a = ["{"], b, i, v;
13775             for (i in o) {
13776                 if(!useHasOwn || o.hasOwnProperty(i)) {
13777                     v = o[i];
13778                     switch (typeof v) {
13779                     case "undefined":
13780                     case "function":
13781                     case "unknown":
13782                         break;
13783                     default:
13784                         if(b){
13785                             a.push(',');
13786                         }
13787                         a.push(this.encode(i), ":",
13788                                 v === null ? "null" : this.encode(v));
13789                         b = true;
13790                     }
13791                 }
13792             }
13793             a.push("}");
13794             return a.join("");
13795         }
13796     };
13797     
13798     /**
13799      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13800      * @param {String} json The JSON string
13801      * @return {Object} The resulting object
13802      */
13803     this.decode = function(json){
13804         
13805         return  /** eval:var:json */ eval("(" + json + ')');
13806     };
13807 })();
13808 /** 
13809  * Shorthand for {@link Roo.util.JSON#encode}
13810  * @member Roo encode 
13811  * @method */
13812 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13813 /** 
13814  * Shorthand for {@link Roo.util.JSON#decode}
13815  * @member Roo decode 
13816  * @method */
13817 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13818 /*
13819  * Based on:
13820  * Ext JS Library 1.1.1
13821  * Copyright(c) 2006-2007, Ext JS, LLC.
13822  *
13823  * Originally Released Under LGPL - original licence link has changed is not relivant.
13824  *
13825  * Fork - LGPL
13826  * <script type="text/javascript">
13827  */
13828  
13829 /**
13830  * @class Roo.util.Format
13831  * Reusable data formatting functions
13832  * @singleton
13833  */
13834 Roo.util.Format = function(){
13835     var trimRe = /^\s+|\s+$/g;
13836     return {
13837         /**
13838          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13839          * @param {String} value The string to truncate
13840          * @param {Number} length The maximum length to allow before truncating
13841          * @return {String} The converted text
13842          */
13843         ellipsis : function(value, len){
13844             if(value && value.length > len){
13845                 return value.substr(0, len-3)+"...";
13846             }
13847             return value;
13848         },
13849
13850         /**
13851          * Checks a reference and converts it to empty string if it is undefined
13852          * @param {Mixed} value Reference to check
13853          * @return {Mixed} Empty string if converted, otherwise the original value
13854          */
13855         undef : function(value){
13856             return typeof value != "undefined" ? value : "";
13857         },
13858
13859         /**
13860          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13861          * @param {String} value The string to encode
13862          * @return {String} The encoded text
13863          */
13864         htmlEncode : function(value){
13865             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13866         },
13867
13868         /**
13869          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13870          * @param {String} value The string to decode
13871          * @return {String} The decoded text
13872          */
13873         htmlDecode : function(value){
13874             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13875         },
13876
13877         /**
13878          * Trims any whitespace from either side of a string
13879          * @param {String} value The text to trim
13880          * @return {String} The trimmed text
13881          */
13882         trim : function(value){
13883             return String(value).replace(trimRe, "");
13884         },
13885
13886         /**
13887          * Returns a substring from within an original string
13888          * @param {String} value The original text
13889          * @param {Number} start The start index of the substring
13890          * @param {Number} length The length of the substring
13891          * @return {String} The substring
13892          */
13893         substr : function(value, start, length){
13894             return String(value).substr(start, length);
13895         },
13896
13897         /**
13898          * Converts a string to all lower case letters
13899          * @param {String} value The text to convert
13900          * @return {String} The converted text
13901          */
13902         lowercase : function(value){
13903             return String(value).toLowerCase();
13904         },
13905
13906         /**
13907          * Converts a string to all upper case letters
13908          * @param {String} value The text to convert
13909          * @return {String} The converted text
13910          */
13911         uppercase : function(value){
13912             return String(value).toUpperCase();
13913         },
13914
13915         /**
13916          * Converts the first character only of a string to upper case
13917          * @param {String} value The text to convert
13918          * @return {String} The converted text
13919          */
13920         capitalize : function(value){
13921             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13922         },
13923
13924         // private
13925         call : function(value, fn){
13926             if(arguments.length > 2){
13927                 var args = Array.prototype.slice.call(arguments, 2);
13928                 args.unshift(value);
13929                  
13930                 return /** eval:var:value */  eval(fn).apply(window, args);
13931             }else{
13932                 /** eval:var:value */
13933                 return /** eval:var:value */ eval(fn).call(window, value);
13934             }
13935         },
13936
13937        
13938         /**
13939          * safer version of Math.toFixed..??/
13940          * @param {Number/String} value The numeric value to format
13941          * @param {Number/String} value Decimal places 
13942          * @return {String} The formatted currency string
13943          */
13944         toFixed : function(v, n)
13945         {
13946             // why not use to fixed - precision is buggered???
13947             if (!n) {
13948                 return Math.round(v-0);
13949             }
13950             var fact = Math.pow(10,n+1);
13951             v = (Math.round((v-0)*fact))/fact;
13952             var z = (''+fact).substring(2);
13953             if (v == Math.floor(v)) {
13954                 return Math.floor(v) + '.' + z;
13955             }
13956             
13957             // now just padd decimals..
13958             var ps = String(v).split('.');
13959             var fd = (ps[1] + z);
13960             var r = fd.substring(0,n); 
13961             var rm = fd.substring(n); 
13962             if (rm < 5) {
13963                 return ps[0] + '.' + r;
13964             }
13965             r*=1; // turn it into a number;
13966             r++;
13967             if (String(r).length != n) {
13968                 ps[0]*=1;
13969                 ps[0]++;
13970                 r = String(r).substring(1); // chop the end off.
13971             }
13972             
13973             return ps[0] + '.' + r;
13974              
13975         },
13976         
13977         /**
13978          * Format a number as US currency
13979          * @param {Number/String} value The numeric value to format
13980          * @return {String} The formatted currency string
13981          */
13982         usMoney : function(v){
13983             return '$' + Roo.util.Format.number(v);
13984         },
13985         
13986         /**
13987          * Format a number
13988          * eventually this should probably emulate php's number_format
13989          * @param {Number/String} value The numeric value to format
13990          * @param {Number} decimals number of decimal places
13991          * @param {String} delimiter for thousands (default comma)
13992          * @return {String} The formatted currency string
13993          */
13994         number : function(v, decimals, thousandsDelimiter)
13995         {
13996             // multiply and round.
13997             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13998             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13999             
14000             var mul = Math.pow(10, decimals);
14001             var zero = String(mul).substring(1);
14002             v = (Math.round((v-0)*mul))/mul;
14003             
14004             // if it's '0' number.. then
14005             
14006             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14007             v = String(v);
14008             var ps = v.split('.');
14009             var whole = ps[0];
14010             
14011             var r = /(\d+)(\d{3})/;
14012             // add comma's
14013             
14014             if(thousandsDelimiter.length != 0) {
14015                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14016             } 
14017             
14018             var sub = ps[1] ?
14019                     // has decimals..
14020                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14021                     // does not have decimals
14022                     (decimals ? ('.' + zero) : '');
14023             
14024             
14025             return whole + sub ;
14026         },
14027         
14028         /**
14029          * Parse a value into a formatted date using the specified format pattern.
14030          * @param {Mixed} value The value to format
14031          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14032          * @return {String} The formatted date string
14033          */
14034         date : function(v, format){
14035             if(!v){
14036                 return "";
14037             }
14038             if(!(v instanceof Date)){
14039                 v = new Date(Date.parse(v));
14040             }
14041             return v.dateFormat(format || Roo.util.Format.defaults.date);
14042         },
14043
14044         /**
14045          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14046          * @param {String} format Any valid date format string
14047          * @return {Function} The date formatting function
14048          */
14049         dateRenderer : function(format){
14050             return function(v){
14051                 return Roo.util.Format.date(v, format);  
14052             };
14053         },
14054
14055         // private
14056         stripTagsRE : /<\/?[^>]+>/gi,
14057         
14058         /**
14059          * Strips all HTML tags
14060          * @param {Mixed} value The text from which to strip tags
14061          * @return {String} The stripped text
14062          */
14063         stripTags : function(v){
14064             return !v ? v : String(v).replace(this.stripTagsRE, "");
14065         },
14066         
14067         /**
14068          * Size in Mb,Gb etc.
14069          * @param {Number} value The number to be formated
14070          * @param {number} decimals how many decimal places
14071          * @return {String} the formated string
14072          */
14073         size : function(value, decimals)
14074         {
14075             var sizes = ['b', 'k', 'M', 'G', 'T'];
14076             if (value == 0) {
14077                 return 0;
14078             }
14079             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14080             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14081         }
14082         
14083         
14084         
14085     };
14086 }();
14087 Roo.util.Format.defaults = {
14088     date : 'd/M/Y'
14089 };/*
14090  * Based on:
14091  * Ext JS Library 1.1.1
14092  * Copyright(c) 2006-2007, Ext JS, LLC.
14093  *
14094  * Originally Released Under LGPL - original licence link has changed is not relivant.
14095  *
14096  * Fork - LGPL
14097  * <script type="text/javascript">
14098  */
14099
14100
14101  
14102
14103 /**
14104  * @class Roo.MasterTemplate
14105  * @extends Roo.Template
14106  * Provides a template that can have child templates. The syntax is:
14107 <pre><code>
14108 var t = new Roo.MasterTemplate(
14109         '&lt;select name="{name}"&gt;',
14110                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14111         '&lt;/select&gt;'
14112 );
14113 t.add('options', {value: 'foo', text: 'bar'});
14114 // or you can add multiple child elements in one shot
14115 t.addAll('options', [
14116     {value: 'foo', text: 'bar'},
14117     {value: 'foo2', text: 'bar2'},
14118     {value: 'foo3', text: 'bar3'}
14119 ]);
14120 // then append, applying the master template values
14121 t.append('my-form', {name: 'my-select'});
14122 </code></pre>
14123 * A name attribute for the child template is not required if you have only one child
14124 * template or you want to refer to them by index.
14125  */
14126 Roo.MasterTemplate = function(){
14127     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14128     this.originalHtml = this.html;
14129     var st = {};
14130     var m, re = this.subTemplateRe;
14131     re.lastIndex = 0;
14132     var subIndex = 0;
14133     while(m = re.exec(this.html)){
14134         var name = m[1], content = m[2];
14135         st[subIndex] = {
14136             name: name,
14137             index: subIndex,
14138             buffer: [],
14139             tpl : new Roo.Template(content)
14140         };
14141         if(name){
14142             st[name] = st[subIndex];
14143         }
14144         st[subIndex].tpl.compile();
14145         st[subIndex].tpl.call = this.call.createDelegate(this);
14146         subIndex++;
14147     }
14148     this.subCount = subIndex;
14149     this.subs = st;
14150 };
14151 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14152     /**
14153     * The regular expression used to match sub templates
14154     * @type RegExp
14155     * @property
14156     */
14157     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14158
14159     /**
14160      * Applies the passed values to a child template.
14161      * @param {String/Number} name (optional) The name or index of the child template
14162      * @param {Array/Object} values The values to be applied to the template
14163      * @return {MasterTemplate} this
14164      */
14165      add : function(name, values){
14166         if(arguments.length == 1){
14167             values = arguments[0];
14168             name = 0;
14169         }
14170         var s = this.subs[name];
14171         s.buffer[s.buffer.length] = s.tpl.apply(values);
14172         return this;
14173     },
14174
14175     /**
14176      * Applies all the passed values to a child template.
14177      * @param {String/Number} name (optional) The name or index of the child template
14178      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14179      * @param {Boolean} reset (optional) True to reset the template first
14180      * @return {MasterTemplate} this
14181      */
14182     fill : function(name, values, reset){
14183         var a = arguments;
14184         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14185             values = a[0];
14186             name = 0;
14187             reset = a[1];
14188         }
14189         if(reset){
14190             this.reset();
14191         }
14192         for(var i = 0, len = values.length; i < len; i++){
14193             this.add(name, values[i]);
14194         }
14195         return this;
14196     },
14197
14198     /**
14199      * Resets the template for reuse
14200      * @return {MasterTemplate} this
14201      */
14202      reset : function(){
14203         var s = this.subs;
14204         for(var i = 0; i < this.subCount; i++){
14205             s[i].buffer = [];
14206         }
14207         return this;
14208     },
14209
14210     applyTemplate : function(values){
14211         var s = this.subs;
14212         var replaceIndex = -1;
14213         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14214             return s[++replaceIndex].buffer.join("");
14215         });
14216         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14217     },
14218
14219     apply : function(){
14220         return this.applyTemplate.apply(this, arguments);
14221     },
14222
14223     compile : function(){return this;}
14224 });
14225
14226 /**
14227  * Alias for fill().
14228  * @method
14229  */
14230 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14231  /**
14232  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14233  * var tpl = Roo.MasterTemplate.from('element-id');
14234  * @param {String/HTMLElement} el
14235  * @param {Object} config
14236  * @static
14237  */
14238 Roo.MasterTemplate.from = function(el, config){
14239     el = Roo.getDom(el);
14240     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14241 };/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251
14252  
14253 /**
14254  * @class Roo.util.CSS
14255  * Utility class for manipulating CSS rules
14256  * @singleton
14257  */
14258 Roo.util.CSS = function(){
14259         var rules = null;
14260         var doc = document;
14261
14262     var camelRe = /(-[a-z])/gi;
14263     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14264
14265    return {
14266    /**
14267     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14268     * tag and appended to the HEAD of the document.
14269     * @param {String|Object} cssText The text containing the css rules
14270     * @param {String} id An id to add to the stylesheet for later removal
14271     * @return {StyleSheet}
14272     */
14273     createStyleSheet : function(cssText, id){
14274         var ss;
14275         var head = doc.getElementsByTagName("head")[0];
14276         var nrules = doc.createElement("style");
14277         nrules.setAttribute("type", "text/css");
14278         if(id){
14279             nrules.setAttribute("id", id);
14280         }
14281         if (typeof(cssText) != 'string') {
14282             // support object maps..
14283             // not sure if this a good idea.. 
14284             // perhaps it should be merged with the general css handling
14285             // and handle js style props.
14286             var cssTextNew = [];
14287             for(var n in cssText) {
14288                 var citems = [];
14289                 for(var k in cssText[n]) {
14290                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14291                 }
14292                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14293                 
14294             }
14295             cssText = cssTextNew.join("\n");
14296             
14297         }
14298        
14299        
14300        if(Roo.isIE){
14301            head.appendChild(nrules);
14302            ss = nrules.styleSheet;
14303            ss.cssText = cssText;
14304        }else{
14305            try{
14306                 nrules.appendChild(doc.createTextNode(cssText));
14307            }catch(e){
14308                nrules.cssText = cssText; 
14309            }
14310            head.appendChild(nrules);
14311            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14312        }
14313        this.cacheStyleSheet(ss);
14314        return ss;
14315    },
14316
14317    /**
14318     * Removes a style or link tag by id
14319     * @param {String} id The id of the tag
14320     */
14321    removeStyleSheet : function(id){
14322        var existing = doc.getElementById(id);
14323        if(existing){
14324            existing.parentNode.removeChild(existing);
14325        }
14326    },
14327
14328    /**
14329     * Dynamically swaps an existing stylesheet reference for a new one
14330     * @param {String} id The id of an existing link tag to remove
14331     * @param {String} url The href of the new stylesheet to include
14332     */
14333    swapStyleSheet : function(id, url){
14334        this.removeStyleSheet(id);
14335        var ss = doc.createElement("link");
14336        ss.setAttribute("rel", "stylesheet");
14337        ss.setAttribute("type", "text/css");
14338        ss.setAttribute("id", id);
14339        ss.setAttribute("href", url);
14340        doc.getElementsByTagName("head")[0].appendChild(ss);
14341    },
14342    
14343    /**
14344     * Refresh the rule cache if you have dynamically added stylesheets
14345     * @return {Object} An object (hash) of rules indexed by selector
14346     */
14347    refreshCache : function(){
14348        return this.getRules(true);
14349    },
14350
14351    // private
14352    cacheStyleSheet : function(stylesheet){
14353        if(!rules){
14354            rules = {};
14355        }
14356        try{// try catch for cross domain access issue
14357            var ssRules = stylesheet.cssRules || stylesheet.rules;
14358            for(var j = ssRules.length-1; j >= 0; --j){
14359                rules[ssRules[j].selectorText] = ssRules[j];
14360            }
14361        }catch(e){}
14362    },
14363    
14364    /**
14365     * Gets all css rules for the document
14366     * @param {Boolean} refreshCache true to refresh the internal cache
14367     * @return {Object} An object (hash) of rules indexed by selector
14368     */
14369    getRules : function(refreshCache){
14370                 if(rules == null || refreshCache){
14371                         rules = {};
14372                         var ds = doc.styleSheets;
14373                         for(var i =0, len = ds.length; i < len; i++){
14374                             try{
14375                         this.cacheStyleSheet(ds[i]);
14376                     }catch(e){} 
14377                 }
14378                 }
14379                 return rules;
14380         },
14381         
14382         /**
14383     * Gets an an individual CSS rule by selector(s)
14384     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14385     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14386     * @return {CSSRule} The CSS rule or null if one is not found
14387     */
14388    getRule : function(selector, refreshCache){
14389                 var rs = this.getRules(refreshCache);
14390                 if(!(selector instanceof Array)){
14391                     return rs[selector];
14392                 }
14393                 for(var i = 0; i < selector.length; i++){
14394                         if(rs[selector[i]]){
14395                                 return rs[selector[i]];
14396                         }
14397                 }
14398                 return null;
14399         },
14400         
14401         
14402         /**
14403     * Updates a rule property
14404     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14405     * @param {String} property The css property
14406     * @param {String} value The new value for the property
14407     * @return {Boolean} true If a rule was found and updated
14408     */
14409    updateRule : function(selector, property, value){
14410                 if(!(selector instanceof Array)){
14411                         var rule = this.getRule(selector);
14412                         if(rule){
14413                                 rule.style[property.replace(camelRe, camelFn)] = value;
14414                                 return true;
14415                         }
14416                 }else{
14417                         for(var i = 0; i < selector.length; i++){
14418                                 if(this.updateRule(selector[i], property, value)){
14419                                         return true;
14420                                 }
14421                         }
14422                 }
14423                 return false;
14424         }
14425    };   
14426 }();/*
14427  * Based on:
14428  * Ext JS Library 1.1.1
14429  * Copyright(c) 2006-2007, Ext JS, LLC.
14430  *
14431  * Originally Released Under LGPL - original licence link has changed is not relivant.
14432  *
14433  * Fork - LGPL
14434  * <script type="text/javascript">
14435  */
14436
14437  
14438
14439 /**
14440  * @class Roo.util.ClickRepeater
14441  * @extends Roo.util.Observable
14442  * 
14443  * A wrapper class which can be applied to any element. Fires a "click" event while the
14444  * mouse is pressed. The interval between firings may be specified in the config but
14445  * defaults to 10 milliseconds.
14446  * 
14447  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14448  * 
14449  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14450  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14451  * Similar to an autorepeat key delay.
14452  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14453  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14454  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14455  *           "interval" and "delay" are ignored. "immediate" is honored.
14456  * @cfg {Boolean} preventDefault True to prevent the default click event
14457  * @cfg {Boolean} stopDefault True to stop the default click event
14458  * 
14459  * @history
14460  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14461  *     2007-02-02 jvs Renamed to ClickRepeater
14462  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14463  *
14464  *  @constructor
14465  * @param {String/HTMLElement/Element} el The element to listen on
14466  * @param {Object} config
14467  **/
14468 Roo.util.ClickRepeater = function(el, config)
14469 {
14470     this.el = Roo.get(el);
14471     this.el.unselectable();
14472
14473     Roo.apply(this, config);
14474
14475     this.addEvents({
14476     /**
14477      * @event mousedown
14478      * Fires when the mouse button is depressed.
14479      * @param {Roo.util.ClickRepeater} this
14480      */
14481         "mousedown" : true,
14482     /**
14483      * @event click
14484      * Fires on a specified interval during the time the element is pressed.
14485      * @param {Roo.util.ClickRepeater} this
14486      */
14487         "click" : true,
14488     /**
14489      * @event mouseup
14490      * Fires when the mouse key is released.
14491      * @param {Roo.util.ClickRepeater} this
14492      */
14493         "mouseup" : true
14494     });
14495
14496     this.el.on("mousedown", this.handleMouseDown, this);
14497     if(this.preventDefault || this.stopDefault){
14498         this.el.on("click", function(e){
14499             if(this.preventDefault){
14500                 e.preventDefault();
14501             }
14502             if(this.stopDefault){
14503                 e.stopEvent();
14504             }
14505         }, this);
14506     }
14507
14508     // allow inline handler
14509     if(this.handler){
14510         this.on("click", this.handler,  this.scope || this);
14511     }
14512
14513     Roo.util.ClickRepeater.superclass.constructor.call(this);
14514 };
14515
14516 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14517     interval : 20,
14518     delay: 250,
14519     preventDefault : true,
14520     stopDefault : false,
14521     timer : 0,
14522
14523     // private
14524     handleMouseDown : function(){
14525         clearTimeout(this.timer);
14526         this.el.blur();
14527         if(this.pressClass){
14528             this.el.addClass(this.pressClass);
14529         }
14530         this.mousedownTime = new Date();
14531
14532         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14533         this.el.on("mouseout", this.handleMouseOut, this);
14534
14535         this.fireEvent("mousedown", this);
14536         this.fireEvent("click", this);
14537         
14538         this.timer = this.click.defer(this.delay || this.interval, this);
14539     },
14540
14541     // private
14542     click : function(){
14543         this.fireEvent("click", this);
14544         this.timer = this.click.defer(this.getInterval(), this);
14545     },
14546
14547     // private
14548     getInterval: function(){
14549         if(!this.accelerate){
14550             return this.interval;
14551         }
14552         var pressTime = this.mousedownTime.getElapsed();
14553         if(pressTime < 500){
14554             return 400;
14555         }else if(pressTime < 1700){
14556             return 320;
14557         }else if(pressTime < 2600){
14558             return 250;
14559         }else if(pressTime < 3500){
14560             return 180;
14561         }else if(pressTime < 4400){
14562             return 140;
14563         }else if(pressTime < 5300){
14564             return 80;
14565         }else if(pressTime < 6200){
14566             return 50;
14567         }else{
14568             return 10;
14569         }
14570     },
14571
14572     // private
14573     handleMouseOut : function(){
14574         clearTimeout(this.timer);
14575         if(this.pressClass){
14576             this.el.removeClass(this.pressClass);
14577         }
14578         this.el.on("mouseover", this.handleMouseReturn, this);
14579     },
14580
14581     // private
14582     handleMouseReturn : function(){
14583         this.el.un("mouseover", this.handleMouseReturn);
14584         if(this.pressClass){
14585             this.el.addClass(this.pressClass);
14586         }
14587         this.click();
14588     },
14589
14590     // private
14591     handleMouseUp : function(){
14592         clearTimeout(this.timer);
14593         this.el.un("mouseover", this.handleMouseReturn);
14594         this.el.un("mouseout", this.handleMouseOut);
14595         Roo.get(document).un("mouseup", this.handleMouseUp);
14596         this.el.removeClass(this.pressClass);
14597         this.fireEvent("mouseup", this);
14598     }
14599 });/**
14600  * @class Roo.util.Clipboard
14601  * @static
14602  * 
14603  * Clipboard UTILS
14604  * 
14605  **/
14606 Roo.util.Clipboard = {
14607     /**
14608      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14609      * @param {String} text to copy to clipboard
14610      */
14611     write : function(text) {
14612         // navigator clipboard api needs a secure context (https)
14613         if (navigator.clipboard && window.isSecureContext) {
14614             // navigator clipboard api method'
14615             navigator.clipboard.writeText(text);
14616             return ;
14617         } 
14618         // text area method
14619         var ta = document.createElement("textarea");
14620         ta.value = text;
14621         // make the textarea out of viewport
14622         ta.style.position = "fixed";
14623         ta.style.left = "-999999px";
14624         ta.style.top = "-999999px";
14625         document.body.appendChild(ta);
14626         ta.focus();
14627         ta.select();
14628         document.execCommand('copy');
14629         (function() {
14630             ta.remove();
14631         }).defer(100);
14632         
14633     }
14634         
14635 }
14636     /*
14637  * Based on:
14638  * Ext JS Library 1.1.1
14639  * Copyright(c) 2006-2007, Ext JS, LLC.
14640  *
14641  * Originally Released Under LGPL - original licence link has changed is not relivant.
14642  *
14643  * Fork - LGPL
14644  * <script type="text/javascript">
14645  */
14646
14647  
14648 /**
14649  * @class Roo.KeyNav
14650  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14651  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14652  * way to implement custom navigation schemes for any UI component.</p>
14653  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14654  * pageUp, pageDown, del, home, end.  Usage:</p>
14655  <pre><code>
14656 var nav = new Roo.KeyNav("my-element", {
14657     "left" : function(e){
14658         this.moveLeft(e.ctrlKey);
14659     },
14660     "right" : function(e){
14661         this.moveRight(e.ctrlKey);
14662     },
14663     "enter" : function(e){
14664         this.save();
14665     },
14666     scope : this
14667 });
14668 </code></pre>
14669  * @constructor
14670  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14671  * @param {Object} config The config
14672  */
14673 Roo.KeyNav = function(el, config){
14674     this.el = Roo.get(el);
14675     Roo.apply(this, config);
14676     if(!this.disabled){
14677         this.disabled = true;
14678         this.enable();
14679     }
14680 };
14681
14682 Roo.KeyNav.prototype = {
14683     /**
14684      * @cfg {Boolean} disabled
14685      * True to disable this KeyNav instance (defaults to false)
14686      */
14687     disabled : false,
14688     /**
14689      * @cfg {String} defaultEventAction
14690      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14691      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14692      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14693      */
14694     defaultEventAction: "stopEvent",
14695     /**
14696      * @cfg {Boolean} forceKeyDown
14697      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14698      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14699      * handle keydown instead of keypress.
14700      */
14701     forceKeyDown : false,
14702
14703     // private
14704     prepareEvent : function(e){
14705         var k = e.getKey();
14706         var h = this.keyToHandler[k];
14707         //if(h && this[h]){
14708         //    e.stopPropagation();
14709         //}
14710         if(Roo.isSafari && h && k >= 37 && k <= 40){
14711             e.stopEvent();
14712         }
14713     },
14714
14715     // private
14716     relay : function(e){
14717         var k = e.getKey();
14718         var h = this.keyToHandler[k];
14719         if(h && this[h]){
14720             if(this.doRelay(e, this[h], h) !== true){
14721                 e[this.defaultEventAction]();
14722             }
14723         }
14724     },
14725
14726     // private
14727     doRelay : function(e, h, hname){
14728         return h.call(this.scope || this, e);
14729     },
14730
14731     // possible handlers
14732     enter : false,
14733     left : false,
14734     right : false,
14735     up : false,
14736     down : false,
14737     tab : false,
14738     esc : false,
14739     pageUp : false,
14740     pageDown : false,
14741     del : false,
14742     home : false,
14743     end : false,
14744
14745     // quick lookup hash
14746     keyToHandler : {
14747         37 : "left",
14748         39 : "right",
14749         38 : "up",
14750         40 : "down",
14751         33 : "pageUp",
14752         34 : "pageDown",
14753         46 : "del",
14754         36 : "home",
14755         35 : "end",
14756         13 : "enter",
14757         27 : "esc",
14758         9  : "tab"
14759     },
14760
14761         /**
14762          * Enable this KeyNav
14763          */
14764         enable: function(){
14765                 if(this.disabled){
14766             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14767             // the EventObject will normalize Safari automatically
14768             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14769                 this.el.on("keydown", this.relay,  this);
14770             }else{
14771                 this.el.on("keydown", this.prepareEvent,  this);
14772                 this.el.on("keypress", this.relay,  this);
14773             }
14774                     this.disabled = false;
14775                 }
14776         },
14777
14778         /**
14779          * Disable this KeyNav
14780          */
14781         disable: function(){
14782                 if(!this.disabled){
14783                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14784                 this.el.un("keydown", this.relay);
14785             }else{
14786                 this.el.un("keydown", this.prepareEvent);
14787                 this.el.un("keypress", this.relay);
14788             }
14789                     this.disabled = true;
14790                 }
14791         }
14792 };/*
14793  * Based on:
14794  * Ext JS Library 1.1.1
14795  * Copyright(c) 2006-2007, Ext JS, LLC.
14796  *
14797  * Originally Released Under LGPL - original licence link has changed is not relivant.
14798  *
14799  * Fork - LGPL
14800  * <script type="text/javascript">
14801  */
14802
14803  
14804 /**
14805  * @class Roo.KeyMap
14806  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14807  * The constructor accepts the same config object as defined by {@link #addBinding}.
14808  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14809  * combination it will call the function with this signature (if the match is a multi-key
14810  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14811  * A KeyMap can also handle a string representation of keys.<br />
14812  * Usage:
14813  <pre><code>
14814 // map one key by key code
14815 var map = new Roo.KeyMap("my-element", {
14816     key: 13, // or Roo.EventObject.ENTER
14817     fn: myHandler,
14818     scope: myObject
14819 });
14820
14821 // map multiple keys to one action by string
14822 var map = new Roo.KeyMap("my-element", {
14823     key: "a\r\n\t",
14824     fn: myHandler,
14825     scope: myObject
14826 });
14827
14828 // map multiple keys to multiple actions by strings and array of codes
14829 var map = new Roo.KeyMap("my-element", [
14830     {
14831         key: [10,13],
14832         fn: function(){ alert("Return was pressed"); }
14833     }, {
14834         key: "abc",
14835         fn: function(){ alert('a, b or c was pressed'); }
14836     }, {
14837         key: "\t",
14838         ctrl:true,
14839         shift:true,
14840         fn: function(){ alert('Control + shift + tab was pressed.'); }
14841     }
14842 ]);
14843 </code></pre>
14844  * <b>Note: A KeyMap starts enabled</b>
14845  * @constructor
14846  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14847  * @param {Object} config The config (see {@link #addBinding})
14848  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14849  */
14850 Roo.KeyMap = function(el, config, eventName){
14851     this.el  = Roo.get(el);
14852     this.eventName = eventName || "keydown";
14853     this.bindings = [];
14854     if(config){
14855         this.addBinding(config);
14856     }
14857     this.enable();
14858 };
14859
14860 Roo.KeyMap.prototype = {
14861     /**
14862      * True to stop the event from bubbling and prevent the default browser action if the
14863      * key was handled by the KeyMap (defaults to false)
14864      * @type Boolean
14865      */
14866     stopEvent : false,
14867
14868     /**
14869      * Add a new binding to this KeyMap. The following config object properties are supported:
14870      * <pre>
14871 Property    Type             Description
14872 ----------  ---------------  ----------------------------------------------------------------------
14873 key         String/Array     A single keycode or an array of keycodes to handle
14874 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14875 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14876 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14877 fn          Function         The function to call when KeyMap finds the expected key combination
14878 scope       Object           The scope of the callback function
14879 </pre>
14880      *
14881      * Usage:
14882      * <pre><code>
14883 // Create a KeyMap
14884 var map = new Roo.KeyMap(document, {
14885     key: Roo.EventObject.ENTER,
14886     fn: handleKey,
14887     scope: this
14888 });
14889
14890 //Add a new binding to the existing KeyMap later
14891 map.addBinding({
14892     key: 'abc',
14893     shift: true,
14894     fn: handleKey,
14895     scope: this
14896 });
14897 </code></pre>
14898      * @param {Object/Array} config A single KeyMap config or an array of configs
14899      */
14900         addBinding : function(config){
14901         if(config instanceof Array){
14902             for(var i = 0, len = config.length; i < len; i++){
14903                 this.addBinding(config[i]);
14904             }
14905             return;
14906         }
14907         var keyCode = config.key,
14908             shift = config.shift, 
14909             ctrl = config.ctrl, 
14910             alt = config.alt,
14911             fn = config.fn,
14912             scope = config.scope;
14913         if(typeof keyCode == "string"){
14914             var ks = [];
14915             var keyString = keyCode.toUpperCase();
14916             for(var j = 0, len = keyString.length; j < len; j++){
14917                 ks.push(keyString.charCodeAt(j));
14918             }
14919             keyCode = ks;
14920         }
14921         var keyArray = keyCode instanceof Array;
14922         var handler = function(e){
14923             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14924                 var k = e.getKey();
14925                 if(keyArray){
14926                     for(var i = 0, len = keyCode.length; i < len; i++){
14927                         if(keyCode[i] == k){
14928                           if(this.stopEvent){
14929                               e.stopEvent();
14930                           }
14931                           fn.call(scope || window, k, e);
14932                           return;
14933                         }
14934                     }
14935                 }else{
14936                     if(k == keyCode){
14937                         if(this.stopEvent){
14938                            e.stopEvent();
14939                         }
14940                         fn.call(scope || window, k, e);
14941                     }
14942                 }
14943             }
14944         };
14945         this.bindings.push(handler);  
14946         },
14947
14948     /**
14949      * Shorthand for adding a single key listener
14950      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14951      * following options:
14952      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14953      * @param {Function} fn The function to call
14954      * @param {Object} scope (optional) The scope of the function
14955      */
14956     on : function(key, fn, scope){
14957         var keyCode, shift, ctrl, alt;
14958         if(typeof key == "object" && !(key instanceof Array)){
14959             keyCode = key.key;
14960             shift = key.shift;
14961             ctrl = key.ctrl;
14962             alt = key.alt;
14963         }else{
14964             keyCode = key;
14965         }
14966         this.addBinding({
14967             key: keyCode,
14968             shift: shift,
14969             ctrl: ctrl,
14970             alt: alt,
14971             fn: fn,
14972             scope: scope
14973         })
14974     },
14975
14976     // private
14977     handleKeyDown : function(e){
14978             if(this.enabled){ //just in case
14979             var b = this.bindings;
14980             for(var i = 0, len = b.length; i < len; i++){
14981                 b[i].call(this, e);
14982             }
14983             }
14984         },
14985         
14986         /**
14987          * Returns true if this KeyMap is enabled
14988          * @return {Boolean} 
14989          */
14990         isEnabled : function(){
14991             return this.enabled;  
14992         },
14993         
14994         /**
14995          * Enables this KeyMap
14996          */
14997         enable: function(){
14998                 if(!this.enabled){
14999                     this.el.on(this.eventName, this.handleKeyDown, this);
15000                     this.enabled = true;
15001                 }
15002         },
15003
15004         /**
15005          * Disable this KeyMap
15006          */
15007         disable: function(){
15008                 if(this.enabled){
15009                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15010                     this.enabled = false;
15011                 }
15012         }
15013 };/*
15014  * Based on:
15015  * Ext JS Library 1.1.1
15016  * Copyright(c) 2006-2007, Ext JS, LLC.
15017  *
15018  * Originally Released Under LGPL - original licence link has changed is not relivant.
15019  *
15020  * Fork - LGPL
15021  * <script type="text/javascript">
15022  */
15023
15024  
15025 /**
15026  * @class Roo.util.TextMetrics
15027  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15028  * wide, in pixels, a given block of text will be.
15029  * @singleton
15030  */
15031 Roo.util.TextMetrics = function(){
15032     var shared;
15033     return {
15034         /**
15035          * Measures the size of the specified text
15036          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15037          * that can affect the size of the rendered text
15038          * @param {String} text The text to measure
15039          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15040          * in order to accurately measure the text height
15041          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15042          */
15043         measure : function(el, text, fixedWidth){
15044             if(!shared){
15045                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15046             }
15047             shared.bind(el);
15048             shared.setFixedWidth(fixedWidth || 'auto');
15049             return shared.getSize(text);
15050         },
15051
15052         /**
15053          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15054          * the overhead of multiple calls to initialize the style properties on each measurement.
15055          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15056          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15057          * in order to accurately measure the text height
15058          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15059          */
15060         createInstance : function(el, fixedWidth){
15061             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15062         }
15063     };
15064 }();
15065
15066  
15067
15068 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15069     var ml = new Roo.Element(document.createElement('div'));
15070     document.body.appendChild(ml.dom);
15071     ml.position('absolute');
15072     ml.setLeftTop(-1000, -1000);
15073     ml.hide();
15074
15075     if(fixedWidth){
15076         ml.setWidth(fixedWidth);
15077     }
15078      
15079     var instance = {
15080         /**
15081          * Returns the size of the specified text based on the internal element's style and width properties
15082          * @memberOf Roo.util.TextMetrics.Instance#
15083          * @param {String} text The text to measure
15084          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15085          */
15086         getSize : function(text){
15087             ml.update(text);
15088             var s = ml.getSize();
15089             ml.update('');
15090             return s;
15091         },
15092
15093         /**
15094          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15095          * that can affect the size of the rendered text
15096          * @memberOf Roo.util.TextMetrics.Instance#
15097          * @param {String/HTMLElement} el The element, dom node or id
15098          */
15099         bind : function(el){
15100             ml.setStyle(
15101                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15102             );
15103         },
15104
15105         /**
15106          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15107          * to set a fixed width in order to accurately measure the text height.
15108          * @memberOf Roo.util.TextMetrics.Instance#
15109          * @param {Number} width The width to set on the element
15110          */
15111         setFixedWidth : function(width){
15112             ml.setWidth(width);
15113         },
15114
15115         /**
15116          * Returns the measured width of the specified text
15117          * @memberOf Roo.util.TextMetrics.Instance#
15118          * @param {String} text The text to measure
15119          * @return {Number} width The width in pixels
15120          */
15121         getWidth : function(text){
15122             ml.dom.style.width = 'auto';
15123             return this.getSize(text).width;
15124         },
15125
15126         /**
15127          * Returns the measured height of the specified text.  For multiline text, be sure to call
15128          * {@link #setFixedWidth} if necessary.
15129          * @memberOf Roo.util.TextMetrics.Instance#
15130          * @param {String} text The text to measure
15131          * @return {Number} height The height in pixels
15132          */
15133         getHeight : function(text){
15134             return this.getSize(text).height;
15135         }
15136     };
15137
15138     instance.bind(bindTo);
15139
15140     return instance;
15141 };
15142
15143 // backwards compat
15144 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15145  * Based on:
15146  * Ext JS Library 1.1.1
15147  * Copyright(c) 2006-2007, Ext JS, LLC.
15148  *
15149  * Originally Released Under LGPL - original licence link has changed is not relivant.
15150  *
15151  * Fork - LGPL
15152  * <script type="text/javascript">
15153  */
15154
15155 /**
15156  * @class Roo.state.Provider
15157  * Abstract base class for state provider implementations. This class provides methods
15158  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15159  * Provider interface.
15160  */
15161 Roo.state.Provider = function(){
15162     /**
15163      * @event statechange
15164      * Fires when a state change occurs.
15165      * @param {Provider} this This state provider
15166      * @param {String} key The state key which was changed
15167      * @param {String} value The encoded value for the state
15168      */
15169     this.addEvents({
15170         "statechange": true
15171     });
15172     this.state = {};
15173     Roo.state.Provider.superclass.constructor.call(this);
15174 };
15175 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15176     /**
15177      * Returns the current value for a key
15178      * @param {String} name The key name
15179      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15180      * @return {Mixed} The state data
15181      */
15182     get : function(name, defaultValue){
15183         return typeof this.state[name] == "undefined" ?
15184             defaultValue : this.state[name];
15185     },
15186     
15187     /**
15188      * Clears a value from the state
15189      * @param {String} name The key name
15190      */
15191     clear : function(name){
15192         delete this.state[name];
15193         this.fireEvent("statechange", this, name, null);
15194     },
15195     
15196     /**
15197      * Sets the value for a key
15198      * @param {String} name The key name
15199      * @param {Mixed} value The value to set
15200      */
15201     set : function(name, value){
15202         this.state[name] = value;
15203         this.fireEvent("statechange", this, name, value);
15204     },
15205     
15206     /**
15207      * Decodes a string previously encoded with {@link #encodeValue}.
15208      * @param {String} value The value to decode
15209      * @return {Mixed} The decoded value
15210      */
15211     decodeValue : function(cookie){
15212         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15213         var matches = re.exec(unescape(cookie));
15214         if(!matches || !matches[1]) {
15215             return; // non state cookie
15216         }
15217         var type = matches[1];
15218         var v = matches[2];
15219         switch(type){
15220             case "n":
15221                 return parseFloat(v);
15222             case "d":
15223                 return new Date(Date.parse(v));
15224             case "b":
15225                 return (v == "1");
15226             case "a":
15227                 var all = [];
15228                 var values = v.split("^");
15229                 for(var i = 0, len = values.length; i < len; i++){
15230                     all.push(this.decodeValue(values[i]));
15231                 }
15232                 return all;
15233            case "o":
15234                 var all = {};
15235                 var values = v.split("^");
15236                 for(var i = 0, len = values.length; i < len; i++){
15237                     var kv = values[i].split("=");
15238                     all[kv[0]] = this.decodeValue(kv[1]);
15239                 }
15240                 return all;
15241            default:
15242                 return v;
15243         }
15244     },
15245     
15246     /**
15247      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15248      * @param {Mixed} value The value to encode
15249      * @return {String} The encoded value
15250      */
15251     encodeValue : function(v){
15252         var enc;
15253         if(typeof v == "number"){
15254             enc = "n:" + v;
15255         }else if(typeof v == "boolean"){
15256             enc = "b:" + (v ? "1" : "0");
15257         }else if(v instanceof Date){
15258             enc = "d:" + v.toGMTString();
15259         }else if(v instanceof Array){
15260             var flat = "";
15261             for(var i = 0, len = v.length; i < len; i++){
15262                 flat += this.encodeValue(v[i]);
15263                 if(i != len-1) {
15264                     flat += "^";
15265                 }
15266             }
15267             enc = "a:" + flat;
15268         }else if(typeof v == "object"){
15269             var flat = "";
15270             for(var key in v){
15271                 if(typeof v[key] != "function"){
15272                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15273                 }
15274             }
15275             enc = "o:" + flat.substring(0, flat.length-1);
15276         }else{
15277             enc = "s:" + v;
15278         }
15279         return escape(enc);        
15280     }
15281 });
15282
15283 /*
15284  * Based on:
15285  * Ext JS Library 1.1.1
15286  * Copyright(c) 2006-2007, Ext JS, LLC.
15287  *
15288  * Originally Released Under LGPL - original licence link has changed is not relivant.
15289  *
15290  * Fork - LGPL
15291  * <script type="text/javascript">
15292  */
15293 /**
15294  * @class Roo.state.Manager
15295  * This is the global state manager. By default all components that are "state aware" check this class
15296  * for state information if you don't pass them a custom state provider. In order for this class
15297  * to be useful, it must be initialized with a provider when your application initializes.
15298  <pre><code>
15299 // in your initialization function
15300 init : function(){
15301    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15302    ...
15303    // supposed you have a {@link Roo.BorderLayout}
15304    var layout = new Roo.BorderLayout(...);
15305    layout.restoreState();
15306    // or a {Roo.BasicDialog}
15307    var dialog = new Roo.BasicDialog(...);
15308    dialog.restoreState();
15309  </code></pre>
15310  * @singleton
15311  */
15312 Roo.state.Manager = function(){
15313     var provider = new Roo.state.Provider();
15314     
15315     return {
15316         /**
15317          * Configures the default state provider for your application
15318          * @param {Provider} stateProvider The state provider to set
15319          */
15320         setProvider : function(stateProvider){
15321             provider = stateProvider;
15322         },
15323         
15324         /**
15325          * Returns the current value for a key
15326          * @param {String} name The key name
15327          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15328          * @return {Mixed} The state data
15329          */
15330         get : function(key, defaultValue){
15331             return provider.get(key, defaultValue);
15332         },
15333         
15334         /**
15335          * Sets the value for a key
15336          * @param {String} name The key name
15337          * @param {Mixed} value The state data
15338          */
15339          set : function(key, value){
15340             provider.set(key, value);
15341         },
15342         
15343         /**
15344          * Clears a value from the state
15345          * @param {String} name The key name
15346          */
15347         clear : function(key){
15348             provider.clear(key);
15349         },
15350         
15351         /**
15352          * Gets the currently configured state provider
15353          * @return {Provider} The state provider
15354          */
15355         getProvider : function(){
15356             return provider;
15357         }
15358     };
15359 }();
15360 /*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370 /**
15371  * @class Roo.state.CookieProvider
15372  * @extends Roo.state.Provider
15373  * The default Provider implementation which saves state via cookies.
15374  * <br />Usage:
15375  <pre><code>
15376    var cp = new Roo.state.CookieProvider({
15377        path: "/cgi-bin/",
15378        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15379        domain: "roojs.com"
15380    })
15381    Roo.state.Manager.setProvider(cp);
15382  </code></pre>
15383  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15384  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15385  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15386  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15387  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15388  * domain the page is running on including the 'www' like 'www.roojs.com')
15389  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15390  * @constructor
15391  * Create a new CookieProvider
15392  * @param {Object} config The configuration object
15393  */
15394 Roo.state.CookieProvider = function(config){
15395     Roo.state.CookieProvider.superclass.constructor.call(this);
15396     this.path = "/";
15397     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15398     this.domain = null;
15399     this.secure = false;
15400     Roo.apply(this, config);
15401     this.state = this.readCookies();
15402 };
15403
15404 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15405     // private
15406     set : function(name, value){
15407         if(typeof value == "undefined" || value === null){
15408             this.clear(name);
15409             return;
15410         }
15411         this.setCookie(name, value);
15412         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15413     },
15414
15415     // private
15416     clear : function(name){
15417         this.clearCookie(name);
15418         Roo.state.CookieProvider.superclass.clear.call(this, name);
15419     },
15420
15421     // private
15422     readCookies : function(){
15423         var cookies = {};
15424         var c = document.cookie + ";";
15425         var re = /\s?(.*?)=(.*?);/g;
15426         var matches;
15427         while((matches = re.exec(c)) != null){
15428             var name = matches[1];
15429             var value = matches[2];
15430             if(name && name.substring(0,3) == "ys-"){
15431                 cookies[name.substr(3)] = this.decodeValue(value);
15432             }
15433         }
15434         return cookies;
15435     },
15436
15437     // private
15438     setCookie : function(name, value){
15439         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15440            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15441            ((this.path == null) ? "" : ("; path=" + this.path)) +
15442            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15443            ((this.secure == true) ? "; secure" : "");
15444     },
15445
15446     // private
15447     clearCookie : function(name){
15448         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15449            ((this.path == null) ? "" : ("; path=" + this.path)) +
15450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15451            ((this.secure == true) ? "; secure" : "");
15452     }
15453 });/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463  
15464
15465 /**
15466  * @class Roo.ComponentMgr
15467  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15468  * @singleton
15469  */
15470 Roo.ComponentMgr = function(){
15471     var all = new Roo.util.MixedCollection();
15472
15473     return {
15474         /**
15475          * Registers a component.
15476          * @param {Roo.Component} c The component
15477          */
15478         register : function(c){
15479             all.add(c);
15480         },
15481
15482         /**
15483          * Unregisters a component.
15484          * @param {Roo.Component} c The component
15485          */
15486         unregister : function(c){
15487             all.remove(c);
15488         },
15489
15490         /**
15491          * Returns a component by id
15492          * @param {String} id The component id
15493          */
15494         get : function(id){
15495             return all.get(id);
15496         },
15497
15498         /**
15499          * Registers a function that will be called when a specified component is added to ComponentMgr
15500          * @param {String} id The component id
15501          * @param {Funtction} fn The callback function
15502          * @param {Object} scope The scope of the callback
15503          */
15504         onAvailable : function(id, fn, scope){
15505             all.on("add", function(index, o){
15506                 if(o.id == id){
15507                     fn.call(scope || o, o);
15508                     all.un("add", fn, scope);
15509                 }
15510             });
15511         }
15512     };
15513 }();/*
15514  * Based on:
15515  * Ext JS Library 1.1.1
15516  * Copyright(c) 2006-2007, Ext JS, LLC.
15517  *
15518  * Originally Released Under LGPL - original licence link has changed is not relivant.
15519  *
15520  * Fork - LGPL
15521  * <script type="text/javascript">
15522  */
15523  
15524 /**
15525  * @class Roo.Component
15526  * @extends Roo.util.Observable
15527  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15528  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15529  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15530  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15531  * All visual components (widgets) that require rendering into a layout should subclass Component.
15532  * @constructor
15533  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15534  * 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
15535  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15536  */
15537 Roo.Component = function(config){
15538     config = config || {};
15539     if(config.tagName || config.dom || typeof config == "string"){ // element object
15540         config = {el: config, id: config.id || config};
15541     }
15542     this.initialConfig = config;
15543
15544     Roo.apply(this, config);
15545     this.addEvents({
15546         /**
15547          * @event disable
15548          * Fires after the component is disabled.
15549              * @param {Roo.Component} this
15550              */
15551         disable : true,
15552         /**
15553          * @event enable
15554          * Fires after the component is enabled.
15555              * @param {Roo.Component} this
15556              */
15557         enable : true,
15558         /**
15559          * @event beforeshow
15560          * Fires before the component is shown.  Return false to stop the show.
15561              * @param {Roo.Component} this
15562              */
15563         beforeshow : true,
15564         /**
15565          * @event show
15566          * Fires after the component is shown.
15567              * @param {Roo.Component} this
15568              */
15569         show : true,
15570         /**
15571          * @event beforehide
15572          * Fires before the component is hidden. Return false to stop the hide.
15573              * @param {Roo.Component} this
15574              */
15575         beforehide : true,
15576         /**
15577          * @event hide
15578          * Fires after the component is hidden.
15579              * @param {Roo.Component} this
15580              */
15581         hide : true,
15582         /**
15583          * @event beforerender
15584          * Fires before the component is rendered. Return false to stop the render.
15585              * @param {Roo.Component} this
15586              */
15587         beforerender : true,
15588         /**
15589          * @event render
15590          * Fires after the component is rendered.
15591              * @param {Roo.Component} this
15592              */
15593         render : true,
15594         /**
15595          * @event beforedestroy
15596          * Fires before the component is destroyed. Return false to stop the destroy.
15597              * @param {Roo.Component} this
15598              */
15599         beforedestroy : true,
15600         /**
15601          * @event destroy
15602          * Fires after the component is destroyed.
15603              * @param {Roo.Component} this
15604              */
15605         destroy : true
15606     });
15607     if(!this.id){
15608         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15609     }
15610     Roo.ComponentMgr.register(this);
15611     Roo.Component.superclass.constructor.call(this);
15612     this.initComponent();
15613     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15614         this.render(this.renderTo);
15615         delete this.renderTo;
15616     }
15617 };
15618
15619 /** @private */
15620 Roo.Component.AUTO_ID = 1000;
15621
15622 Roo.extend(Roo.Component, Roo.util.Observable, {
15623     /**
15624      * @scope Roo.Component.prototype
15625      * @type {Boolean}
15626      * true if this component is hidden. Read-only.
15627      */
15628     hidden : false,
15629     /**
15630      * @type {Boolean}
15631      * true if this component is disabled. Read-only.
15632      */
15633     disabled : false,
15634     /**
15635      * @type {Boolean}
15636      * true if this component has been rendered. Read-only.
15637      */
15638     rendered : false,
15639     
15640     /** @cfg {String} disableClass
15641      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15642      */
15643     disabledClass : "x-item-disabled",
15644         /** @cfg {Boolean} allowDomMove
15645          * Whether the component can move the Dom node when rendering (defaults to true).
15646          */
15647     allowDomMove : true,
15648     /** @cfg {String} hideMode (display|visibility)
15649      * How this component should hidden. Supported values are
15650      * "visibility" (css visibility), "offsets" (negative offset position) and
15651      * "display" (css display) - defaults to "display".
15652      */
15653     hideMode: 'display',
15654
15655     /** @private */
15656     ctype : "Roo.Component",
15657
15658     /**
15659      * @cfg {String} actionMode 
15660      * which property holds the element that used for  hide() / show() / disable() / enable()
15661      * default is 'el' for forms you probably want to set this to fieldEl 
15662      */
15663     actionMode : "el",
15664
15665     /** @private */
15666     getActionEl : function(){
15667         return this[this.actionMode];
15668     },
15669
15670     initComponent : Roo.emptyFn,
15671     /**
15672      * If this is a lazy rendering component, render it to its container element.
15673      * @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.
15674      */
15675     render : function(container, position){
15676         
15677         if(this.rendered){
15678             return this;
15679         }
15680         
15681         if(this.fireEvent("beforerender", this) === false){
15682             return false;
15683         }
15684         
15685         if(!container && this.el){
15686             this.el = Roo.get(this.el);
15687             container = this.el.dom.parentNode;
15688             this.allowDomMove = false;
15689         }
15690         this.container = Roo.get(container);
15691         this.rendered = true;
15692         if(position !== undefined){
15693             if(typeof position == 'number'){
15694                 position = this.container.dom.childNodes[position];
15695             }else{
15696                 position = Roo.getDom(position);
15697             }
15698         }
15699         this.onRender(this.container, position || null);
15700         if(this.cls){
15701             this.el.addClass(this.cls);
15702             delete this.cls;
15703         }
15704         if(this.style){
15705             this.el.applyStyles(this.style);
15706             delete this.style;
15707         }
15708         this.fireEvent("render", this);
15709         this.afterRender(this.container);
15710         if(this.hidden){
15711             this.hide();
15712         }
15713         if(this.disabled){
15714             this.disable();
15715         }
15716
15717         return this;
15718         
15719     },
15720
15721     /** @private */
15722     // default function is not really useful
15723     onRender : function(ct, position){
15724         if(this.el){
15725             this.el = Roo.get(this.el);
15726             if(this.allowDomMove !== false){
15727                 ct.dom.insertBefore(this.el.dom, position);
15728             }
15729         }
15730     },
15731
15732     /** @private */
15733     getAutoCreate : function(){
15734         var cfg = typeof this.autoCreate == "object" ?
15735                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15736         if(this.id && !cfg.id){
15737             cfg.id = this.id;
15738         }
15739         return cfg;
15740     },
15741
15742     /** @private */
15743     afterRender : Roo.emptyFn,
15744
15745     /**
15746      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15747      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15748      */
15749     destroy : function(){
15750         if(this.fireEvent("beforedestroy", this) !== false){
15751             this.purgeListeners();
15752             this.beforeDestroy();
15753             if(this.rendered){
15754                 this.el.removeAllListeners();
15755                 this.el.remove();
15756                 if(this.actionMode == "container"){
15757                     this.container.remove();
15758                 }
15759             }
15760             this.onDestroy();
15761             Roo.ComponentMgr.unregister(this);
15762             this.fireEvent("destroy", this);
15763         }
15764     },
15765
15766         /** @private */
15767     beforeDestroy : function(){
15768
15769     },
15770
15771         /** @private */
15772         onDestroy : function(){
15773
15774     },
15775
15776     /**
15777      * Returns the underlying {@link Roo.Element}.
15778      * @return {Roo.Element} The element
15779      */
15780     getEl : function(){
15781         return this.el;
15782     },
15783
15784     /**
15785      * Returns the id of this component.
15786      * @return {String}
15787      */
15788     getId : function(){
15789         return this.id;
15790     },
15791
15792     /**
15793      * Try to focus this component.
15794      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15795      * @return {Roo.Component} this
15796      */
15797     focus : function(selectText){
15798         if(this.rendered){
15799             this.el.focus();
15800             if(selectText === true){
15801                 this.el.dom.select();
15802             }
15803         }
15804         return this;
15805     },
15806
15807     /** @private */
15808     blur : function(){
15809         if(this.rendered){
15810             this.el.blur();
15811         }
15812         return this;
15813     },
15814
15815     /**
15816      * Disable this component.
15817      * @return {Roo.Component} this
15818      */
15819     disable : function(){
15820         if(this.rendered){
15821             this.onDisable();
15822         }
15823         this.disabled = true;
15824         this.fireEvent("disable", this);
15825         return this;
15826     },
15827
15828         // private
15829     onDisable : function(){
15830         this.getActionEl().addClass(this.disabledClass);
15831         this.el.dom.disabled = true;
15832     },
15833
15834     /**
15835      * Enable this component.
15836      * @return {Roo.Component} this
15837      */
15838     enable : function(){
15839         if(this.rendered){
15840             this.onEnable();
15841         }
15842         this.disabled = false;
15843         this.fireEvent("enable", this);
15844         return this;
15845     },
15846
15847         // private
15848     onEnable : function(){
15849         this.getActionEl().removeClass(this.disabledClass);
15850         this.el.dom.disabled = false;
15851     },
15852
15853     /**
15854      * Convenience function for setting disabled/enabled by boolean.
15855      * @param {Boolean} disabled
15856      */
15857     setDisabled : function(disabled){
15858         this[disabled ? "disable" : "enable"]();
15859     },
15860
15861     /**
15862      * Show this component.
15863      * @return {Roo.Component} this
15864      */
15865     show: function(){
15866         if(this.fireEvent("beforeshow", this) !== false){
15867             this.hidden = false;
15868             if(this.rendered){
15869                 this.onShow();
15870             }
15871             this.fireEvent("show", this);
15872         }
15873         return this;
15874     },
15875
15876     // private
15877     onShow : function(){
15878         var ae = this.getActionEl();
15879         if(this.hideMode == 'visibility'){
15880             ae.dom.style.visibility = "visible";
15881         }else if(this.hideMode == 'offsets'){
15882             ae.removeClass('x-hidden');
15883         }else{
15884             ae.dom.style.display = "";
15885         }
15886     },
15887
15888     /**
15889      * Hide this component.
15890      * @return {Roo.Component} this
15891      */
15892     hide: function(){
15893         if(this.fireEvent("beforehide", this) !== false){
15894             this.hidden = true;
15895             if(this.rendered){
15896                 this.onHide();
15897             }
15898             this.fireEvent("hide", this);
15899         }
15900         return this;
15901     },
15902
15903     // private
15904     onHide : function(){
15905         var ae = this.getActionEl();
15906         if(this.hideMode == 'visibility'){
15907             ae.dom.style.visibility = "hidden";
15908         }else if(this.hideMode == 'offsets'){
15909             ae.addClass('x-hidden');
15910         }else{
15911             ae.dom.style.display = "none";
15912         }
15913     },
15914
15915     /**
15916      * Convenience function to hide or show this component by boolean.
15917      * @param {Boolean} visible True to show, false to hide
15918      * @return {Roo.Component} this
15919      */
15920     setVisible: function(visible){
15921         if(visible) {
15922             this.show();
15923         }else{
15924             this.hide();
15925         }
15926         return this;
15927     },
15928
15929     /**
15930      * Returns true if this component is visible.
15931      */
15932     isVisible : function(){
15933         return this.getActionEl().isVisible();
15934     },
15935
15936     cloneConfig : function(overrides){
15937         overrides = overrides || {};
15938         var id = overrides.id || Roo.id();
15939         var cfg = Roo.applyIf(overrides, this.initialConfig);
15940         cfg.id = id; // prevent dup id
15941         return new this.constructor(cfg);
15942     }
15943 });/*
15944  * Based on:
15945  * Ext JS Library 1.1.1
15946  * Copyright(c) 2006-2007, Ext JS, LLC.
15947  *
15948  * Originally Released Under LGPL - original licence link has changed is not relivant.
15949  *
15950  * Fork - LGPL
15951  * <script type="text/javascript">
15952  */
15953
15954 /**
15955  * @class Roo.BoxComponent
15956  * @extends Roo.Component
15957  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15958  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15959  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15960  * layout containers.
15961  * @constructor
15962  * @param {Roo.Element/String/Object} config The configuration options.
15963  */
15964 Roo.BoxComponent = function(config){
15965     Roo.Component.call(this, config);
15966     this.addEvents({
15967         /**
15968          * @event resize
15969          * Fires after the component is resized.
15970              * @param {Roo.Component} this
15971              * @param {Number} adjWidth The box-adjusted width that was set
15972              * @param {Number} adjHeight The box-adjusted height that was set
15973              * @param {Number} rawWidth The width that was originally specified
15974              * @param {Number} rawHeight The height that was originally specified
15975              */
15976         resize : true,
15977         /**
15978          * @event move
15979          * Fires after the component is moved.
15980              * @param {Roo.Component} this
15981              * @param {Number} x The new x position
15982              * @param {Number} y The new y position
15983              */
15984         move : true
15985     });
15986 };
15987
15988 Roo.extend(Roo.BoxComponent, Roo.Component, {
15989     // private, set in afterRender to signify that the component has been rendered
15990     boxReady : false,
15991     // private, used to defer height settings to subclasses
15992     deferHeight: false,
15993     /** @cfg {Number} width
15994      * width (optional) size of component
15995      */
15996      /** @cfg {Number} height
15997      * height (optional) size of component
15998      */
15999      
16000     /**
16001      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16002      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16003      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16004      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16005      * @return {Roo.BoxComponent} this
16006      */
16007     setSize : function(w, h){
16008         // support for standard size objects
16009         if(typeof w == 'object'){
16010             h = w.height;
16011             w = w.width;
16012         }
16013         // not rendered
16014         if(!this.boxReady){
16015             this.width = w;
16016             this.height = h;
16017             return this;
16018         }
16019
16020         // prevent recalcs when not needed
16021         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16022             return this;
16023         }
16024         this.lastSize = {width: w, height: h};
16025
16026         var adj = this.adjustSize(w, h);
16027         var aw = adj.width, ah = adj.height;
16028         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16029             var rz = this.getResizeEl();
16030             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16031                 rz.setSize(aw, ah);
16032             }else if(!this.deferHeight && ah !== undefined){
16033                 rz.setHeight(ah);
16034             }else if(aw !== undefined){
16035                 rz.setWidth(aw);
16036             }
16037             this.onResize(aw, ah, w, h);
16038             this.fireEvent('resize', this, aw, ah, w, h);
16039         }
16040         return this;
16041     },
16042
16043     /**
16044      * Gets the current size of the component's underlying element.
16045      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16046      */
16047     getSize : function(){
16048         return this.el.getSize();
16049     },
16050
16051     /**
16052      * Gets the current XY position of the component's underlying element.
16053      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16054      * @return {Array} The XY position of the element (e.g., [100, 200])
16055      */
16056     getPosition : function(local){
16057         if(local === true){
16058             return [this.el.getLeft(true), this.el.getTop(true)];
16059         }
16060         return this.xy || this.el.getXY();
16061     },
16062
16063     /**
16064      * Gets the current box measurements of the component's underlying element.
16065      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16066      * @returns {Object} box An object in the format {x, y, width, height}
16067      */
16068     getBox : function(local){
16069         var s = this.el.getSize();
16070         if(local){
16071             s.x = this.el.getLeft(true);
16072             s.y = this.el.getTop(true);
16073         }else{
16074             var xy = this.xy || this.el.getXY();
16075             s.x = xy[0];
16076             s.y = xy[1];
16077         }
16078         return s;
16079     },
16080
16081     /**
16082      * Sets the current box measurements of the component's underlying element.
16083      * @param {Object} box An object in the format {x, y, width, height}
16084      * @returns {Roo.BoxComponent} this
16085      */
16086     updateBox : function(box){
16087         this.setSize(box.width, box.height);
16088         this.setPagePosition(box.x, box.y);
16089         return this;
16090     },
16091
16092     // protected
16093     getResizeEl : function(){
16094         return this.resizeEl || this.el;
16095     },
16096
16097     // protected
16098     getPositionEl : function(){
16099         return this.positionEl || this.el;
16100     },
16101
16102     /**
16103      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16104      * This method fires the move event.
16105      * @param {Number} left The new left
16106      * @param {Number} top The new top
16107      * @returns {Roo.BoxComponent} this
16108      */
16109     setPosition : function(x, y){
16110         this.x = x;
16111         this.y = y;
16112         if(!this.boxReady){
16113             return this;
16114         }
16115         var adj = this.adjustPosition(x, y);
16116         var ax = adj.x, ay = adj.y;
16117
16118         var el = this.getPositionEl();
16119         if(ax !== undefined || ay !== undefined){
16120             if(ax !== undefined && ay !== undefined){
16121                 el.setLeftTop(ax, ay);
16122             }else if(ax !== undefined){
16123                 el.setLeft(ax);
16124             }else if(ay !== undefined){
16125                 el.setTop(ay);
16126             }
16127             this.onPosition(ax, ay);
16128             this.fireEvent('move', this, ax, ay);
16129         }
16130         return this;
16131     },
16132
16133     /**
16134      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16135      * This method fires the move event.
16136      * @param {Number} x The new x position
16137      * @param {Number} y The new y position
16138      * @returns {Roo.BoxComponent} this
16139      */
16140     setPagePosition : function(x, y){
16141         this.pageX = x;
16142         this.pageY = y;
16143         if(!this.boxReady){
16144             return;
16145         }
16146         if(x === undefined || y === undefined){ // cannot translate undefined points
16147             return;
16148         }
16149         var p = this.el.translatePoints(x, y);
16150         this.setPosition(p.left, p.top);
16151         return this;
16152     },
16153
16154     // private
16155     onRender : function(ct, position){
16156         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16157         if(this.resizeEl){
16158             this.resizeEl = Roo.get(this.resizeEl);
16159         }
16160         if(this.positionEl){
16161             this.positionEl = Roo.get(this.positionEl);
16162         }
16163     },
16164
16165     // private
16166     afterRender : function(){
16167         Roo.BoxComponent.superclass.afterRender.call(this);
16168         this.boxReady = true;
16169         this.setSize(this.width, this.height);
16170         if(this.x || this.y){
16171             this.setPosition(this.x, this.y);
16172         }
16173         if(this.pageX || this.pageY){
16174             this.setPagePosition(this.pageX, this.pageY);
16175         }
16176     },
16177
16178     /**
16179      * Force the component's size to recalculate based on the underlying element's current height and width.
16180      * @returns {Roo.BoxComponent} this
16181      */
16182     syncSize : function(){
16183         delete this.lastSize;
16184         this.setSize(this.el.getWidth(), this.el.getHeight());
16185         return this;
16186     },
16187
16188     /**
16189      * Called after the component is resized, this method is empty by default but can be implemented by any
16190      * subclass that needs to perform custom logic after a resize occurs.
16191      * @param {Number} adjWidth The box-adjusted width that was set
16192      * @param {Number} adjHeight The box-adjusted height that was set
16193      * @param {Number} rawWidth The width that was originally specified
16194      * @param {Number} rawHeight The height that was originally specified
16195      */
16196     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16197
16198     },
16199
16200     /**
16201      * Called after the component is moved, this method is empty by default but can be implemented by any
16202      * subclass that needs to perform custom logic after a move occurs.
16203      * @param {Number} x The new x position
16204      * @param {Number} y The new y position
16205      */
16206     onPosition : function(x, y){
16207
16208     },
16209
16210     // private
16211     adjustSize : function(w, h){
16212         if(this.autoWidth){
16213             w = 'auto';
16214         }
16215         if(this.autoHeight){
16216             h = 'auto';
16217         }
16218         return {width : w, height: h};
16219     },
16220
16221     // private
16222     adjustPosition : function(x, y){
16223         return {x : x, y: y};
16224     }
16225 });/*
16226  * Based on:
16227  * Ext JS Library 1.1.1
16228  * Copyright(c) 2006-2007, Ext JS, LLC.
16229  *
16230  * Originally Released Under LGPL - original licence link has changed is not relivant.
16231  *
16232  * Fork - LGPL
16233  * <script type="text/javascript">
16234  */
16235  (function(){ 
16236 /**
16237  * @class Roo.Layer
16238  * @extends Roo.Element
16239  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16240  * automatic maintaining of shadow/shim positions.
16241  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16242  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16243  * you can pass a string with a CSS class name. False turns off the shadow.
16244  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16245  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16246  * @cfg {String} cls CSS class to add to the element
16247  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16248  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16249  * @constructor
16250  * @param {Object} config An object with config options.
16251  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16252  */
16253
16254 Roo.Layer = function(config, existingEl){
16255     config = config || {};
16256     var dh = Roo.DomHelper;
16257     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16258     if(existingEl){
16259         this.dom = Roo.getDom(existingEl);
16260     }
16261     if(!this.dom){
16262         var o = config.dh || {tag: "div", cls: "x-layer"};
16263         this.dom = dh.append(pel, o);
16264     }
16265     if(config.cls){
16266         this.addClass(config.cls);
16267     }
16268     this.constrain = config.constrain !== false;
16269     this.visibilityMode = Roo.Element.VISIBILITY;
16270     if(config.id){
16271         this.id = this.dom.id = config.id;
16272     }else{
16273         this.id = Roo.id(this.dom);
16274     }
16275     this.zindex = config.zindex || this.getZIndex();
16276     this.position("absolute", this.zindex);
16277     if(config.shadow){
16278         this.shadowOffset = config.shadowOffset || 4;
16279         this.shadow = new Roo.Shadow({
16280             offset : this.shadowOffset,
16281             mode : config.shadow
16282         });
16283     }else{
16284         this.shadowOffset = 0;
16285     }
16286     this.useShim = config.shim !== false && Roo.useShims;
16287     this.useDisplay = config.useDisplay;
16288     this.hide();
16289 };
16290
16291 var supr = Roo.Element.prototype;
16292
16293 // shims are shared among layer to keep from having 100 iframes
16294 var shims = [];
16295
16296 Roo.extend(Roo.Layer, Roo.Element, {
16297
16298     getZIndex : function(){
16299         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16300     },
16301
16302     getShim : function(){
16303         if(!this.useShim){
16304             return null;
16305         }
16306         if(this.shim){
16307             return this.shim;
16308         }
16309         var shim = shims.shift();
16310         if(!shim){
16311             shim = this.createShim();
16312             shim.enableDisplayMode('block');
16313             shim.dom.style.display = 'none';
16314             shim.dom.style.visibility = 'visible';
16315         }
16316         var pn = this.dom.parentNode;
16317         if(shim.dom.parentNode != pn){
16318             pn.insertBefore(shim.dom, this.dom);
16319         }
16320         shim.setStyle('z-index', this.getZIndex()-2);
16321         this.shim = shim;
16322         return shim;
16323     },
16324
16325     hideShim : function(){
16326         if(this.shim){
16327             this.shim.setDisplayed(false);
16328             shims.push(this.shim);
16329             delete this.shim;
16330         }
16331     },
16332
16333     disableShadow : function(){
16334         if(this.shadow){
16335             this.shadowDisabled = true;
16336             this.shadow.hide();
16337             this.lastShadowOffset = this.shadowOffset;
16338             this.shadowOffset = 0;
16339         }
16340     },
16341
16342     enableShadow : function(show){
16343         if(this.shadow){
16344             this.shadowDisabled = false;
16345             this.shadowOffset = this.lastShadowOffset;
16346             delete this.lastShadowOffset;
16347             if(show){
16348                 this.sync(true);
16349             }
16350         }
16351     },
16352
16353     // private
16354     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16355     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16356     sync : function(doShow){
16357         var sw = this.shadow;
16358         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16359             var sh = this.getShim();
16360
16361             var w = this.getWidth(),
16362                 h = this.getHeight();
16363
16364             var l = this.getLeft(true),
16365                 t = this.getTop(true);
16366
16367             if(sw && !this.shadowDisabled){
16368                 if(doShow && !sw.isVisible()){
16369                     sw.show(this);
16370                 }else{
16371                     sw.realign(l, t, w, h);
16372                 }
16373                 if(sh){
16374                     if(doShow){
16375                        sh.show();
16376                     }
16377                     // fit the shim behind the shadow, so it is shimmed too
16378                     var a = sw.adjusts, s = sh.dom.style;
16379                     s.left = (Math.min(l, l+a.l))+"px";
16380                     s.top = (Math.min(t, t+a.t))+"px";
16381                     s.width = (w+a.w)+"px";
16382                     s.height = (h+a.h)+"px";
16383                 }
16384             }else if(sh){
16385                 if(doShow){
16386                    sh.show();
16387                 }
16388                 sh.setSize(w, h);
16389                 sh.setLeftTop(l, t);
16390             }
16391             
16392         }
16393     },
16394
16395     // private
16396     destroy : function(){
16397         this.hideShim();
16398         if(this.shadow){
16399             this.shadow.hide();
16400         }
16401         this.removeAllListeners();
16402         var pn = this.dom.parentNode;
16403         if(pn){
16404             pn.removeChild(this.dom);
16405         }
16406         Roo.Element.uncache(this.id);
16407     },
16408
16409     remove : function(){
16410         this.destroy();
16411     },
16412
16413     // private
16414     beginUpdate : function(){
16415         this.updating = true;
16416     },
16417
16418     // private
16419     endUpdate : function(){
16420         this.updating = false;
16421         this.sync(true);
16422     },
16423
16424     // private
16425     hideUnders : function(negOffset){
16426         if(this.shadow){
16427             this.shadow.hide();
16428         }
16429         this.hideShim();
16430     },
16431
16432     // private
16433     constrainXY : function(){
16434         if(this.constrain){
16435             var vw = Roo.lib.Dom.getViewWidth(),
16436                 vh = Roo.lib.Dom.getViewHeight();
16437             var s = Roo.get(document).getScroll();
16438
16439             var xy = this.getXY();
16440             var x = xy[0], y = xy[1];   
16441             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16442             // only move it if it needs it
16443             var moved = false;
16444             // first validate right/bottom
16445             if((x + w) > vw+s.left){
16446                 x = vw - w - this.shadowOffset;
16447                 moved = true;
16448             }
16449             if((y + h) > vh+s.top){
16450                 y = vh - h - this.shadowOffset;
16451                 moved = true;
16452             }
16453             // then make sure top/left isn't negative
16454             if(x < s.left){
16455                 x = s.left;
16456                 moved = true;
16457             }
16458             if(y < s.top){
16459                 y = s.top;
16460                 moved = true;
16461             }
16462             if(moved){
16463                 if(this.avoidY){
16464                     var ay = this.avoidY;
16465                     if(y <= ay && (y+h) >= ay){
16466                         y = ay-h-5;   
16467                     }
16468                 }
16469                 xy = [x, y];
16470                 this.storeXY(xy);
16471                 supr.setXY.call(this, xy);
16472                 this.sync();
16473             }
16474         }
16475     },
16476
16477     isVisible : function(){
16478         return this.visible;    
16479     },
16480
16481     // private
16482     showAction : function(){
16483         this.visible = true; // track visibility to prevent getStyle calls
16484         if(this.useDisplay === true){
16485             this.setDisplayed("");
16486         }else if(this.lastXY){
16487             supr.setXY.call(this, this.lastXY);
16488         }else if(this.lastLT){
16489             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16490         }
16491     },
16492
16493     // private
16494     hideAction : function(){
16495         this.visible = false;
16496         if(this.useDisplay === true){
16497             this.setDisplayed(false);
16498         }else{
16499             this.setLeftTop(-10000,-10000);
16500         }
16501     },
16502
16503     // overridden Element method
16504     setVisible : function(v, a, d, c, e){
16505         if(v){
16506             this.showAction();
16507         }
16508         if(a && v){
16509             var cb = function(){
16510                 this.sync(true);
16511                 if(c){
16512                     c();
16513                 }
16514             }.createDelegate(this);
16515             supr.setVisible.call(this, true, true, d, cb, e);
16516         }else{
16517             if(!v){
16518                 this.hideUnders(true);
16519             }
16520             var cb = c;
16521             if(a){
16522                 cb = function(){
16523                     this.hideAction();
16524                     if(c){
16525                         c();
16526                     }
16527                 }.createDelegate(this);
16528             }
16529             supr.setVisible.call(this, v, a, d, cb, e);
16530             if(v){
16531                 this.sync(true);
16532             }else if(!a){
16533                 this.hideAction();
16534             }
16535         }
16536     },
16537
16538     storeXY : function(xy){
16539         delete this.lastLT;
16540         this.lastXY = xy;
16541     },
16542
16543     storeLeftTop : function(left, top){
16544         delete this.lastXY;
16545         this.lastLT = [left, top];
16546     },
16547
16548     // private
16549     beforeFx : function(){
16550         this.beforeAction();
16551         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16552     },
16553
16554     // private
16555     afterFx : function(){
16556         Roo.Layer.superclass.afterFx.apply(this, arguments);
16557         this.sync(this.isVisible());
16558     },
16559
16560     // private
16561     beforeAction : function(){
16562         if(!this.updating && this.shadow){
16563             this.shadow.hide();
16564         }
16565     },
16566
16567     // overridden Element method
16568     setLeft : function(left){
16569         this.storeLeftTop(left, this.getTop(true));
16570         supr.setLeft.apply(this, arguments);
16571         this.sync();
16572     },
16573
16574     setTop : function(top){
16575         this.storeLeftTop(this.getLeft(true), top);
16576         supr.setTop.apply(this, arguments);
16577         this.sync();
16578     },
16579
16580     setLeftTop : function(left, top){
16581         this.storeLeftTop(left, top);
16582         supr.setLeftTop.apply(this, arguments);
16583         this.sync();
16584     },
16585
16586     setXY : function(xy, a, d, c, e){
16587         this.fixDisplay();
16588         this.beforeAction();
16589         this.storeXY(xy);
16590         var cb = this.createCB(c);
16591         supr.setXY.call(this, xy, a, d, cb, e);
16592         if(!a){
16593             cb();
16594         }
16595     },
16596
16597     // private
16598     createCB : function(c){
16599         var el = this;
16600         return function(){
16601             el.constrainXY();
16602             el.sync(true);
16603             if(c){
16604                 c();
16605             }
16606         };
16607     },
16608
16609     // overridden Element method
16610     setX : function(x, a, d, c, e){
16611         this.setXY([x, this.getY()], a, d, c, e);
16612     },
16613
16614     // overridden Element method
16615     setY : function(y, a, d, c, e){
16616         this.setXY([this.getX(), y], a, d, c, e);
16617     },
16618
16619     // overridden Element method
16620     setSize : function(w, h, a, d, c, e){
16621         this.beforeAction();
16622         var cb = this.createCB(c);
16623         supr.setSize.call(this, w, h, a, d, cb, e);
16624         if(!a){
16625             cb();
16626         }
16627     },
16628
16629     // overridden Element method
16630     setWidth : function(w, a, d, c, e){
16631         this.beforeAction();
16632         var cb = this.createCB(c);
16633         supr.setWidth.call(this, w, a, d, cb, e);
16634         if(!a){
16635             cb();
16636         }
16637     },
16638
16639     // overridden Element method
16640     setHeight : function(h, a, d, c, e){
16641         this.beforeAction();
16642         var cb = this.createCB(c);
16643         supr.setHeight.call(this, h, a, d, cb, e);
16644         if(!a){
16645             cb();
16646         }
16647     },
16648
16649     // overridden Element method
16650     setBounds : function(x, y, w, h, a, d, c, e){
16651         this.beforeAction();
16652         var cb = this.createCB(c);
16653         if(!a){
16654             this.storeXY([x, y]);
16655             supr.setXY.call(this, [x, y]);
16656             supr.setSize.call(this, w, h, a, d, cb, e);
16657             cb();
16658         }else{
16659             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16660         }
16661         return this;
16662     },
16663     
16664     /**
16665      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16666      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16667      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16668      * @param {Number} zindex The new z-index to set
16669      * @return {this} The Layer
16670      */
16671     setZIndex : function(zindex){
16672         this.zindex = zindex;
16673         this.setStyle("z-index", zindex + 2);
16674         if(this.shadow){
16675             this.shadow.setZIndex(zindex + 1);
16676         }
16677         if(this.shim){
16678             this.shim.setStyle("z-index", zindex);
16679         }
16680     }
16681 });
16682 })();/*
16683  * Original code for Roojs - LGPL
16684  * <script type="text/javascript">
16685  */
16686  
16687 /**
16688  * @class Roo.XComponent
16689  * A delayed Element creator...
16690  * Or a way to group chunks of interface together.
16691  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16692  *  used in conjunction with XComponent.build() it will create an instance of each element,
16693  *  then call addxtype() to build the User interface.
16694  * 
16695  * Mypart.xyx = new Roo.XComponent({
16696
16697     parent : 'Mypart.xyz', // empty == document.element.!!
16698     order : '001',
16699     name : 'xxxx'
16700     region : 'xxxx'
16701     disabled : function() {} 
16702      
16703     tree : function() { // return an tree of xtype declared components
16704         var MODULE = this;
16705         return 
16706         {
16707             xtype : 'NestedLayoutPanel',
16708             // technicall
16709         }
16710      ]
16711  *})
16712  *
16713  *
16714  * It can be used to build a big heiracy, with parent etc.
16715  * or you can just use this to render a single compoent to a dom element
16716  * MYPART.render(Roo.Element | String(id) | dom_element )
16717  *
16718  *
16719  * Usage patterns.
16720  *
16721  * Classic Roo
16722  *
16723  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16724  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16725  *
16726  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16727  *
16728  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16729  * - if mulitple topModules exist, the last one is defined as the top module.
16730  *
16731  * Embeded Roo
16732  * 
16733  * When the top level or multiple modules are to embedded into a existing HTML page,
16734  * the parent element can container '#id' of the element where the module will be drawn.
16735  *
16736  * Bootstrap Roo
16737  *
16738  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16739  * it relies more on a include mechanism, where sub modules are included into an outer page.
16740  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16741  * 
16742  * Bootstrap Roo Included elements
16743  *
16744  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16745  * hence confusing the component builder as it thinks there are multiple top level elements. 
16746  *
16747  * String Over-ride & Translations
16748  *
16749  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16750  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16751  * are needed. @see Roo.XComponent.overlayString  
16752  * 
16753  * 
16754  * 
16755  * @extends Roo.util.Observable
16756  * @constructor
16757  * @param cfg {Object} configuration of component
16758  * 
16759  */
16760 Roo.XComponent = function(cfg) {
16761     Roo.apply(this, cfg);
16762     this.addEvents({ 
16763         /**
16764              * @event built
16765              * Fires when this the componnt is built
16766              * @param {Roo.XComponent} c the component
16767              */
16768         'built' : true
16769         
16770     });
16771     this.region = this.region || 'center'; // default..
16772     Roo.XComponent.register(this);
16773     this.modules = false;
16774     this.el = false; // where the layout goes..
16775     
16776     
16777 }
16778 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16779     /**
16780      * @property el
16781      * The created element (with Roo.factory())
16782      * @type {Roo.Layout}
16783      */
16784     el  : false,
16785     
16786     /**
16787      * @property el
16788      * for BC  - use el in new code
16789      * @type {Roo.Layout}
16790      */
16791     panel : false,
16792     
16793     /**
16794      * @property layout
16795      * for BC  - use el in new code
16796      * @type {Roo.Layout}
16797      */
16798     layout : false,
16799     
16800      /**
16801      * @cfg {Function|boolean} disabled
16802      * If this module is disabled by some rule, return true from the funtion
16803      */
16804     disabled : false,
16805     
16806     /**
16807      * @cfg {String} parent 
16808      * Name of parent element which it get xtype added to..
16809      */
16810     parent: false,
16811     
16812     /**
16813      * @cfg {String} order
16814      * Used to set the order in which elements are created (usefull for multiple tabs)
16815      */
16816     
16817     order : false,
16818     /**
16819      * @cfg {String} name
16820      * String to display while loading.
16821      */
16822     name : false,
16823     /**
16824      * @cfg {String} region
16825      * Region to render component to (defaults to center)
16826      */
16827     region : 'center',
16828     
16829     /**
16830      * @cfg {Array} items
16831      * A single item array - the first element is the root of the tree..
16832      * It's done this way to stay compatible with the Xtype system...
16833      */
16834     items : false,
16835     
16836     /**
16837      * @property _tree
16838      * The method that retuns the tree of parts that make up this compoennt 
16839      * @type {function}
16840      */
16841     _tree  : false,
16842     
16843      /**
16844      * render
16845      * render element to dom or tree
16846      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16847      */
16848     
16849     render : function(el)
16850     {
16851         
16852         el = el || false;
16853         var hp = this.parent ? 1 : 0;
16854         Roo.debug &&  Roo.log(this);
16855         
16856         var tree = this._tree ? this._tree() : this.tree();
16857
16858         
16859         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16860             // if parent is a '#.....' string, then let's use that..
16861             var ename = this.parent.substr(1);
16862             this.parent = false;
16863             Roo.debug && Roo.log(ename);
16864             switch (ename) {
16865                 case 'bootstrap-body':
16866                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16867                         // this is the BorderLayout standard?
16868                        this.parent = { el : true };
16869                        break;
16870                     }
16871                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16872                         // need to insert stuff...
16873                         this.parent =  {
16874                              el : new Roo.bootstrap.layout.Border({
16875                                  el : document.body, 
16876                      
16877                                  center: {
16878                                     titlebar: false,
16879                                     autoScroll:false,
16880                                     closeOnTab: true,
16881                                     tabPosition: 'top',
16882                                       //resizeTabs: true,
16883                                     alwaysShowTabs: true,
16884                                     hideTabs: false
16885                                      //minTabWidth: 140
16886                                  }
16887                              })
16888                         
16889                          };
16890                          break;
16891                     }
16892                          
16893                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16894                         this.parent = { el :  new  Roo.bootstrap.Body() };
16895                         Roo.debug && Roo.log("setting el to doc body");
16896                          
16897                     } else {
16898                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16899                     }
16900                     break;
16901                 case 'bootstrap':
16902                     this.parent = { el : true};
16903                     // fall through
16904                 default:
16905                     el = Roo.get(ename);
16906                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16907                         this.parent = { el : true};
16908                     }
16909                     
16910                     break;
16911             }
16912                 
16913             
16914             if (!el && !this.parent) {
16915                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16916                 return;
16917             }
16918         }
16919         
16920         Roo.debug && Roo.log("EL:");
16921         Roo.debug && Roo.log(el);
16922         Roo.debug && Roo.log("this.parent.el:");
16923         Roo.debug && Roo.log(this.parent.el);
16924         
16925
16926         // altertive root elements ??? - we need a better way to indicate these.
16927         var is_alt = Roo.XComponent.is_alt ||
16928                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16929                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16930                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16931         
16932         
16933         
16934         if (!this.parent && is_alt) {
16935             //el = Roo.get(document.body);
16936             this.parent = { el : true };
16937         }
16938             
16939             
16940         
16941         if (!this.parent) {
16942             
16943             Roo.debug && Roo.log("no parent - creating one");
16944             
16945             el = el ? Roo.get(el) : false;      
16946             
16947             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16948                 
16949                 this.parent =  {
16950                     el : new Roo.bootstrap.layout.Border({
16951                         el: el || document.body,
16952                     
16953                         center: {
16954                             titlebar: false,
16955                             autoScroll:false,
16956                             closeOnTab: true,
16957                             tabPosition: 'top',
16958                              //resizeTabs: true,
16959                             alwaysShowTabs: false,
16960                             hideTabs: true,
16961                             minTabWidth: 140,
16962                             overflow: 'visible'
16963                          }
16964                      })
16965                 };
16966             } else {
16967             
16968                 // it's a top level one..
16969                 this.parent =  {
16970                     el : new Roo.BorderLayout(el || document.body, {
16971                         center: {
16972                             titlebar: false,
16973                             autoScroll:false,
16974                             closeOnTab: true,
16975                             tabPosition: 'top',
16976                              //resizeTabs: true,
16977                             alwaysShowTabs: el && hp? false :  true,
16978                             hideTabs: el || !hp ? true :  false,
16979                             minTabWidth: 140
16980                          }
16981                     })
16982                 };
16983             }
16984         }
16985         
16986         if (!this.parent.el) {
16987                 // probably an old style ctor, which has been disabled.
16988                 return;
16989
16990         }
16991                 // The 'tree' method is  '_tree now' 
16992             
16993         tree.region = tree.region || this.region;
16994         var is_body = false;
16995         if (this.parent.el === true) {
16996             // bootstrap... - body..
16997             if (el) {
16998                 tree.el = el;
16999             }
17000             this.parent.el = Roo.factory(tree);
17001             is_body = true;
17002         }
17003         
17004         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17005         this.fireEvent('built', this);
17006         
17007         this.panel = this.el;
17008         this.layout = this.panel.layout;
17009         this.parentLayout = this.parent.layout  || false;  
17010          
17011     }
17012     
17013 });
17014
17015 Roo.apply(Roo.XComponent, {
17016     /**
17017      * @property  hideProgress
17018      * true to disable the building progress bar.. usefull on single page renders.
17019      * @type Boolean
17020      */
17021     hideProgress : false,
17022     /**
17023      * @property  buildCompleted
17024      * True when the builder has completed building the interface.
17025      * @type Boolean
17026      */
17027     buildCompleted : false,
17028      
17029     /**
17030      * @property  topModule
17031      * the upper most module - uses document.element as it's constructor.
17032      * @type Object
17033      */
17034      
17035     topModule  : false,
17036       
17037     /**
17038      * @property  modules
17039      * array of modules to be created by registration system.
17040      * @type {Array} of Roo.XComponent
17041      */
17042     
17043     modules : [],
17044     /**
17045      * @property  elmodules
17046      * array of modules to be created by which use #ID 
17047      * @type {Array} of Roo.XComponent
17048      */
17049      
17050     elmodules : [],
17051
17052      /**
17053      * @property  is_alt
17054      * Is an alternative Root - normally used by bootstrap or other systems,
17055      *    where the top element in the tree can wrap 'body' 
17056      * @type {boolean}  (default false)
17057      */
17058      
17059     is_alt : false,
17060     /**
17061      * @property  build_from_html
17062      * Build elements from html - used by bootstrap HTML stuff 
17063      *    - this is cleared after build is completed
17064      * @type {boolean}    (default false)
17065      */
17066      
17067     build_from_html : false,
17068     /**
17069      * Register components to be built later.
17070      *
17071      * This solves the following issues
17072      * - Building is not done on page load, but after an authentication process has occured.
17073      * - Interface elements are registered on page load
17074      * - Parent Interface elements may not be loaded before child, so this handles that..
17075      * 
17076      *
17077      * example:
17078      * 
17079      * MyApp.register({
17080           order : '000001',
17081           module : 'Pman.Tab.projectMgr',
17082           region : 'center',
17083           parent : 'Pman.layout',
17084           disabled : false,  // or use a function..
17085         })
17086      
17087      * * @param {Object} details about module
17088      */
17089     register : function(obj) {
17090                 
17091         Roo.XComponent.event.fireEvent('register', obj);
17092         switch(typeof(obj.disabled) ) {
17093                 
17094             case 'undefined':
17095                 break;
17096             
17097             case 'function':
17098                 if ( obj.disabled() ) {
17099                         return;
17100                 }
17101                 break;
17102             
17103             default:
17104                 if (obj.disabled || obj.region == '#disabled') {
17105                         return;
17106                 }
17107                 break;
17108         }
17109                 
17110         this.modules.push(obj);
17111          
17112     },
17113     /**
17114      * convert a string to an object..
17115      * eg. 'AAA.BBB' -> finds AAA.BBB
17116
17117      */
17118     
17119     toObject : function(str)
17120     {
17121         if (!str || typeof(str) == 'object') {
17122             return str;
17123         }
17124         if (str.substring(0,1) == '#') {
17125             return str;
17126         }
17127
17128         var ar = str.split('.');
17129         var rt, o;
17130         rt = ar.shift();
17131             /** eval:var:o */
17132         try {
17133             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17134         } catch (e) {
17135             throw "Module not found : " + str;
17136         }
17137         
17138         if (o === false) {
17139             throw "Module not found : " + str;
17140         }
17141         Roo.each(ar, function(e) {
17142             if (typeof(o[e]) == 'undefined') {
17143                 throw "Module not found : " + str;
17144             }
17145             o = o[e];
17146         });
17147         
17148         return o;
17149         
17150     },
17151     
17152     
17153     /**
17154      * move modules into their correct place in the tree..
17155      * 
17156      */
17157     preBuild : function ()
17158     {
17159         var _t = this;
17160         Roo.each(this.modules , function (obj)
17161         {
17162             Roo.XComponent.event.fireEvent('beforebuild', obj);
17163             
17164             var opar = obj.parent;
17165             try { 
17166                 obj.parent = this.toObject(opar);
17167             } catch(e) {
17168                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17169                 return;
17170             }
17171             
17172             if (!obj.parent) {
17173                 Roo.debug && Roo.log("GOT top level module");
17174                 Roo.debug && Roo.log(obj);
17175                 obj.modules = new Roo.util.MixedCollection(false, 
17176                     function(o) { return o.order + '' }
17177                 );
17178                 this.topModule = obj;
17179                 return;
17180             }
17181                         // parent is a string (usually a dom element name..)
17182             if (typeof(obj.parent) == 'string') {
17183                 this.elmodules.push(obj);
17184                 return;
17185             }
17186             if (obj.parent.constructor != Roo.XComponent) {
17187                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17188             }
17189             if (!obj.parent.modules) {
17190                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17191                     function(o) { return o.order + '' }
17192                 );
17193             }
17194             if (obj.parent.disabled) {
17195                 obj.disabled = true;
17196             }
17197             obj.parent.modules.add(obj);
17198         }, this);
17199     },
17200     
17201      /**
17202      * make a list of modules to build.
17203      * @return {Array} list of modules. 
17204      */ 
17205     
17206     buildOrder : function()
17207     {
17208         var _this = this;
17209         var cmp = function(a,b) {   
17210             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17211         };
17212         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17213             throw "No top level modules to build";
17214         }
17215         
17216         // make a flat list in order of modules to build.
17217         var mods = this.topModule ? [ this.topModule ] : [];
17218                 
17219         
17220         // elmodules (is a list of DOM based modules )
17221         Roo.each(this.elmodules, function(e) {
17222             mods.push(e);
17223             if (!this.topModule &&
17224                 typeof(e.parent) == 'string' &&
17225                 e.parent.substring(0,1) == '#' &&
17226                 Roo.get(e.parent.substr(1))
17227                ) {
17228                 
17229                 _this.topModule = e;
17230             }
17231             
17232         });
17233
17234         
17235         // add modules to their parents..
17236         var addMod = function(m) {
17237             Roo.debug && Roo.log("build Order: add: " + m.name);
17238                 
17239             mods.push(m);
17240             if (m.modules && !m.disabled) {
17241                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17242                 m.modules.keySort('ASC',  cmp );
17243                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17244     
17245                 m.modules.each(addMod);
17246             } else {
17247                 Roo.debug && Roo.log("build Order: no child modules");
17248             }
17249             // not sure if this is used any more..
17250             if (m.finalize) {
17251                 m.finalize.name = m.name + " (clean up) ";
17252                 mods.push(m.finalize);
17253             }
17254             
17255         }
17256         if (this.topModule && this.topModule.modules) { 
17257             this.topModule.modules.keySort('ASC',  cmp );
17258             this.topModule.modules.each(addMod);
17259         } 
17260         return mods;
17261     },
17262     
17263      /**
17264      * Build the registered modules.
17265      * @param {Object} parent element.
17266      * @param {Function} optional method to call after module has been added.
17267      * 
17268      */ 
17269    
17270     build : function(opts) 
17271     {
17272         
17273         if (typeof(opts) != 'undefined') {
17274             Roo.apply(this,opts);
17275         }
17276         
17277         this.preBuild();
17278         var mods = this.buildOrder();
17279       
17280         //this.allmods = mods;
17281         //Roo.debug && Roo.log(mods);
17282         //return;
17283         if (!mods.length) { // should not happen
17284             throw "NO modules!!!";
17285         }
17286         
17287         
17288         var msg = "Building Interface...";
17289         // flash it up as modal - so we store the mask!?
17290         if (!this.hideProgress && Roo.MessageBox) {
17291             Roo.MessageBox.show({ title: 'loading' });
17292             Roo.MessageBox.show({
17293                title: "Please wait...",
17294                msg: msg,
17295                width:450,
17296                progress:true,
17297                buttons : false,
17298                closable:false,
17299                modal: false
17300               
17301             });
17302         }
17303         var total = mods.length;
17304         
17305         var _this = this;
17306         var progressRun = function() {
17307             if (!mods.length) {
17308                 Roo.debug && Roo.log('hide?');
17309                 if (!this.hideProgress && Roo.MessageBox) {
17310                     Roo.MessageBox.hide();
17311                 }
17312                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17313                 
17314                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17315                 
17316                 // THE END...
17317                 return false;   
17318             }
17319             
17320             var m = mods.shift();
17321             
17322             
17323             Roo.debug && Roo.log(m);
17324             // not sure if this is supported any more.. - modules that are are just function
17325             if (typeof(m) == 'function') { 
17326                 m.call(this);
17327                 return progressRun.defer(10, _this);
17328             } 
17329             
17330             
17331             msg = "Building Interface " + (total  - mods.length) + 
17332                     " of " + total + 
17333                     (m.name ? (' - ' + m.name) : '');
17334                         Roo.debug && Roo.log(msg);
17335             if (!_this.hideProgress &&  Roo.MessageBox) { 
17336                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17337             }
17338             
17339          
17340             // is the module disabled?
17341             var disabled = (typeof(m.disabled) == 'function') ?
17342                 m.disabled.call(m.module.disabled) : m.disabled;    
17343             
17344             
17345             if (disabled) {
17346                 return progressRun(); // we do not update the display!
17347             }
17348             
17349             // now build 
17350             
17351                         
17352                         
17353             m.render();
17354             // it's 10 on top level, and 1 on others??? why...
17355             return progressRun.defer(10, _this);
17356              
17357         }
17358         progressRun.defer(1, _this);
17359      
17360         
17361         
17362     },
17363     /**
17364      * Overlay a set of modified strings onto a component
17365      * This is dependant on our builder exporting the strings and 'named strings' elements.
17366      * 
17367      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17368      * @param {Object} associative array of 'named' string and it's new value.
17369      * 
17370      */
17371         overlayStrings : function( component, strings )
17372     {
17373         if (typeof(component['_named_strings']) == 'undefined') {
17374             throw "ERROR: component does not have _named_strings";
17375         }
17376         for ( var k in strings ) {
17377             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17378             if (md !== false) {
17379                 component['_strings'][md] = strings[k];
17380             } else {
17381                 Roo.log('could not find named string: ' + k + ' in');
17382                 Roo.log(component);
17383             }
17384             
17385         }
17386         
17387     },
17388     
17389         
17390         /**
17391          * Event Object.
17392          *
17393          *
17394          */
17395         event: false, 
17396     /**
17397          * wrapper for event.on - aliased later..  
17398          * Typically use to register a event handler for register:
17399          *
17400          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17401          *
17402          */
17403     on : false
17404    
17405     
17406     
17407 });
17408
17409 Roo.XComponent.event = new Roo.util.Observable({
17410                 events : { 
17411                         /**
17412                          * @event register
17413                          * Fires when an Component is registered,
17414                          * set the disable property on the Component to stop registration.
17415                          * @param {Roo.XComponent} c the component being registerd.
17416                          * 
17417                          */
17418                         'register' : true,
17419             /**
17420                          * @event beforebuild
17421                          * Fires before each Component is built
17422                          * can be used to apply permissions.
17423                          * @param {Roo.XComponent} c the component being registerd.
17424                          * 
17425                          */
17426                         'beforebuild' : true,
17427                         /**
17428                          * @event buildcomplete
17429                          * Fires on the top level element when all elements have been built
17430                          * @param {Roo.XComponent} the top level component.
17431                          */
17432                         'buildcomplete' : true
17433                         
17434                 }
17435 });
17436
17437 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17438  //
17439  /**
17440  * marked - a markdown parser
17441  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17442  * https://github.com/chjj/marked
17443  */
17444
17445
17446 /**
17447  *
17448  * Roo.Markdown - is a very crude wrapper around marked..
17449  *
17450  * usage:
17451  * 
17452  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17453  * 
17454  * Note: move the sample code to the bottom of this
17455  * file before uncommenting it.
17456  *
17457  */
17458
17459 Roo.Markdown = {};
17460 Roo.Markdown.toHtml = function(text) {
17461     
17462     var c = new Roo.Markdown.marked.setOptions({
17463             renderer: new Roo.Markdown.marked.Renderer(),
17464             gfm: true,
17465             tables: true,
17466             breaks: false,
17467             pedantic: false,
17468             sanitize: false,
17469             smartLists: true,
17470             smartypants: false
17471           });
17472     // A FEW HACKS!!?
17473     
17474     text = text.replace(/\\\n/g,' ');
17475     return Roo.Markdown.marked(text);
17476 };
17477 //
17478 // converter
17479 //
17480 // Wraps all "globals" so that the only thing
17481 // exposed is makeHtml().
17482 //
17483 (function() {
17484     
17485      /**
17486          * eval:var:escape
17487          * eval:var:unescape
17488          * eval:var:replace
17489          */
17490       
17491     /**
17492      * Helpers
17493      */
17494     
17495     var escape = function (html, encode) {
17496       return html
17497         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17498         .replace(/</g, '&lt;')
17499         .replace(/>/g, '&gt;')
17500         .replace(/"/g, '&quot;')
17501         .replace(/'/g, '&#39;');
17502     }
17503     
17504     var unescape = function (html) {
17505         // explicitly match decimal, hex, and named HTML entities 
17506       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17507         n = n.toLowerCase();
17508         if (n === 'colon') { return ':'; }
17509         if (n.charAt(0) === '#') {
17510           return n.charAt(1) === 'x'
17511             ? String.fromCharCode(parseInt(n.substring(2), 16))
17512             : String.fromCharCode(+n.substring(1));
17513         }
17514         return '';
17515       });
17516     }
17517     
17518     var replace = function (regex, opt) {
17519       regex = regex.source;
17520       opt = opt || '';
17521       return function self(name, val) {
17522         if (!name) { return new RegExp(regex, opt); }
17523         val = val.source || val;
17524         val = val.replace(/(^|[^\[])\^/g, '$1');
17525         regex = regex.replace(name, val);
17526         return self;
17527       };
17528     }
17529
17530
17531          /**
17532          * eval:var:noop
17533     */
17534     var noop = function () {}
17535     noop.exec = noop;
17536     
17537          /**
17538          * eval:var:merge
17539     */
17540     var merge = function (obj) {
17541       var i = 1
17542         , target
17543         , key;
17544     
17545       for (; i < arguments.length; i++) {
17546         target = arguments[i];
17547         for (key in target) {
17548           if (Object.prototype.hasOwnProperty.call(target, key)) {
17549             obj[key] = target[key];
17550           }
17551         }
17552       }
17553     
17554       return obj;
17555     }
17556     
17557     
17558     /**
17559      * Block-Level Grammar
17560      */
17561     
17562     
17563     
17564     
17565     var block = {
17566       newline: /^\n+/,
17567       code: /^( {4}[^\n]+\n*)+/,
17568       fences: noop,
17569       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17570       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17571       nptable: noop,
17572       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17573       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17574       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17575       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17576       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17577       table: noop,
17578       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17579       text: /^[^\n]+/
17580     };
17581     
17582     block.bullet = /(?:[*+-]|\d+\.)/;
17583     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17584     block.item = replace(block.item, 'gm')
17585       (/bull/g, block.bullet)
17586       ();
17587     
17588     block.list = replace(block.list)
17589       (/bull/g, block.bullet)
17590       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17591       ('def', '\\n+(?=' + block.def.source + ')')
17592       ();
17593     
17594     block.blockquote = replace(block.blockquote)
17595       ('def', block.def)
17596       ();
17597     
17598     block._tag = '(?!(?:'
17599       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17600       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17601       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17602     
17603     block.html = replace(block.html)
17604       ('comment', /<!--[\s\S]*?-->/)
17605       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17606       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17607       (/tag/g, block._tag)
17608       ();
17609     
17610     block.paragraph = replace(block.paragraph)
17611       ('hr', block.hr)
17612       ('heading', block.heading)
17613       ('lheading', block.lheading)
17614       ('blockquote', block.blockquote)
17615       ('tag', '<' + block._tag)
17616       ('def', block.def)
17617       ();
17618     
17619     /**
17620      * Normal Block Grammar
17621      */
17622     
17623     block.normal = merge({}, block);
17624     
17625     /**
17626      * GFM Block Grammar
17627      */
17628     
17629     block.gfm = merge({}, block.normal, {
17630       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17631       paragraph: /^/,
17632       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17633     });
17634     
17635     block.gfm.paragraph = replace(block.paragraph)
17636       ('(?!', '(?!'
17637         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17638         + block.list.source.replace('\\1', '\\3') + '|')
17639       ();
17640     
17641     /**
17642      * GFM + Tables Block Grammar
17643      */
17644     
17645     block.tables = merge({}, block.gfm, {
17646       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17647       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17648     });
17649     
17650     /**
17651      * Block Lexer
17652      */
17653     
17654     var Lexer = function (options) {
17655       this.tokens = [];
17656       this.tokens.links = {};
17657       this.options = options || marked.defaults;
17658       this.rules = block.normal;
17659     
17660       if (this.options.gfm) {
17661         if (this.options.tables) {
17662           this.rules = block.tables;
17663         } else {
17664           this.rules = block.gfm;
17665         }
17666       }
17667     }
17668     
17669     /**
17670      * Expose Block Rules
17671      */
17672     
17673     Lexer.rules = block;
17674     
17675     /**
17676      * Static Lex Method
17677      */
17678     
17679     Lexer.lex = function(src, options) {
17680       var lexer = new Lexer(options);
17681       return lexer.lex(src);
17682     };
17683     
17684     /**
17685      * Preprocessing
17686      */
17687     
17688     Lexer.prototype.lex = function(src) {
17689       src = src
17690         .replace(/\r\n|\r/g, '\n')
17691         .replace(/\t/g, '    ')
17692         .replace(/\u00a0/g, ' ')
17693         .replace(/\u2424/g, '\n');
17694     
17695       return this.token(src, true);
17696     };
17697     
17698     /**
17699      * Lexing
17700      */
17701     
17702     Lexer.prototype.token = function(src, top, bq) {
17703       var src = src.replace(/^ +$/gm, '')
17704         , next
17705         , loose
17706         , cap
17707         , bull
17708         , b
17709         , item
17710         , space
17711         , i
17712         , l;
17713     
17714       while (src) {
17715         // newline
17716         if (cap = this.rules.newline.exec(src)) {
17717           src = src.substring(cap[0].length);
17718           if (cap[0].length > 1) {
17719             this.tokens.push({
17720               type: 'space'
17721             });
17722           }
17723         }
17724     
17725         // code
17726         if (cap = this.rules.code.exec(src)) {
17727           src = src.substring(cap[0].length);
17728           cap = cap[0].replace(/^ {4}/gm, '');
17729           this.tokens.push({
17730             type: 'code',
17731             text: !this.options.pedantic
17732               ? cap.replace(/\n+$/, '')
17733               : cap
17734           });
17735           continue;
17736         }
17737     
17738         // fences (gfm)
17739         if (cap = this.rules.fences.exec(src)) {
17740           src = src.substring(cap[0].length);
17741           this.tokens.push({
17742             type: 'code',
17743             lang: cap[2],
17744             text: cap[3] || ''
17745           });
17746           continue;
17747         }
17748     
17749         // heading
17750         if (cap = this.rules.heading.exec(src)) {
17751           src = src.substring(cap[0].length);
17752           this.tokens.push({
17753             type: 'heading',
17754             depth: cap[1].length,
17755             text: cap[2]
17756           });
17757           continue;
17758         }
17759     
17760         // table no leading pipe (gfm)
17761         if (top && (cap = this.rules.nptable.exec(src))) {
17762           src = src.substring(cap[0].length);
17763     
17764           item = {
17765             type: 'table',
17766             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17767             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17768             cells: cap[3].replace(/\n$/, '').split('\n')
17769           };
17770     
17771           for (i = 0; i < item.align.length; i++) {
17772             if (/^ *-+: *$/.test(item.align[i])) {
17773               item.align[i] = 'right';
17774             } else if (/^ *:-+: *$/.test(item.align[i])) {
17775               item.align[i] = 'center';
17776             } else if (/^ *:-+ *$/.test(item.align[i])) {
17777               item.align[i] = 'left';
17778             } else {
17779               item.align[i] = null;
17780             }
17781           }
17782     
17783           for (i = 0; i < item.cells.length; i++) {
17784             item.cells[i] = item.cells[i].split(/ *\| */);
17785           }
17786     
17787           this.tokens.push(item);
17788     
17789           continue;
17790         }
17791     
17792         // lheading
17793         if (cap = this.rules.lheading.exec(src)) {
17794           src = src.substring(cap[0].length);
17795           this.tokens.push({
17796             type: 'heading',
17797             depth: cap[2] === '=' ? 1 : 2,
17798             text: cap[1]
17799           });
17800           continue;
17801         }
17802     
17803         // hr
17804         if (cap = this.rules.hr.exec(src)) {
17805           src = src.substring(cap[0].length);
17806           this.tokens.push({
17807             type: 'hr'
17808           });
17809           continue;
17810         }
17811     
17812         // blockquote
17813         if (cap = this.rules.blockquote.exec(src)) {
17814           src = src.substring(cap[0].length);
17815     
17816           this.tokens.push({
17817             type: 'blockquote_start'
17818           });
17819     
17820           cap = cap[0].replace(/^ *> ?/gm, '');
17821     
17822           // Pass `top` to keep the current
17823           // "toplevel" state. This is exactly
17824           // how markdown.pl works.
17825           this.token(cap, top, true);
17826     
17827           this.tokens.push({
17828             type: 'blockquote_end'
17829           });
17830     
17831           continue;
17832         }
17833     
17834         // list
17835         if (cap = this.rules.list.exec(src)) {
17836           src = src.substring(cap[0].length);
17837           bull = cap[2];
17838     
17839           this.tokens.push({
17840             type: 'list_start',
17841             ordered: bull.length > 1
17842           });
17843     
17844           // Get each top-level item.
17845           cap = cap[0].match(this.rules.item);
17846     
17847           next = false;
17848           l = cap.length;
17849           i = 0;
17850     
17851           for (; i < l; i++) {
17852             item = cap[i];
17853     
17854             // Remove the list item's bullet
17855             // so it is seen as the next token.
17856             space = item.length;
17857             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17858     
17859             // Outdent whatever the
17860             // list item contains. Hacky.
17861             if (~item.indexOf('\n ')) {
17862               space -= item.length;
17863               item = !this.options.pedantic
17864                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17865                 : item.replace(/^ {1,4}/gm, '');
17866             }
17867     
17868             // Determine whether the next list item belongs here.
17869             // Backpedal if it does not belong in this list.
17870             if (this.options.smartLists && i !== l - 1) {
17871               b = block.bullet.exec(cap[i + 1])[0];
17872               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17873                 src = cap.slice(i + 1).join('\n') + src;
17874                 i = l - 1;
17875               }
17876             }
17877     
17878             // Determine whether item is loose or not.
17879             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17880             // for discount behavior.
17881             loose = next || /\n\n(?!\s*$)/.test(item);
17882             if (i !== l - 1) {
17883               next = item.charAt(item.length - 1) === '\n';
17884               if (!loose) { loose = next; }
17885             }
17886     
17887             this.tokens.push({
17888               type: loose
17889                 ? 'loose_item_start'
17890                 : 'list_item_start'
17891             });
17892     
17893             // Recurse.
17894             this.token(item, false, bq);
17895     
17896             this.tokens.push({
17897               type: 'list_item_end'
17898             });
17899           }
17900     
17901           this.tokens.push({
17902             type: 'list_end'
17903           });
17904     
17905           continue;
17906         }
17907     
17908         // html
17909         if (cap = this.rules.html.exec(src)) {
17910           src = src.substring(cap[0].length);
17911           this.tokens.push({
17912             type: this.options.sanitize
17913               ? 'paragraph'
17914               : 'html',
17915             pre: !this.options.sanitizer
17916               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17917             text: cap[0]
17918           });
17919           continue;
17920         }
17921     
17922         // def
17923         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17924           src = src.substring(cap[0].length);
17925           this.tokens.links[cap[1].toLowerCase()] = {
17926             href: cap[2],
17927             title: cap[3]
17928           };
17929           continue;
17930         }
17931     
17932         // table (gfm)
17933         if (top && (cap = this.rules.table.exec(src))) {
17934           src = src.substring(cap[0].length);
17935     
17936           item = {
17937             type: 'table',
17938             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17939             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17940             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17941           };
17942     
17943           for (i = 0; i < item.align.length; i++) {
17944             if (/^ *-+: *$/.test(item.align[i])) {
17945               item.align[i] = 'right';
17946             } else if (/^ *:-+: *$/.test(item.align[i])) {
17947               item.align[i] = 'center';
17948             } else if (/^ *:-+ *$/.test(item.align[i])) {
17949               item.align[i] = 'left';
17950             } else {
17951               item.align[i] = null;
17952             }
17953           }
17954     
17955           for (i = 0; i < item.cells.length; i++) {
17956             item.cells[i] = item.cells[i]
17957               .replace(/^ *\| *| *\| *$/g, '')
17958               .split(/ *\| */);
17959           }
17960     
17961           this.tokens.push(item);
17962     
17963           continue;
17964         }
17965     
17966         // top-level paragraph
17967         if (top && (cap = this.rules.paragraph.exec(src))) {
17968           src = src.substring(cap[0].length);
17969           this.tokens.push({
17970             type: 'paragraph',
17971             text: cap[1].charAt(cap[1].length - 1) === '\n'
17972               ? cap[1].slice(0, -1)
17973               : cap[1]
17974           });
17975           continue;
17976         }
17977     
17978         // text
17979         if (cap = this.rules.text.exec(src)) {
17980           // Top-level should never reach here.
17981           src = src.substring(cap[0].length);
17982           this.tokens.push({
17983             type: 'text',
17984             text: cap[0]
17985           });
17986           continue;
17987         }
17988     
17989         if (src) {
17990           throw new
17991             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17992         }
17993       }
17994     
17995       return this.tokens;
17996     };
17997     
17998     /**
17999      * Inline-Level Grammar
18000      */
18001     
18002     var inline = {
18003       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18004       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18005       url: noop,
18006       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18007       link: /^!?\[(inside)\]\(href\)/,
18008       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18009       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18010       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18011       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18012       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18013       br: /^ {2,}\n(?!\s*$)/,
18014       del: noop,
18015       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18016     };
18017     
18018     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18019     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18020     
18021     inline.link = replace(inline.link)
18022       ('inside', inline._inside)
18023       ('href', inline._href)
18024       ();
18025     
18026     inline.reflink = replace(inline.reflink)
18027       ('inside', inline._inside)
18028       ();
18029     
18030     /**
18031      * Normal Inline Grammar
18032      */
18033     
18034     inline.normal = merge({}, inline);
18035     
18036     /**
18037      * Pedantic Inline Grammar
18038      */
18039     
18040     inline.pedantic = merge({}, inline.normal, {
18041       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18042       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18043     });
18044     
18045     /**
18046      * GFM Inline Grammar
18047      */
18048     
18049     inline.gfm = merge({}, inline.normal, {
18050       escape: replace(inline.escape)('])', '~|])')(),
18051       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18052       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18053       text: replace(inline.text)
18054         (']|', '~]|')
18055         ('|', '|https?://|')
18056         ()
18057     });
18058     
18059     /**
18060      * GFM + Line Breaks Inline Grammar
18061      */
18062     
18063     inline.breaks = merge({}, inline.gfm, {
18064       br: replace(inline.br)('{2,}', '*')(),
18065       text: replace(inline.gfm.text)('{2,}', '*')()
18066     });
18067     
18068     /**
18069      * Inline Lexer & Compiler
18070      */
18071     
18072     var InlineLexer  = function (links, options) {
18073       this.options = options || marked.defaults;
18074       this.links = links;
18075       this.rules = inline.normal;
18076       this.renderer = this.options.renderer || new Renderer;
18077       this.renderer.options = this.options;
18078     
18079       if (!this.links) {
18080         throw new
18081           Error('Tokens array requires a `links` property.');
18082       }
18083     
18084       if (this.options.gfm) {
18085         if (this.options.breaks) {
18086           this.rules = inline.breaks;
18087         } else {
18088           this.rules = inline.gfm;
18089         }
18090       } else if (this.options.pedantic) {
18091         this.rules = inline.pedantic;
18092       }
18093     }
18094     
18095     /**
18096      * Expose Inline Rules
18097      */
18098     
18099     InlineLexer.rules = inline;
18100     
18101     /**
18102      * Static Lexing/Compiling Method
18103      */
18104     
18105     InlineLexer.output = function(src, links, options) {
18106       var inline = new InlineLexer(links, options);
18107       return inline.output(src);
18108     };
18109     
18110     /**
18111      * Lexing/Compiling
18112      */
18113     
18114     InlineLexer.prototype.output = function(src) {
18115       var out = ''
18116         , link
18117         , text
18118         , href
18119         , cap;
18120     
18121       while (src) {
18122         // escape
18123         if (cap = this.rules.escape.exec(src)) {
18124           src = src.substring(cap[0].length);
18125           out += cap[1];
18126           continue;
18127         }
18128     
18129         // autolink
18130         if (cap = this.rules.autolink.exec(src)) {
18131           src = src.substring(cap[0].length);
18132           if (cap[2] === '@') {
18133             text = cap[1].charAt(6) === ':'
18134               ? this.mangle(cap[1].substring(7))
18135               : this.mangle(cap[1]);
18136             href = this.mangle('mailto:') + text;
18137           } else {
18138             text = escape(cap[1]);
18139             href = text;
18140           }
18141           out += this.renderer.link(href, null, text);
18142           continue;
18143         }
18144     
18145         // url (gfm)
18146         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18147           src = src.substring(cap[0].length);
18148           text = escape(cap[1]);
18149           href = text;
18150           out += this.renderer.link(href, null, text);
18151           continue;
18152         }
18153     
18154         // tag
18155         if (cap = this.rules.tag.exec(src)) {
18156           if (!this.inLink && /^<a /i.test(cap[0])) {
18157             this.inLink = true;
18158           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18159             this.inLink = false;
18160           }
18161           src = src.substring(cap[0].length);
18162           out += this.options.sanitize
18163             ? this.options.sanitizer
18164               ? this.options.sanitizer(cap[0])
18165               : escape(cap[0])
18166             : cap[0];
18167           continue;
18168         }
18169     
18170         // link
18171         if (cap = this.rules.link.exec(src)) {
18172           src = src.substring(cap[0].length);
18173           this.inLink = true;
18174           out += this.outputLink(cap, {
18175             href: cap[2],
18176             title: cap[3]
18177           });
18178           this.inLink = false;
18179           continue;
18180         }
18181     
18182         // reflink, nolink
18183         if ((cap = this.rules.reflink.exec(src))
18184             || (cap = this.rules.nolink.exec(src))) {
18185           src = src.substring(cap[0].length);
18186           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18187           link = this.links[link.toLowerCase()];
18188           if (!link || !link.href) {
18189             out += cap[0].charAt(0);
18190             src = cap[0].substring(1) + src;
18191             continue;
18192           }
18193           this.inLink = true;
18194           out += this.outputLink(cap, link);
18195           this.inLink = false;
18196           continue;
18197         }
18198     
18199         // strong
18200         if (cap = this.rules.strong.exec(src)) {
18201           src = src.substring(cap[0].length);
18202           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18203           continue;
18204         }
18205     
18206         // em
18207         if (cap = this.rules.em.exec(src)) {
18208           src = src.substring(cap[0].length);
18209           out += this.renderer.em(this.output(cap[2] || cap[1]));
18210           continue;
18211         }
18212     
18213         // code
18214         if (cap = this.rules.code.exec(src)) {
18215           src = src.substring(cap[0].length);
18216           out += this.renderer.codespan(escape(cap[2], true));
18217           continue;
18218         }
18219     
18220         // br
18221         if (cap = this.rules.br.exec(src)) {
18222           src = src.substring(cap[0].length);
18223           out += this.renderer.br();
18224           continue;
18225         }
18226     
18227         // del (gfm)
18228         if (cap = this.rules.del.exec(src)) {
18229           src = src.substring(cap[0].length);
18230           out += this.renderer.del(this.output(cap[1]));
18231           continue;
18232         }
18233     
18234         // text
18235         if (cap = this.rules.text.exec(src)) {
18236           src = src.substring(cap[0].length);
18237           out += this.renderer.text(escape(this.smartypants(cap[0])));
18238           continue;
18239         }
18240     
18241         if (src) {
18242           throw new
18243             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18244         }
18245       }
18246     
18247       return out;
18248     };
18249     
18250     /**
18251      * Compile Link
18252      */
18253     
18254     InlineLexer.prototype.outputLink = function(cap, link) {
18255       var href = escape(link.href)
18256         , title = link.title ? escape(link.title) : null;
18257     
18258       return cap[0].charAt(0) !== '!'
18259         ? this.renderer.link(href, title, this.output(cap[1]))
18260         : this.renderer.image(href, title, escape(cap[1]));
18261     };
18262     
18263     /**
18264      * Smartypants Transformations
18265      */
18266     
18267     InlineLexer.prototype.smartypants = function(text) {
18268       if (!this.options.smartypants)  { return text; }
18269       return text
18270         // em-dashes
18271         .replace(/---/g, '\u2014')
18272         // en-dashes
18273         .replace(/--/g, '\u2013')
18274         // opening singles
18275         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18276         // closing singles & apostrophes
18277         .replace(/'/g, '\u2019')
18278         // opening doubles
18279         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18280         // closing doubles
18281         .replace(/"/g, '\u201d')
18282         // ellipses
18283         .replace(/\.{3}/g, '\u2026');
18284     };
18285     
18286     /**
18287      * Mangle Links
18288      */
18289     
18290     InlineLexer.prototype.mangle = function(text) {
18291       if (!this.options.mangle) { return text; }
18292       var out = ''
18293         , l = text.length
18294         , i = 0
18295         , ch;
18296     
18297       for (; i < l; i++) {
18298         ch = text.charCodeAt(i);
18299         if (Math.random() > 0.5) {
18300           ch = 'x' + ch.toString(16);
18301         }
18302         out += '&#' + ch + ';';
18303       }
18304     
18305       return out;
18306     };
18307     
18308     /**
18309      * Renderer
18310      */
18311     
18312      /**
18313          * eval:var:Renderer
18314     */
18315     
18316     var Renderer   = function (options) {
18317       this.options = options || {};
18318     }
18319     
18320     Renderer.prototype.code = function(code, lang, escaped) {
18321       if (this.options.highlight) {
18322         var out = this.options.highlight(code, lang);
18323         if (out != null && out !== code) {
18324           escaped = true;
18325           code = out;
18326         }
18327       } else {
18328             // hack!!! - it's already escapeD?
18329             escaped = true;
18330       }
18331     
18332       if (!lang) {
18333         return '<pre><code>'
18334           + (escaped ? code : escape(code, true))
18335           + '\n</code></pre>';
18336       }
18337     
18338       return '<pre><code class="'
18339         + this.options.langPrefix
18340         + escape(lang, true)
18341         + '">'
18342         + (escaped ? code : escape(code, true))
18343         + '\n</code></pre>\n';
18344     };
18345     
18346     Renderer.prototype.blockquote = function(quote) {
18347       return '<blockquote>\n' + quote + '</blockquote>\n';
18348     };
18349     
18350     Renderer.prototype.html = function(html) {
18351       return html;
18352     };
18353     
18354     Renderer.prototype.heading = function(text, level, raw) {
18355       return '<h'
18356         + level
18357         + ' id="'
18358         + this.options.headerPrefix
18359         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18360         + '">'
18361         + text
18362         + '</h'
18363         + level
18364         + '>\n';
18365     };
18366     
18367     Renderer.prototype.hr = function() {
18368       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18369     };
18370     
18371     Renderer.prototype.list = function(body, ordered) {
18372       var type = ordered ? 'ol' : 'ul';
18373       return '<' + type + '>\n' + body + '</' + type + '>\n';
18374     };
18375     
18376     Renderer.prototype.listitem = function(text) {
18377       return '<li>' + text + '</li>\n';
18378     };
18379     
18380     Renderer.prototype.paragraph = function(text) {
18381       return '<p>' + text + '</p>\n';
18382     };
18383     
18384     Renderer.prototype.table = function(header, body) {
18385       return '<table class="table table-striped">\n'
18386         + '<thead>\n'
18387         + header
18388         + '</thead>\n'
18389         + '<tbody>\n'
18390         + body
18391         + '</tbody>\n'
18392         + '</table>\n';
18393     };
18394     
18395     Renderer.prototype.tablerow = function(content) {
18396       return '<tr>\n' + content + '</tr>\n';
18397     };
18398     
18399     Renderer.prototype.tablecell = function(content, flags) {
18400       var type = flags.header ? 'th' : 'td';
18401       var tag = flags.align
18402         ? '<' + type + ' style="text-align:' + flags.align + '">'
18403         : '<' + type + '>';
18404       return tag + content + '</' + type + '>\n';
18405     };
18406     
18407     // span level renderer
18408     Renderer.prototype.strong = function(text) {
18409       return '<strong>' + text + '</strong>';
18410     };
18411     
18412     Renderer.prototype.em = function(text) {
18413       return '<em>' + text + '</em>';
18414     };
18415     
18416     Renderer.prototype.codespan = function(text) {
18417       return '<code>' + text + '</code>';
18418     };
18419     
18420     Renderer.prototype.br = function() {
18421       return this.options.xhtml ? '<br/>' : '<br>';
18422     };
18423     
18424     Renderer.prototype.del = function(text) {
18425       return '<del>' + text + '</del>';
18426     };
18427     
18428     Renderer.prototype.link = function(href, title, text) {
18429       if (this.options.sanitize) {
18430         try {
18431           var prot = decodeURIComponent(unescape(href))
18432             .replace(/[^\w:]/g, '')
18433             .toLowerCase();
18434         } catch (e) {
18435           return '';
18436         }
18437         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18438           return '';
18439         }
18440       }
18441       var out = '<a href="' + href + '"';
18442       if (title) {
18443         out += ' title="' + title + '"';
18444       }
18445       out += '>' + text + '</a>';
18446       return out;
18447     };
18448     
18449     Renderer.prototype.image = function(href, title, text) {
18450       var out = '<img src="' + href + '" alt="' + text + '"';
18451       if (title) {
18452         out += ' title="' + title + '"';
18453       }
18454       out += this.options.xhtml ? '/>' : '>';
18455       return out;
18456     };
18457     
18458     Renderer.prototype.text = function(text) {
18459       return text;
18460     };
18461     
18462     /**
18463      * Parsing & Compiling
18464      */
18465          /**
18466          * eval:var:Parser
18467     */
18468     
18469     var Parser= function (options) {
18470       this.tokens = [];
18471       this.token = null;
18472       this.options = options || marked.defaults;
18473       this.options.renderer = this.options.renderer || new Renderer;
18474       this.renderer = this.options.renderer;
18475       this.renderer.options = this.options;
18476     }
18477     
18478     /**
18479      * Static Parse Method
18480      */
18481     
18482     Parser.parse = function(src, options, renderer) {
18483       var parser = new Parser(options, renderer);
18484       return parser.parse(src);
18485     };
18486     
18487     /**
18488      * Parse Loop
18489      */
18490     
18491     Parser.prototype.parse = function(src) {
18492       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18493       this.tokens = src.reverse();
18494     
18495       var out = '';
18496       while (this.next()) {
18497         out += this.tok();
18498       }
18499     
18500       return out;
18501     };
18502     
18503     /**
18504      * Next Token
18505      */
18506     
18507     Parser.prototype.next = function() {
18508       return this.token = this.tokens.pop();
18509     };
18510     
18511     /**
18512      * Preview Next Token
18513      */
18514     
18515     Parser.prototype.peek = function() {
18516       return this.tokens[this.tokens.length - 1] || 0;
18517     };
18518     
18519     /**
18520      * Parse Text Tokens
18521      */
18522     
18523     Parser.prototype.parseText = function() {
18524       var body = this.token.text;
18525     
18526       while (this.peek().type === 'text') {
18527         body += '\n' + this.next().text;
18528       }
18529     
18530       return this.inline.output(body);
18531     };
18532     
18533     /**
18534      * Parse Current Token
18535      */
18536     
18537     Parser.prototype.tok = function() {
18538       switch (this.token.type) {
18539         case 'space': {
18540           return '';
18541         }
18542         case 'hr': {
18543           return this.renderer.hr();
18544         }
18545         case 'heading': {
18546           return this.renderer.heading(
18547             this.inline.output(this.token.text),
18548             this.token.depth,
18549             this.token.text);
18550         }
18551         case 'code': {
18552           return this.renderer.code(this.token.text,
18553             this.token.lang,
18554             this.token.escaped);
18555         }
18556         case 'table': {
18557           var header = ''
18558             , body = ''
18559             , i
18560             , row
18561             , cell
18562             , flags
18563             , j;
18564     
18565           // header
18566           cell = '';
18567           for (i = 0; i < this.token.header.length; i++) {
18568             flags = { header: true, align: this.token.align[i] };
18569             cell += this.renderer.tablecell(
18570               this.inline.output(this.token.header[i]),
18571               { header: true, align: this.token.align[i] }
18572             );
18573           }
18574           header += this.renderer.tablerow(cell);
18575     
18576           for (i = 0; i < this.token.cells.length; i++) {
18577             row = this.token.cells[i];
18578     
18579             cell = '';
18580             for (j = 0; j < row.length; j++) {
18581               cell += this.renderer.tablecell(
18582                 this.inline.output(row[j]),
18583                 { header: false, align: this.token.align[j] }
18584               );
18585             }
18586     
18587             body += this.renderer.tablerow(cell);
18588           }
18589           return this.renderer.table(header, body);
18590         }
18591         case 'blockquote_start': {
18592           var body = '';
18593     
18594           while (this.next().type !== 'blockquote_end') {
18595             body += this.tok();
18596           }
18597     
18598           return this.renderer.blockquote(body);
18599         }
18600         case 'list_start': {
18601           var body = ''
18602             , ordered = this.token.ordered;
18603     
18604           while (this.next().type !== 'list_end') {
18605             body += this.tok();
18606           }
18607     
18608           return this.renderer.list(body, ordered);
18609         }
18610         case 'list_item_start': {
18611           var body = '';
18612     
18613           while (this.next().type !== 'list_item_end') {
18614             body += this.token.type === 'text'
18615               ? this.parseText()
18616               : this.tok();
18617           }
18618     
18619           return this.renderer.listitem(body);
18620         }
18621         case 'loose_item_start': {
18622           var body = '';
18623     
18624           while (this.next().type !== 'list_item_end') {
18625             body += this.tok();
18626           }
18627     
18628           return this.renderer.listitem(body);
18629         }
18630         case 'html': {
18631           var html = !this.token.pre && !this.options.pedantic
18632             ? this.inline.output(this.token.text)
18633             : this.token.text;
18634           return this.renderer.html(html);
18635         }
18636         case 'paragraph': {
18637           return this.renderer.paragraph(this.inline.output(this.token.text));
18638         }
18639         case 'text': {
18640           return this.renderer.paragraph(this.parseText());
18641         }
18642       }
18643     };
18644   
18645     
18646     /**
18647      * Marked
18648      */
18649          /**
18650          * eval:var:marked
18651     */
18652     var marked = function (src, opt, callback) {
18653       if (callback || typeof opt === 'function') {
18654         if (!callback) {
18655           callback = opt;
18656           opt = null;
18657         }
18658     
18659         opt = merge({}, marked.defaults, opt || {});
18660     
18661         var highlight = opt.highlight
18662           , tokens
18663           , pending
18664           , i = 0;
18665     
18666         try {
18667           tokens = Lexer.lex(src, opt)
18668         } catch (e) {
18669           return callback(e);
18670         }
18671     
18672         pending = tokens.length;
18673          /**
18674          * eval:var:done
18675     */
18676         var done = function(err) {
18677           if (err) {
18678             opt.highlight = highlight;
18679             return callback(err);
18680           }
18681     
18682           var out;
18683     
18684           try {
18685             out = Parser.parse(tokens, opt);
18686           } catch (e) {
18687             err = e;
18688           }
18689     
18690           opt.highlight = highlight;
18691     
18692           return err
18693             ? callback(err)
18694             : callback(null, out);
18695         };
18696     
18697         if (!highlight || highlight.length < 3) {
18698           return done();
18699         }
18700     
18701         delete opt.highlight;
18702     
18703         if (!pending) { return done(); }
18704     
18705         for (; i < tokens.length; i++) {
18706           (function(token) {
18707             if (token.type !== 'code') {
18708               return --pending || done();
18709             }
18710             return highlight(token.text, token.lang, function(err, code) {
18711               if (err) { return done(err); }
18712               if (code == null || code === token.text) {
18713                 return --pending || done();
18714               }
18715               token.text = code;
18716               token.escaped = true;
18717               --pending || done();
18718             });
18719           })(tokens[i]);
18720         }
18721     
18722         return;
18723       }
18724       try {
18725         if (opt) { opt = merge({}, marked.defaults, opt); }
18726         return Parser.parse(Lexer.lex(src, opt), opt);
18727       } catch (e) {
18728         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18729         if ((opt || marked.defaults).silent) {
18730           return '<p>An error occured:</p><pre>'
18731             + escape(e.message + '', true)
18732             + '</pre>';
18733         }
18734         throw e;
18735       }
18736     }
18737     
18738     /**
18739      * Options
18740      */
18741     
18742     marked.options =
18743     marked.setOptions = function(opt) {
18744       merge(marked.defaults, opt);
18745       return marked;
18746     };
18747     
18748     marked.defaults = {
18749       gfm: true,
18750       tables: true,
18751       breaks: false,
18752       pedantic: false,
18753       sanitize: false,
18754       sanitizer: null,
18755       mangle: true,
18756       smartLists: false,
18757       silent: false,
18758       highlight: null,
18759       langPrefix: 'lang-',
18760       smartypants: false,
18761       headerPrefix: '',
18762       renderer: new Renderer,
18763       xhtml: false
18764     };
18765     
18766     /**
18767      * Expose
18768      */
18769     
18770     marked.Parser = Parser;
18771     marked.parser = Parser.parse;
18772     
18773     marked.Renderer = Renderer;
18774     
18775     marked.Lexer = Lexer;
18776     marked.lexer = Lexer.lex;
18777     
18778     marked.InlineLexer = InlineLexer;
18779     marked.inlineLexer = InlineLexer.output;
18780     
18781     marked.parse = marked;
18782     
18783     Roo.Markdown.marked = marked;
18784
18785 })();/*
18786  * Based on:
18787  * Ext JS Library 1.1.1
18788  * Copyright(c) 2006-2007, Ext JS, LLC.
18789  *
18790  * Originally Released Under LGPL - original licence link has changed is not relivant.
18791  *
18792  * Fork - LGPL
18793  * <script type="text/javascript">
18794  */
18795
18796
18797
18798 /*
18799  * These classes are derivatives of the similarly named classes in the YUI Library.
18800  * The original license:
18801  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18802  * Code licensed under the BSD License:
18803  * http://developer.yahoo.net/yui/license.txt
18804  */
18805
18806 (function() {
18807
18808 var Event=Roo.EventManager;
18809 var Dom=Roo.lib.Dom;
18810
18811 /**
18812  * @class Roo.dd.DragDrop
18813  * @extends Roo.util.Observable
18814  * Defines the interface and base operation of items that that can be
18815  * dragged or can be drop targets.  It was designed to be extended, overriding
18816  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18817  * Up to three html elements can be associated with a DragDrop instance:
18818  * <ul>
18819  * <li>linked element: the element that is passed into the constructor.
18820  * This is the element which defines the boundaries for interaction with
18821  * other DragDrop objects.</li>
18822  * <li>handle element(s): The drag operation only occurs if the element that
18823  * was clicked matches a handle element.  By default this is the linked
18824  * element, but there are times that you will want only a portion of the
18825  * linked element to initiate the drag operation, and the setHandleElId()
18826  * method provides a way to define this.</li>
18827  * <li>drag element: this represents the element that would be moved along
18828  * with the cursor during a drag operation.  By default, this is the linked
18829  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18830  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18831  * </li>
18832  * </ul>
18833  * This class should not be instantiated until the onload event to ensure that
18834  * the associated elements are available.
18835  * The following would define a DragDrop obj that would interact with any
18836  * other DragDrop obj in the "group1" group:
18837  * <pre>
18838  *  dd = new Roo.dd.DragDrop("div1", "group1");
18839  * </pre>
18840  * Since none of the event handlers have been implemented, nothing would
18841  * actually happen if you were to run the code above.  Normally you would
18842  * override this class or one of the default implementations, but you can
18843  * also override the methods you want on an instance of the class...
18844  * <pre>
18845  *  dd.onDragDrop = function(e, id) {
18846  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18847  *  }
18848  * </pre>
18849  * @constructor
18850  * @param {String} id of the element that is linked to this instance
18851  * @param {String} sGroup the group of related DragDrop objects
18852  * @param {object} config an object containing configurable attributes
18853  *                Valid properties for DragDrop:
18854  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18855  */
18856 Roo.dd.DragDrop = function(id, sGroup, config) {
18857     if (id) {
18858         this.init(id, sGroup, config);
18859     }
18860     
18861 };
18862
18863 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18864
18865     /**
18866      * The id of the element associated with this object.  This is what we
18867      * refer to as the "linked element" because the size and position of
18868      * this element is used to determine when the drag and drop objects have
18869      * interacted.
18870      * @property id
18871      * @type String
18872      */
18873     id: null,
18874
18875     /**
18876      * Configuration attributes passed into the constructor
18877      * @property config
18878      * @type object
18879      */
18880     config: null,
18881
18882     /**
18883      * The id of the element that will be dragged.  By default this is same
18884      * as the linked element , but could be changed to another element. Ex:
18885      * Roo.dd.DDProxy
18886      * @property dragElId
18887      * @type String
18888      * @private
18889      */
18890     dragElId: null,
18891
18892     /**
18893      * the id of the element that initiates the drag operation.  By default
18894      * this is the linked element, but could be changed to be a child of this
18895      * element.  This lets us do things like only starting the drag when the
18896      * header element within the linked html element is clicked.
18897      * @property handleElId
18898      * @type String
18899      * @private
18900      */
18901     handleElId: null,
18902
18903     /**
18904      * An associative array of HTML tags that will be ignored if clicked.
18905      * @property invalidHandleTypes
18906      * @type {string: string}
18907      */
18908     invalidHandleTypes: null,
18909
18910     /**
18911      * An associative array of ids for elements that will be ignored if clicked
18912      * @property invalidHandleIds
18913      * @type {string: string}
18914      */
18915     invalidHandleIds: null,
18916
18917     /**
18918      * An indexted array of css class names for elements that will be ignored
18919      * if clicked.
18920      * @property invalidHandleClasses
18921      * @type string[]
18922      */
18923     invalidHandleClasses: null,
18924
18925     /**
18926      * The linked element's absolute X position at the time the drag was
18927      * started
18928      * @property startPageX
18929      * @type int
18930      * @private
18931      */
18932     startPageX: 0,
18933
18934     /**
18935      * The linked element's absolute X position at the time the drag was
18936      * started
18937      * @property startPageY
18938      * @type int
18939      * @private
18940      */
18941     startPageY: 0,
18942
18943     /**
18944      * The group defines a logical collection of DragDrop objects that are
18945      * related.  Instances only get events when interacting with other
18946      * DragDrop object in the same group.  This lets us define multiple
18947      * groups using a single DragDrop subclass if we want.
18948      * @property groups
18949      * @type {string: string}
18950      */
18951     groups: null,
18952
18953     /**
18954      * Individual drag/drop instances can be locked.  This will prevent
18955      * onmousedown start drag.
18956      * @property locked
18957      * @type boolean
18958      * @private
18959      */
18960     locked: false,
18961
18962     /**
18963      * Lock this instance
18964      * @method lock
18965      */
18966     lock: function() { this.locked = true; },
18967
18968     /**
18969      * Unlock this instace
18970      * @method unlock
18971      */
18972     unlock: function() { this.locked = false; },
18973
18974     /**
18975      * By default, all insances can be a drop target.  This can be disabled by
18976      * setting isTarget to false.
18977      * @method isTarget
18978      * @type boolean
18979      */
18980     isTarget: true,
18981
18982     /**
18983      * The padding configured for this drag and drop object for calculating
18984      * the drop zone intersection with this object.
18985      * @method padding
18986      * @type int[]
18987      */
18988     padding: null,
18989
18990     /**
18991      * Cached reference to the linked element
18992      * @property _domRef
18993      * @private
18994      */
18995     _domRef: null,
18996
18997     /**
18998      * Internal typeof flag
18999      * @property __ygDragDrop
19000      * @private
19001      */
19002     __ygDragDrop: true,
19003
19004     /**
19005      * Set to true when horizontal contraints are applied
19006      * @property constrainX
19007      * @type boolean
19008      * @private
19009      */
19010     constrainX: false,
19011
19012     /**
19013      * Set to true when vertical contraints are applied
19014      * @property constrainY
19015      * @type boolean
19016      * @private
19017      */
19018     constrainY: false,
19019
19020     /**
19021      * The left constraint
19022      * @property minX
19023      * @type int
19024      * @private
19025      */
19026     minX: 0,
19027
19028     /**
19029      * The right constraint
19030      * @property maxX
19031      * @type int
19032      * @private
19033      */
19034     maxX: 0,
19035
19036     /**
19037      * The up constraint
19038      * @property minY
19039      * @type int
19040      * @type int
19041      * @private
19042      */
19043     minY: 0,
19044
19045     /**
19046      * The down constraint
19047      * @property maxY
19048      * @type int
19049      * @private
19050      */
19051     maxY: 0,
19052
19053     /**
19054      * Maintain offsets when we resetconstraints.  Set to true when you want
19055      * the position of the element relative to its parent to stay the same
19056      * when the page changes
19057      *
19058      * @property maintainOffset
19059      * @type boolean
19060      */
19061     maintainOffset: false,
19062
19063     /**
19064      * Array of pixel locations the element will snap to if we specified a
19065      * horizontal graduation/interval.  This array is generated automatically
19066      * when you define a tick interval.
19067      * @property xTicks
19068      * @type int[]
19069      */
19070     xTicks: null,
19071
19072     /**
19073      * Array of pixel locations the element will snap to if we specified a
19074      * vertical graduation/interval.  This array is generated automatically
19075      * when you define a tick interval.
19076      * @property yTicks
19077      * @type int[]
19078      */
19079     yTicks: null,
19080
19081     /**
19082      * By default the drag and drop instance will only respond to the primary
19083      * button click (left button for a right-handed mouse).  Set to true to
19084      * allow drag and drop to start with any mouse click that is propogated
19085      * by the browser
19086      * @property primaryButtonOnly
19087      * @type boolean
19088      */
19089     primaryButtonOnly: true,
19090
19091     /**
19092      * The availabe property is false until the linked dom element is accessible.
19093      * @property available
19094      * @type boolean
19095      */
19096     available: false,
19097
19098     /**
19099      * By default, drags can only be initiated if the mousedown occurs in the
19100      * region the linked element is.  This is done in part to work around a
19101      * bug in some browsers that mis-report the mousedown if the previous
19102      * mouseup happened outside of the window.  This property is set to true
19103      * if outer handles are defined.
19104      *
19105      * @property hasOuterHandles
19106      * @type boolean
19107      * @default false
19108      */
19109     hasOuterHandles: false,
19110
19111     /**
19112      * Code that executes immediately before the startDrag event
19113      * @method b4StartDrag
19114      * @private
19115      */
19116     b4StartDrag: function(x, y) { },
19117
19118     /**
19119      * Abstract method called after a drag/drop object is clicked
19120      * and the drag or mousedown time thresholds have beeen met.
19121      * @method startDrag
19122      * @param {int} X click location
19123      * @param {int} Y click location
19124      */
19125     startDrag: function(x, y) { /* override this */ },
19126
19127     /**
19128      * Code that executes immediately before the onDrag event
19129      * @method b4Drag
19130      * @private
19131      */
19132     b4Drag: function(e) { },
19133
19134     /**
19135      * Abstract method called during the onMouseMove event while dragging an
19136      * object.
19137      * @method onDrag
19138      * @param {Event} e the mousemove event
19139      */
19140     onDrag: function(e) { /* override this */ },
19141
19142     /**
19143      * Abstract method called when this element fist begins hovering over
19144      * another DragDrop obj
19145      * @method onDragEnter
19146      * @param {Event} e the mousemove event
19147      * @param {String|DragDrop[]} id In POINT mode, the element
19148      * id this is hovering over.  In INTERSECT mode, an array of one or more
19149      * dragdrop items being hovered over.
19150      */
19151     onDragEnter: function(e, id) { /* override this */ },
19152
19153     /**
19154      * Code that executes immediately before the onDragOver event
19155      * @method b4DragOver
19156      * @private
19157      */
19158     b4DragOver: function(e) { },
19159
19160     /**
19161      * Abstract method called when this element is hovering over another
19162      * DragDrop obj
19163      * @method onDragOver
19164      * @param {Event} e the mousemove event
19165      * @param {String|DragDrop[]} id In POINT mode, the element
19166      * id this is hovering over.  In INTERSECT mode, an array of dd items
19167      * being hovered over.
19168      */
19169     onDragOver: function(e, id) { /* override this */ },
19170
19171     /**
19172      * Code that executes immediately before the onDragOut event
19173      * @method b4DragOut
19174      * @private
19175      */
19176     b4DragOut: function(e) { },
19177
19178     /**
19179      * Abstract method called when we are no longer hovering over an element
19180      * @method onDragOut
19181      * @param {Event} e the mousemove event
19182      * @param {String|DragDrop[]} id In POINT mode, the element
19183      * id this was hovering over.  In INTERSECT mode, an array of dd items
19184      * that the mouse is no longer over.
19185      */
19186     onDragOut: function(e, id) { /* override this */ },
19187
19188     /**
19189      * Code that executes immediately before the onDragDrop event
19190      * @method b4DragDrop
19191      * @private
19192      */
19193     b4DragDrop: function(e) { },
19194
19195     /**
19196      * Abstract method called when this item is dropped on another DragDrop
19197      * obj
19198      * @method onDragDrop
19199      * @param {Event} e the mouseup event
19200      * @param {String|DragDrop[]} id In POINT mode, the element
19201      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19202      * was dropped on.
19203      */
19204     onDragDrop: function(e, id) { /* override this */ },
19205
19206     /**
19207      * Abstract method called when this item is dropped on an area with no
19208      * drop target
19209      * @method onInvalidDrop
19210      * @param {Event} e the mouseup event
19211      */
19212     onInvalidDrop: function(e) { /* override this */ },
19213
19214     /**
19215      * Code that executes immediately before the endDrag event
19216      * @method b4EndDrag
19217      * @private
19218      */
19219     b4EndDrag: function(e) { },
19220
19221     /**
19222      * Fired when we are done dragging the object
19223      * @method endDrag
19224      * @param {Event} e the mouseup event
19225      */
19226     endDrag: function(e) { /* override this */ },
19227
19228     /**
19229      * Code executed immediately before the onMouseDown event
19230      * @method b4MouseDown
19231      * @param {Event} e the mousedown event
19232      * @private
19233      */
19234     b4MouseDown: function(e) {  },
19235
19236     /**
19237      * Event handler that fires when a drag/drop obj gets a mousedown
19238      * @method onMouseDown
19239      * @param {Event} e the mousedown event
19240      */
19241     onMouseDown: function(e) { /* override this */ },
19242
19243     /**
19244      * Event handler that fires when a drag/drop obj gets a mouseup
19245      * @method onMouseUp
19246      * @param {Event} e the mouseup event
19247      */
19248     onMouseUp: function(e) { /* override this */ },
19249
19250     /**
19251      * Override the onAvailable method to do what is needed after the initial
19252      * position was determined.
19253      * @method onAvailable
19254      */
19255     onAvailable: function () {
19256     },
19257
19258     /*
19259      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19260      * @type Object
19261      */
19262     defaultPadding : {left:0, right:0, top:0, bottom:0},
19263
19264     /*
19265      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19266  *
19267  * Usage:
19268  <pre><code>
19269  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19270                 { dragElId: "existingProxyDiv" });
19271  dd.startDrag = function(){
19272      this.constrainTo("parent-id");
19273  };
19274  </code></pre>
19275  * Or you can initalize it using the {@link Roo.Element} object:
19276  <pre><code>
19277  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19278      startDrag : function(){
19279          this.constrainTo("parent-id");
19280      }
19281  });
19282  </code></pre>
19283      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19284      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19285      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19286      * an object containing the sides to pad. For example: {right:10, bottom:10}
19287      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19288      */
19289     constrainTo : function(constrainTo, pad, inContent){
19290         if(typeof pad == "number"){
19291             pad = {left: pad, right:pad, top:pad, bottom:pad};
19292         }
19293         pad = pad || this.defaultPadding;
19294         var b = Roo.get(this.getEl()).getBox();
19295         var ce = Roo.get(constrainTo);
19296         var s = ce.getScroll();
19297         var c, cd = ce.dom;
19298         if(cd == document.body){
19299             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19300         }else{
19301             xy = ce.getXY();
19302             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19303         }
19304
19305
19306         var topSpace = b.y - c.y;
19307         var leftSpace = b.x - c.x;
19308
19309         this.resetConstraints();
19310         this.setXConstraint(leftSpace - (pad.left||0), // left
19311                 c.width - leftSpace - b.width - (pad.right||0) //right
19312         );
19313         this.setYConstraint(topSpace - (pad.top||0), //top
19314                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19315         );
19316     },
19317
19318     /**
19319      * Returns a reference to the linked element
19320      * @method getEl
19321      * @return {HTMLElement} the html element
19322      */
19323     getEl: function() {
19324         if (!this._domRef) {
19325             this._domRef = Roo.getDom(this.id);
19326         }
19327
19328         return this._domRef;
19329     },
19330
19331     /**
19332      * Returns a reference to the actual element to drag.  By default this is
19333      * the same as the html element, but it can be assigned to another
19334      * element. An example of this can be found in Roo.dd.DDProxy
19335      * @method getDragEl
19336      * @return {HTMLElement} the html element
19337      */
19338     getDragEl: function() {
19339         return Roo.getDom(this.dragElId);
19340     },
19341
19342     /**
19343      * Sets up the DragDrop object.  Must be called in the constructor of any
19344      * Roo.dd.DragDrop subclass
19345      * @method init
19346      * @param id the id of the linked element
19347      * @param {String} sGroup the group of related items
19348      * @param {object} config configuration attributes
19349      */
19350     init: function(id, sGroup, config) {
19351         this.initTarget(id, sGroup, config);
19352         if (!Roo.isTouch) {
19353             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19354         }
19355         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19356         // Event.on(this.id, "selectstart", Event.preventDefault);
19357     },
19358
19359     /**
19360      * Initializes Targeting functionality only... the object does not
19361      * get a mousedown handler.
19362      * @method initTarget
19363      * @param id the id of the linked element
19364      * @param {String} sGroup the group of related items
19365      * @param {object} config configuration attributes
19366      */
19367     initTarget: function(id, sGroup, config) {
19368
19369         // configuration attributes
19370         this.config = config || {};
19371
19372         // create a local reference to the drag and drop manager
19373         this.DDM = Roo.dd.DDM;
19374         // initialize the groups array
19375         this.groups = {};
19376
19377         // assume that we have an element reference instead of an id if the
19378         // parameter is not a string
19379         if (typeof id !== "string") {
19380             id = Roo.id(id);
19381         }
19382
19383         // set the id
19384         this.id = id;
19385
19386         // add to an interaction group
19387         this.addToGroup((sGroup) ? sGroup : "default");
19388
19389         // We don't want to register this as the handle with the manager
19390         // so we just set the id rather than calling the setter.
19391         this.handleElId = id;
19392
19393         // the linked element is the element that gets dragged by default
19394         this.setDragElId(id);
19395
19396         // by default, clicked anchors will not start drag operations.
19397         this.invalidHandleTypes = { A: "A" };
19398         this.invalidHandleIds = {};
19399         this.invalidHandleClasses = [];
19400
19401         this.applyConfig();
19402
19403         this.handleOnAvailable();
19404     },
19405
19406     /**
19407      * Applies the configuration parameters that were passed into the constructor.
19408      * This is supposed to happen at each level through the inheritance chain.  So
19409      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19410      * DragDrop in order to get all of the parameters that are available in
19411      * each object.
19412      * @method applyConfig
19413      */
19414     applyConfig: function() {
19415
19416         // configurable properties:
19417         //    padding, isTarget, maintainOffset, primaryButtonOnly
19418         this.padding           = this.config.padding || [0, 0, 0, 0];
19419         this.isTarget          = (this.config.isTarget !== false);
19420         this.maintainOffset    = (this.config.maintainOffset);
19421         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19422
19423     },
19424
19425     /**
19426      * Executed when the linked element is available
19427      * @method handleOnAvailable
19428      * @private
19429      */
19430     handleOnAvailable: function() {
19431         this.available = true;
19432         this.resetConstraints();
19433         this.onAvailable();
19434     },
19435
19436      /**
19437      * Configures the padding for the target zone in px.  Effectively expands
19438      * (or reduces) the virtual object size for targeting calculations.
19439      * Supports css-style shorthand; if only one parameter is passed, all sides
19440      * will have that padding, and if only two are passed, the top and bottom
19441      * will have the first param, the left and right the second.
19442      * @method setPadding
19443      * @param {int} iTop    Top pad
19444      * @param {int} iRight  Right pad
19445      * @param {int} iBot    Bot pad
19446      * @param {int} iLeft   Left pad
19447      */
19448     setPadding: function(iTop, iRight, iBot, iLeft) {
19449         // this.padding = [iLeft, iRight, iTop, iBot];
19450         if (!iRight && 0 !== iRight) {
19451             this.padding = [iTop, iTop, iTop, iTop];
19452         } else if (!iBot && 0 !== iBot) {
19453             this.padding = [iTop, iRight, iTop, iRight];
19454         } else {
19455             this.padding = [iTop, iRight, iBot, iLeft];
19456         }
19457     },
19458
19459     /**
19460      * Stores the initial placement of the linked element.
19461      * @method setInitialPosition
19462      * @param {int} diffX   the X offset, default 0
19463      * @param {int} diffY   the Y offset, default 0
19464      */
19465     setInitPosition: function(diffX, diffY) {
19466         var el = this.getEl();
19467
19468         if (!this.DDM.verifyEl(el)) {
19469             return;
19470         }
19471
19472         var dx = diffX || 0;
19473         var dy = diffY || 0;
19474
19475         var p = Dom.getXY( el );
19476
19477         this.initPageX = p[0] - dx;
19478         this.initPageY = p[1] - dy;
19479
19480         this.lastPageX = p[0];
19481         this.lastPageY = p[1];
19482
19483
19484         this.setStartPosition(p);
19485     },
19486
19487     /**
19488      * Sets the start position of the element.  This is set when the obj
19489      * is initialized, the reset when a drag is started.
19490      * @method setStartPosition
19491      * @param pos current position (from previous lookup)
19492      * @private
19493      */
19494     setStartPosition: function(pos) {
19495         var p = pos || Dom.getXY( this.getEl() );
19496         this.deltaSetXY = null;
19497
19498         this.startPageX = p[0];
19499         this.startPageY = p[1];
19500     },
19501
19502     /**
19503      * Add this instance to a group of related drag/drop objects.  All
19504      * instances belong to at least one group, and can belong to as many
19505      * groups as needed.
19506      * @method addToGroup
19507      * @param sGroup {string} the name of the group
19508      */
19509     addToGroup: function(sGroup) {
19510         this.groups[sGroup] = true;
19511         this.DDM.regDragDrop(this, sGroup);
19512     },
19513
19514     /**
19515      * Remove's this instance from the supplied interaction group
19516      * @method removeFromGroup
19517      * @param {string}  sGroup  The group to drop
19518      */
19519     removeFromGroup: function(sGroup) {
19520         if (this.groups[sGroup]) {
19521             delete this.groups[sGroup];
19522         }
19523
19524         this.DDM.removeDDFromGroup(this, sGroup);
19525     },
19526
19527     /**
19528      * Allows you to specify that an element other than the linked element
19529      * will be moved with the cursor during a drag
19530      * @method setDragElId
19531      * @param id {string} the id of the element that will be used to initiate the drag
19532      */
19533     setDragElId: function(id) {
19534         this.dragElId = id;
19535     },
19536
19537     /**
19538      * Allows you to specify a child of the linked element that should be
19539      * used to initiate the drag operation.  An example of this would be if
19540      * you have a content div with text and links.  Clicking anywhere in the
19541      * content area would normally start the drag operation.  Use this method
19542      * to specify that an element inside of the content div is the element
19543      * that starts the drag operation.
19544      * @method setHandleElId
19545      * @param id {string} the id of the element that will be used to
19546      * initiate the drag.
19547      */
19548     setHandleElId: function(id) {
19549         if (typeof id !== "string") {
19550             id = Roo.id(id);
19551         }
19552         this.handleElId = id;
19553         this.DDM.regHandle(this.id, id);
19554     },
19555
19556     /**
19557      * Allows you to set an element outside of the linked element as a drag
19558      * handle
19559      * @method setOuterHandleElId
19560      * @param id the id of the element that will be used to initiate the drag
19561      */
19562     setOuterHandleElId: function(id) {
19563         if (typeof id !== "string") {
19564             id = Roo.id(id);
19565         }
19566         Event.on(id, "mousedown",
19567                 this.handleMouseDown, this);
19568         this.setHandleElId(id);
19569
19570         this.hasOuterHandles = true;
19571     },
19572
19573     /**
19574      * Remove all drag and drop hooks for this element
19575      * @method unreg
19576      */
19577     unreg: function() {
19578         Event.un(this.id, "mousedown",
19579                 this.handleMouseDown);
19580         Event.un(this.id, "touchstart",
19581                 this.handleMouseDown);
19582         this._domRef = null;
19583         this.DDM._remove(this);
19584     },
19585
19586     destroy : function(){
19587         this.unreg();
19588     },
19589
19590     /**
19591      * Returns true if this instance is locked, or the drag drop mgr is locked
19592      * (meaning that all drag/drop is disabled on the page.)
19593      * @method isLocked
19594      * @return {boolean} true if this obj or all drag/drop is locked, else
19595      * false
19596      */
19597     isLocked: function() {
19598         return (this.DDM.isLocked() || this.locked);
19599     },
19600
19601     /**
19602      * Fired when this object is clicked
19603      * @method handleMouseDown
19604      * @param {Event} e
19605      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19606      * @private
19607      */
19608     handleMouseDown: function(e, oDD){
19609      
19610         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19611             //Roo.log('not touch/ button !=0');
19612             return;
19613         }
19614         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19615             return; // double touch..
19616         }
19617         
19618
19619         if (this.isLocked()) {
19620             //Roo.log('locked');
19621             return;
19622         }
19623
19624         this.DDM.refreshCache(this.groups);
19625 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19626         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19627         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19628             //Roo.log('no outer handes or not over target');
19629                 // do nothing.
19630         } else {
19631 //            Roo.log('check validator');
19632             if (this.clickValidator(e)) {
19633 //                Roo.log('validate success');
19634                 // set the initial element position
19635                 this.setStartPosition();
19636
19637
19638                 this.b4MouseDown(e);
19639                 this.onMouseDown(e);
19640
19641                 this.DDM.handleMouseDown(e, this);
19642
19643                 this.DDM.stopEvent(e);
19644             } else {
19645
19646
19647             }
19648         }
19649     },
19650
19651     clickValidator: function(e) {
19652         var target = e.getTarget();
19653         return ( this.isValidHandleChild(target) &&
19654                     (this.id == this.handleElId ||
19655                         this.DDM.handleWasClicked(target, this.id)) );
19656     },
19657
19658     /**
19659      * Allows you to specify a tag name that should not start a drag operation
19660      * when clicked.  This is designed to facilitate embedding links within a
19661      * drag handle that do something other than start the drag.
19662      * @method addInvalidHandleType
19663      * @param {string} tagName the type of element to exclude
19664      */
19665     addInvalidHandleType: function(tagName) {
19666         var type = tagName.toUpperCase();
19667         this.invalidHandleTypes[type] = type;
19668     },
19669
19670     /**
19671      * Lets you to specify an element id for a child of a drag handle
19672      * that should not initiate a drag
19673      * @method addInvalidHandleId
19674      * @param {string} id the element id of the element you wish to ignore
19675      */
19676     addInvalidHandleId: function(id) {
19677         if (typeof id !== "string") {
19678             id = Roo.id(id);
19679         }
19680         this.invalidHandleIds[id] = id;
19681     },
19682
19683     /**
19684      * Lets you specify a css class of elements that will not initiate a drag
19685      * @method addInvalidHandleClass
19686      * @param {string} cssClass the class of the elements you wish to ignore
19687      */
19688     addInvalidHandleClass: function(cssClass) {
19689         this.invalidHandleClasses.push(cssClass);
19690     },
19691
19692     /**
19693      * Unsets an excluded tag name set by addInvalidHandleType
19694      * @method removeInvalidHandleType
19695      * @param {string} tagName the type of element to unexclude
19696      */
19697     removeInvalidHandleType: function(tagName) {
19698         var type = tagName.toUpperCase();
19699         // this.invalidHandleTypes[type] = null;
19700         delete this.invalidHandleTypes[type];
19701     },
19702
19703     /**
19704      * Unsets an invalid handle id
19705      * @method removeInvalidHandleId
19706      * @param {string} id the id of the element to re-enable
19707      */
19708     removeInvalidHandleId: function(id) {
19709         if (typeof id !== "string") {
19710             id = Roo.id(id);
19711         }
19712         delete this.invalidHandleIds[id];
19713     },
19714
19715     /**
19716      * Unsets an invalid css class
19717      * @method removeInvalidHandleClass
19718      * @param {string} cssClass the class of the element(s) you wish to
19719      * re-enable
19720      */
19721     removeInvalidHandleClass: function(cssClass) {
19722         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19723             if (this.invalidHandleClasses[i] == cssClass) {
19724                 delete this.invalidHandleClasses[i];
19725             }
19726         }
19727     },
19728
19729     /**
19730      * Checks the tag exclusion list to see if this click should be ignored
19731      * @method isValidHandleChild
19732      * @param {HTMLElement} node the HTMLElement to evaluate
19733      * @return {boolean} true if this is a valid tag type, false if not
19734      */
19735     isValidHandleChild: function(node) {
19736
19737         var valid = true;
19738         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19739         var nodeName;
19740         try {
19741             nodeName = node.nodeName.toUpperCase();
19742         } catch(e) {
19743             nodeName = node.nodeName;
19744         }
19745         valid = valid && !this.invalidHandleTypes[nodeName];
19746         valid = valid && !this.invalidHandleIds[node.id];
19747
19748         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19749             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19750         }
19751
19752
19753         return valid;
19754
19755     },
19756
19757     /**
19758      * Create the array of horizontal tick marks if an interval was specified
19759      * in setXConstraint().
19760      * @method setXTicks
19761      * @private
19762      */
19763     setXTicks: function(iStartX, iTickSize) {
19764         this.xTicks = [];
19765         this.xTickSize = iTickSize;
19766
19767         var tickMap = {};
19768
19769         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19770             if (!tickMap[i]) {
19771                 this.xTicks[this.xTicks.length] = i;
19772                 tickMap[i] = true;
19773             }
19774         }
19775
19776         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19777             if (!tickMap[i]) {
19778                 this.xTicks[this.xTicks.length] = i;
19779                 tickMap[i] = true;
19780             }
19781         }
19782
19783         this.xTicks.sort(this.DDM.numericSort) ;
19784     },
19785
19786     /**
19787      * Create the array of vertical tick marks if an interval was specified in
19788      * setYConstraint().
19789      * @method setYTicks
19790      * @private
19791      */
19792     setYTicks: function(iStartY, iTickSize) {
19793         this.yTicks = [];
19794         this.yTickSize = iTickSize;
19795
19796         var tickMap = {};
19797
19798         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19799             if (!tickMap[i]) {
19800                 this.yTicks[this.yTicks.length] = i;
19801                 tickMap[i] = true;
19802             }
19803         }
19804
19805         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19806             if (!tickMap[i]) {
19807                 this.yTicks[this.yTicks.length] = i;
19808                 tickMap[i] = true;
19809             }
19810         }
19811
19812         this.yTicks.sort(this.DDM.numericSort) ;
19813     },
19814
19815     /**
19816      * By default, the element can be dragged any place on the screen.  Use
19817      * this method to limit the horizontal travel of the element.  Pass in
19818      * 0,0 for the parameters if you want to lock the drag to the y axis.
19819      * @method setXConstraint
19820      * @param {int} iLeft the number of pixels the element can move to the left
19821      * @param {int} iRight the number of pixels the element can move to the
19822      * right
19823      * @param {int} iTickSize optional parameter for specifying that the
19824      * element
19825      * should move iTickSize pixels at a time.
19826      */
19827     setXConstraint: function(iLeft, iRight, iTickSize) {
19828         this.leftConstraint = iLeft;
19829         this.rightConstraint = iRight;
19830
19831         this.minX = this.initPageX - iLeft;
19832         this.maxX = this.initPageX + iRight;
19833         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19834
19835         this.constrainX = true;
19836     },
19837
19838     /**
19839      * Clears any constraints applied to this instance.  Also clears ticks
19840      * since they can't exist independent of a constraint at this time.
19841      * @method clearConstraints
19842      */
19843     clearConstraints: function() {
19844         this.constrainX = false;
19845         this.constrainY = false;
19846         this.clearTicks();
19847     },
19848
19849     /**
19850      * Clears any tick interval defined for this instance
19851      * @method clearTicks
19852      */
19853     clearTicks: function() {
19854         this.xTicks = null;
19855         this.yTicks = null;
19856         this.xTickSize = 0;
19857         this.yTickSize = 0;
19858     },
19859
19860     /**
19861      * By default, the element can be dragged any place on the screen.  Set
19862      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19863      * parameters if you want to lock the drag to the x axis.
19864      * @method setYConstraint
19865      * @param {int} iUp the number of pixels the element can move up
19866      * @param {int} iDown the number of pixels the element can move down
19867      * @param {int} iTickSize optional parameter for specifying that the
19868      * element should move iTickSize pixels at a time.
19869      */
19870     setYConstraint: function(iUp, iDown, iTickSize) {
19871         this.topConstraint = iUp;
19872         this.bottomConstraint = iDown;
19873
19874         this.minY = this.initPageY - iUp;
19875         this.maxY = this.initPageY + iDown;
19876         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19877
19878         this.constrainY = true;
19879
19880     },
19881
19882     /**
19883      * resetConstraints must be called if you manually reposition a dd element.
19884      * @method resetConstraints
19885      * @param {boolean} maintainOffset
19886      */
19887     resetConstraints: function() {
19888
19889
19890         // Maintain offsets if necessary
19891         if (this.initPageX || this.initPageX === 0) {
19892             // figure out how much this thing has moved
19893             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19894             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19895
19896             this.setInitPosition(dx, dy);
19897
19898         // This is the first time we have detected the element's position
19899         } else {
19900             this.setInitPosition();
19901         }
19902
19903         if (this.constrainX) {
19904             this.setXConstraint( this.leftConstraint,
19905                                  this.rightConstraint,
19906                                  this.xTickSize        );
19907         }
19908
19909         if (this.constrainY) {
19910             this.setYConstraint( this.topConstraint,
19911                                  this.bottomConstraint,
19912                                  this.yTickSize         );
19913         }
19914     },
19915
19916     /**
19917      * Normally the drag element is moved pixel by pixel, but we can specify
19918      * that it move a number of pixels at a time.  This method resolves the
19919      * location when we have it set up like this.
19920      * @method getTick
19921      * @param {int} val where we want to place the object
19922      * @param {int[]} tickArray sorted array of valid points
19923      * @return {int} the closest tick
19924      * @private
19925      */
19926     getTick: function(val, tickArray) {
19927
19928         if (!tickArray) {
19929             // If tick interval is not defined, it is effectively 1 pixel,
19930             // so we return the value passed to us.
19931             return val;
19932         } else if (tickArray[0] >= val) {
19933             // The value is lower than the first tick, so we return the first
19934             // tick.
19935             return tickArray[0];
19936         } else {
19937             for (var i=0, len=tickArray.length; i<len; ++i) {
19938                 var next = i + 1;
19939                 if (tickArray[next] && tickArray[next] >= val) {
19940                     var diff1 = val - tickArray[i];
19941                     var diff2 = tickArray[next] - val;
19942                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19943                 }
19944             }
19945
19946             // The value is larger than the last tick, so we return the last
19947             // tick.
19948             return tickArray[tickArray.length - 1];
19949         }
19950     },
19951
19952     /**
19953      * toString method
19954      * @method toString
19955      * @return {string} string representation of the dd obj
19956      */
19957     toString: function() {
19958         return ("DragDrop " + this.id);
19959     }
19960
19961 });
19962
19963 })();
19964 /*
19965  * Based on:
19966  * Ext JS Library 1.1.1
19967  * Copyright(c) 2006-2007, Ext JS, LLC.
19968  *
19969  * Originally Released Under LGPL - original licence link has changed is not relivant.
19970  *
19971  * Fork - LGPL
19972  * <script type="text/javascript">
19973  */
19974
19975
19976 /**
19977  * The drag and drop utility provides a framework for building drag and drop
19978  * applications.  In addition to enabling drag and drop for specific elements,
19979  * the drag and drop elements are tracked by the manager class, and the
19980  * interactions between the various elements are tracked during the drag and
19981  * the implementing code is notified about these important moments.
19982  */
19983
19984 // Only load the library once.  Rewriting the manager class would orphan
19985 // existing drag and drop instances.
19986 if (!Roo.dd.DragDropMgr) {
19987
19988 /**
19989  * @class Roo.dd.DragDropMgr
19990  * DragDropMgr is a singleton that tracks the element interaction for
19991  * all DragDrop items in the window.  Generally, you will not call
19992  * this class directly, but it does have helper methods that could
19993  * be useful in your DragDrop implementations.
19994  * @singleton
19995  */
19996 Roo.dd.DragDropMgr = function() {
19997
19998     var Event = Roo.EventManager;
19999
20000     return {
20001
20002         /**
20003          * Two dimensional Array of registered DragDrop objects.  The first
20004          * dimension is the DragDrop item group, the second the DragDrop
20005          * object.
20006          * @property ids
20007          * @type {string: string}
20008          * @private
20009          * @static
20010          */
20011         ids: {},
20012
20013         /**
20014          * Array of element ids defined as drag handles.  Used to determine
20015          * if the element that generated the mousedown event is actually the
20016          * handle and not the html element itself.
20017          * @property handleIds
20018          * @type {string: string}
20019          * @private
20020          * @static
20021          */
20022         handleIds: {},
20023
20024         /**
20025          * the DragDrop object that is currently being dragged
20026          * @property dragCurrent
20027          * @type DragDrop
20028          * @private
20029          * @static
20030          **/
20031         dragCurrent: null,
20032
20033         /**
20034          * the DragDrop object(s) that are being hovered over
20035          * @property dragOvers
20036          * @type Array
20037          * @private
20038          * @static
20039          */
20040         dragOvers: {},
20041
20042         /**
20043          * the X distance between the cursor and the object being dragged
20044          * @property deltaX
20045          * @type int
20046          * @private
20047          * @static
20048          */
20049         deltaX: 0,
20050
20051         /**
20052          * the Y distance between the cursor and the object being dragged
20053          * @property deltaY
20054          * @type int
20055          * @private
20056          * @static
20057          */
20058         deltaY: 0,
20059
20060         /**
20061          * Flag to determine if we should prevent the default behavior of the
20062          * events we define. By default this is true, but this can be set to
20063          * false if you need the default behavior (not recommended)
20064          * @property preventDefault
20065          * @type boolean
20066          * @static
20067          */
20068         preventDefault: true,
20069
20070         /**
20071          * Flag to determine if we should stop the propagation of the events
20072          * we generate. This is true by default but you may want to set it to
20073          * false if the html element contains other features that require the
20074          * mouse click.
20075          * @property stopPropagation
20076          * @type boolean
20077          * @static
20078          */
20079         stopPropagation: true,
20080
20081         /**
20082          * Internal flag that is set to true when drag and drop has been
20083          * intialized
20084          * @property initialized
20085          * @private
20086          * @static
20087          */
20088         initalized: false,
20089
20090         /**
20091          * All drag and drop can be disabled.
20092          * @property locked
20093          * @private
20094          * @static
20095          */
20096         locked: false,
20097
20098         /**
20099          * Called the first time an element is registered.
20100          * @method init
20101          * @private
20102          * @static
20103          */
20104         init: function() {
20105             this.initialized = true;
20106         },
20107
20108         /**
20109          * In point mode, drag and drop interaction is defined by the
20110          * location of the cursor during the drag/drop
20111          * @property POINT
20112          * @type int
20113          * @static
20114          */
20115         POINT: 0,
20116
20117         /**
20118          * In intersect mode, drag and drop interactio nis defined by the
20119          * overlap of two or more drag and drop objects.
20120          * @property INTERSECT
20121          * @type int
20122          * @static
20123          */
20124         INTERSECT: 1,
20125
20126         /**
20127          * The current drag and drop mode.  Default: POINT
20128          * @property mode
20129          * @type int
20130          * @static
20131          */
20132         mode: 0,
20133
20134         /**
20135          * Runs method on all drag and drop objects
20136          * @method _execOnAll
20137          * @private
20138          * @static
20139          */
20140         _execOnAll: function(sMethod, args) {
20141             for (var i in this.ids) {
20142                 for (var j in this.ids[i]) {
20143                     var oDD = this.ids[i][j];
20144                     if (! this.isTypeOfDD(oDD)) {
20145                         continue;
20146                     }
20147                     oDD[sMethod].apply(oDD, args);
20148                 }
20149             }
20150         },
20151
20152         /**
20153          * Drag and drop initialization.  Sets up the global event handlers
20154          * @method _onLoad
20155          * @private
20156          * @static
20157          */
20158         _onLoad: function() {
20159
20160             this.init();
20161
20162             if (!Roo.isTouch) {
20163                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20164                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20165             }
20166             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20167             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20168             
20169             Event.on(window,   "unload",    this._onUnload, this, true);
20170             Event.on(window,   "resize",    this._onResize, this, true);
20171             // Event.on(window,   "mouseout",    this._test);
20172
20173         },
20174
20175         /**
20176          * Reset constraints on all drag and drop objs
20177          * @method _onResize
20178          * @private
20179          * @static
20180          */
20181         _onResize: function(e) {
20182             this._execOnAll("resetConstraints", []);
20183         },
20184
20185         /**
20186          * Lock all drag and drop functionality
20187          * @method lock
20188          * @static
20189          */
20190         lock: function() { this.locked = true; },
20191
20192         /**
20193          * Unlock all drag and drop functionality
20194          * @method unlock
20195          * @static
20196          */
20197         unlock: function() { this.locked = false; },
20198
20199         /**
20200          * Is drag and drop locked?
20201          * @method isLocked
20202          * @return {boolean} True if drag and drop is locked, false otherwise.
20203          * @static
20204          */
20205         isLocked: function() { return this.locked; },
20206
20207         /**
20208          * Location cache that is set for all drag drop objects when a drag is
20209          * initiated, cleared when the drag is finished.
20210          * @property locationCache
20211          * @private
20212          * @static
20213          */
20214         locationCache: {},
20215
20216         /**
20217          * Set useCache to false if you want to force object the lookup of each
20218          * drag and drop linked element constantly during a drag.
20219          * @property useCache
20220          * @type boolean
20221          * @static
20222          */
20223         useCache: true,
20224
20225         /**
20226          * The number of pixels that the mouse needs to move after the
20227          * mousedown before the drag is initiated.  Default=3;
20228          * @property clickPixelThresh
20229          * @type int
20230          * @static
20231          */
20232         clickPixelThresh: 3,
20233
20234         /**
20235          * The number of milliseconds after the mousedown event to initiate the
20236          * drag if we don't get a mouseup event. Default=1000
20237          * @property clickTimeThresh
20238          * @type int
20239          * @static
20240          */
20241         clickTimeThresh: 350,
20242
20243         /**
20244          * Flag that indicates that either the drag pixel threshold or the
20245          * mousdown time threshold has been met
20246          * @property dragThreshMet
20247          * @type boolean
20248          * @private
20249          * @static
20250          */
20251         dragThreshMet: false,
20252
20253         /**
20254          * Timeout used for the click time threshold
20255          * @property clickTimeout
20256          * @type Object
20257          * @private
20258          * @static
20259          */
20260         clickTimeout: null,
20261
20262         /**
20263          * The X position of the mousedown event stored for later use when a
20264          * drag threshold is met.
20265          * @property startX
20266          * @type int
20267          * @private
20268          * @static
20269          */
20270         startX: 0,
20271
20272         /**
20273          * The Y position of the mousedown event stored for later use when a
20274          * drag threshold is met.
20275          * @property startY
20276          * @type int
20277          * @private
20278          * @static
20279          */
20280         startY: 0,
20281
20282         /**
20283          * Each DragDrop instance must be registered with the DragDropMgr.
20284          * This is executed in DragDrop.init()
20285          * @method regDragDrop
20286          * @param {DragDrop} oDD the DragDrop object to register
20287          * @param {String} sGroup the name of the group this element belongs to
20288          * @static
20289          */
20290         regDragDrop: function(oDD, sGroup) {
20291             if (!this.initialized) { this.init(); }
20292
20293             if (!this.ids[sGroup]) {
20294                 this.ids[sGroup] = {};
20295             }
20296             this.ids[sGroup][oDD.id] = oDD;
20297         },
20298
20299         /**
20300          * Removes the supplied dd instance from the supplied group. Executed
20301          * by DragDrop.removeFromGroup, so don't call this function directly.
20302          * @method removeDDFromGroup
20303          * @private
20304          * @static
20305          */
20306         removeDDFromGroup: function(oDD, sGroup) {
20307             if (!this.ids[sGroup]) {
20308                 this.ids[sGroup] = {};
20309             }
20310
20311             var obj = this.ids[sGroup];
20312             if (obj && obj[oDD.id]) {
20313                 delete obj[oDD.id];
20314             }
20315         },
20316
20317         /**
20318          * Unregisters a drag and drop item.  This is executed in
20319          * DragDrop.unreg, use that method instead of calling this directly.
20320          * @method _remove
20321          * @private
20322          * @static
20323          */
20324         _remove: function(oDD) {
20325             for (var g in oDD.groups) {
20326                 if (g && this.ids[g][oDD.id]) {
20327                     delete this.ids[g][oDD.id];
20328                 }
20329             }
20330             delete this.handleIds[oDD.id];
20331         },
20332
20333         /**
20334          * Each DragDrop handle element must be registered.  This is done
20335          * automatically when executing DragDrop.setHandleElId()
20336          * @method regHandle
20337          * @param {String} sDDId the DragDrop id this element is a handle for
20338          * @param {String} sHandleId the id of the element that is the drag
20339          * handle
20340          * @static
20341          */
20342         regHandle: function(sDDId, sHandleId) {
20343             if (!this.handleIds[sDDId]) {
20344                 this.handleIds[sDDId] = {};
20345             }
20346             this.handleIds[sDDId][sHandleId] = sHandleId;
20347         },
20348
20349         /**
20350          * Utility function to determine if a given element has been
20351          * registered as a drag drop item.
20352          * @method isDragDrop
20353          * @param {String} id the element id to check
20354          * @return {boolean} true if this element is a DragDrop item,
20355          * false otherwise
20356          * @static
20357          */
20358         isDragDrop: function(id) {
20359             return ( this.getDDById(id) ) ? true : false;
20360         },
20361
20362         /**
20363          * Returns the drag and drop instances that are in all groups the
20364          * passed in instance belongs to.
20365          * @method getRelated
20366          * @param {DragDrop} p_oDD the obj to get related data for
20367          * @param {boolean} bTargetsOnly if true, only return targetable objs
20368          * @return {DragDrop[]} the related instances
20369          * @static
20370          */
20371         getRelated: function(p_oDD, bTargetsOnly) {
20372             var oDDs = [];
20373             for (var i in p_oDD.groups) {
20374                 for (j in this.ids[i]) {
20375                     var dd = this.ids[i][j];
20376                     if (! this.isTypeOfDD(dd)) {
20377                         continue;
20378                     }
20379                     if (!bTargetsOnly || dd.isTarget) {
20380                         oDDs[oDDs.length] = dd;
20381                     }
20382                 }
20383             }
20384
20385             return oDDs;
20386         },
20387
20388         /**
20389          * Returns true if the specified dd target is a legal target for
20390          * the specifice drag obj
20391          * @method isLegalTarget
20392          * @param {DragDrop} the drag obj
20393          * @param {DragDrop} the target
20394          * @return {boolean} true if the target is a legal target for the
20395          * dd obj
20396          * @static
20397          */
20398         isLegalTarget: function (oDD, oTargetDD) {
20399             var targets = this.getRelated(oDD, true);
20400             for (var i=0, len=targets.length;i<len;++i) {
20401                 if (targets[i].id == oTargetDD.id) {
20402                     return true;
20403                 }
20404             }
20405
20406             return false;
20407         },
20408
20409         /**
20410          * My goal is to be able to transparently determine if an object is
20411          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20412          * returns "object", oDD.constructor.toString() always returns
20413          * "DragDrop" and not the name of the subclass.  So for now it just
20414          * evaluates a well-known variable in DragDrop.
20415          * @method isTypeOfDD
20416          * @param {Object} the object to evaluate
20417          * @return {boolean} true if typeof oDD = DragDrop
20418          * @static
20419          */
20420         isTypeOfDD: function (oDD) {
20421             return (oDD && oDD.__ygDragDrop);
20422         },
20423
20424         /**
20425          * Utility function to determine if a given element has been
20426          * registered as a drag drop handle for the given Drag Drop object.
20427          * @method isHandle
20428          * @param {String} id the element id to check
20429          * @return {boolean} true if this element is a DragDrop handle, false
20430          * otherwise
20431          * @static
20432          */
20433         isHandle: function(sDDId, sHandleId) {
20434             return ( this.handleIds[sDDId] &&
20435                             this.handleIds[sDDId][sHandleId] );
20436         },
20437
20438         /**
20439          * Returns the DragDrop instance for a given id
20440          * @method getDDById
20441          * @param {String} id the id of the DragDrop object
20442          * @return {DragDrop} the drag drop object, null if it is not found
20443          * @static
20444          */
20445         getDDById: function(id) {
20446             for (var i in this.ids) {
20447                 if (this.ids[i][id]) {
20448                     return this.ids[i][id];
20449                 }
20450             }
20451             return null;
20452         },
20453
20454         /**
20455          * Fired after a registered DragDrop object gets the mousedown event.
20456          * Sets up the events required to track the object being dragged
20457          * @method handleMouseDown
20458          * @param {Event} e the event
20459          * @param oDD the DragDrop object being dragged
20460          * @private
20461          * @static
20462          */
20463         handleMouseDown: function(e, oDD) {
20464             if(Roo.QuickTips){
20465                 Roo.QuickTips.disable();
20466             }
20467             this.currentTarget = e.getTarget();
20468
20469             this.dragCurrent = oDD;
20470
20471             var el = oDD.getEl();
20472
20473             // track start position
20474             this.startX = e.getPageX();
20475             this.startY = e.getPageY();
20476
20477             this.deltaX = this.startX - el.offsetLeft;
20478             this.deltaY = this.startY - el.offsetTop;
20479
20480             this.dragThreshMet = false;
20481
20482             this.clickTimeout = setTimeout(
20483                     function() {
20484                         var DDM = Roo.dd.DDM;
20485                         DDM.startDrag(DDM.startX, DDM.startY);
20486                     },
20487                     this.clickTimeThresh );
20488         },
20489
20490         /**
20491          * Fired when either the drag pixel threshol or the mousedown hold
20492          * time threshold has been met.
20493          * @method startDrag
20494          * @param x {int} the X position of the original mousedown
20495          * @param y {int} the Y position of the original mousedown
20496          * @static
20497          */
20498         startDrag: function(x, y) {
20499             clearTimeout(this.clickTimeout);
20500             if (this.dragCurrent) {
20501                 this.dragCurrent.b4StartDrag(x, y);
20502                 this.dragCurrent.startDrag(x, y);
20503             }
20504             this.dragThreshMet = true;
20505         },
20506
20507         /**
20508          * Internal function to handle the mouseup event.  Will be invoked
20509          * from the context of the document.
20510          * @method handleMouseUp
20511          * @param {Event} e the event
20512          * @private
20513          * @static
20514          */
20515         handleMouseUp: function(e) {
20516
20517             if(Roo.QuickTips){
20518                 Roo.QuickTips.enable();
20519             }
20520             if (! this.dragCurrent) {
20521                 return;
20522             }
20523
20524             clearTimeout(this.clickTimeout);
20525
20526             if (this.dragThreshMet) {
20527                 this.fireEvents(e, true);
20528             } else {
20529             }
20530
20531             this.stopDrag(e);
20532
20533             this.stopEvent(e);
20534         },
20535
20536         /**
20537          * Utility to stop event propagation and event default, if these
20538          * features are turned on.
20539          * @method stopEvent
20540          * @param {Event} e the event as returned by this.getEvent()
20541          * @static
20542          */
20543         stopEvent: function(e){
20544             if(this.stopPropagation) {
20545                 e.stopPropagation();
20546             }
20547
20548             if (this.preventDefault) {
20549                 e.preventDefault();
20550             }
20551         },
20552
20553         /**
20554          * Internal function to clean up event handlers after the drag
20555          * operation is complete
20556          * @method stopDrag
20557          * @param {Event} e the event
20558          * @private
20559          * @static
20560          */
20561         stopDrag: function(e) {
20562             // Fire the drag end event for the item that was dragged
20563             if (this.dragCurrent) {
20564                 if (this.dragThreshMet) {
20565                     this.dragCurrent.b4EndDrag(e);
20566                     this.dragCurrent.endDrag(e);
20567                 }
20568
20569                 this.dragCurrent.onMouseUp(e);
20570             }
20571
20572             this.dragCurrent = null;
20573             this.dragOvers = {};
20574         },
20575
20576         /**
20577          * Internal function to handle the mousemove event.  Will be invoked
20578          * from the context of the html element.
20579          *
20580          * @TODO figure out what we can do about mouse events lost when the
20581          * user drags objects beyond the window boundary.  Currently we can
20582          * detect this in internet explorer by verifying that the mouse is
20583          * down during the mousemove event.  Firefox doesn't give us the
20584          * button state on the mousemove event.
20585          * @method handleMouseMove
20586          * @param {Event} e the event
20587          * @private
20588          * @static
20589          */
20590         handleMouseMove: function(e) {
20591             if (! this.dragCurrent) {
20592                 return true;
20593             }
20594
20595             // var button = e.which || e.button;
20596
20597             // check for IE mouseup outside of page boundary
20598             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20599                 this.stopEvent(e);
20600                 return this.handleMouseUp(e);
20601             }
20602
20603             if (!this.dragThreshMet) {
20604                 var diffX = Math.abs(this.startX - e.getPageX());
20605                 var diffY = Math.abs(this.startY - e.getPageY());
20606                 if (diffX > this.clickPixelThresh ||
20607                             diffY > this.clickPixelThresh) {
20608                     this.startDrag(this.startX, this.startY);
20609                 }
20610             }
20611
20612             if (this.dragThreshMet) {
20613                 this.dragCurrent.b4Drag(e);
20614                 this.dragCurrent.onDrag(e);
20615                 if(!this.dragCurrent.moveOnly){
20616                     this.fireEvents(e, false);
20617                 }
20618             }
20619
20620             this.stopEvent(e);
20621
20622             return true;
20623         },
20624
20625         /**
20626          * Iterates over all of the DragDrop elements to find ones we are
20627          * hovering over or dropping on
20628          * @method fireEvents
20629          * @param {Event} e the event
20630          * @param {boolean} isDrop is this a drop op or a mouseover op?
20631          * @private
20632          * @static
20633          */
20634         fireEvents: function(e, isDrop) {
20635             var dc = this.dragCurrent;
20636
20637             // If the user did the mouse up outside of the window, we could
20638             // get here even though we have ended the drag.
20639             if (!dc || dc.isLocked()) {
20640                 return;
20641             }
20642
20643             var pt = e.getPoint();
20644
20645             // cache the previous dragOver array
20646             var oldOvers = [];
20647
20648             var outEvts   = [];
20649             var overEvts  = [];
20650             var dropEvts  = [];
20651             var enterEvts = [];
20652
20653             // Check to see if the object(s) we were hovering over is no longer
20654             // being hovered over so we can fire the onDragOut event
20655             for (var i in this.dragOvers) {
20656
20657                 var ddo = this.dragOvers[i];
20658
20659                 if (! this.isTypeOfDD(ddo)) {
20660                     continue;
20661                 }
20662
20663                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20664                     outEvts.push( ddo );
20665                 }
20666
20667                 oldOvers[i] = true;
20668                 delete this.dragOvers[i];
20669             }
20670
20671             for (var sGroup in dc.groups) {
20672
20673                 if ("string" != typeof sGroup) {
20674                     continue;
20675                 }
20676
20677                 for (i in this.ids[sGroup]) {
20678                     var oDD = this.ids[sGroup][i];
20679                     if (! this.isTypeOfDD(oDD)) {
20680                         continue;
20681                     }
20682
20683                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20684                         if (this.isOverTarget(pt, oDD, this.mode)) {
20685                             // look for drop interactions
20686                             if (isDrop) {
20687                                 dropEvts.push( oDD );
20688                             // look for drag enter and drag over interactions
20689                             } else {
20690
20691                                 // initial drag over: dragEnter fires
20692                                 if (!oldOvers[oDD.id]) {
20693                                     enterEvts.push( oDD );
20694                                 // subsequent drag overs: dragOver fires
20695                                 } else {
20696                                     overEvts.push( oDD );
20697                                 }
20698
20699                                 this.dragOvers[oDD.id] = oDD;
20700                             }
20701                         }
20702                     }
20703                 }
20704             }
20705
20706             if (this.mode) {
20707                 if (outEvts.length) {
20708                     dc.b4DragOut(e, outEvts);
20709                     dc.onDragOut(e, outEvts);
20710                 }
20711
20712                 if (enterEvts.length) {
20713                     dc.onDragEnter(e, enterEvts);
20714                 }
20715
20716                 if (overEvts.length) {
20717                     dc.b4DragOver(e, overEvts);
20718                     dc.onDragOver(e, overEvts);
20719                 }
20720
20721                 if (dropEvts.length) {
20722                     dc.b4DragDrop(e, dropEvts);
20723                     dc.onDragDrop(e, dropEvts);
20724                 }
20725
20726             } else {
20727                 // fire dragout events
20728                 var len = 0;
20729                 for (i=0, len=outEvts.length; i<len; ++i) {
20730                     dc.b4DragOut(e, outEvts[i].id);
20731                     dc.onDragOut(e, outEvts[i].id);
20732                 }
20733
20734                 // fire enter events
20735                 for (i=0,len=enterEvts.length; i<len; ++i) {
20736                     // dc.b4DragEnter(e, oDD.id);
20737                     dc.onDragEnter(e, enterEvts[i].id);
20738                 }
20739
20740                 // fire over events
20741                 for (i=0,len=overEvts.length; i<len; ++i) {
20742                     dc.b4DragOver(e, overEvts[i].id);
20743                     dc.onDragOver(e, overEvts[i].id);
20744                 }
20745
20746                 // fire drop events
20747                 for (i=0, len=dropEvts.length; i<len; ++i) {
20748                     dc.b4DragDrop(e, dropEvts[i].id);
20749                     dc.onDragDrop(e, dropEvts[i].id);
20750                 }
20751
20752             }
20753
20754             // notify about a drop that did not find a target
20755             if (isDrop && !dropEvts.length) {
20756                 dc.onInvalidDrop(e);
20757             }
20758
20759         },
20760
20761         /**
20762          * Helper function for getting the best match from the list of drag
20763          * and drop objects returned by the drag and drop events when we are
20764          * in INTERSECT mode.  It returns either the first object that the
20765          * cursor is over, or the object that has the greatest overlap with
20766          * the dragged element.
20767          * @method getBestMatch
20768          * @param  {DragDrop[]} dds The array of drag and drop objects
20769          * targeted
20770          * @return {DragDrop}       The best single match
20771          * @static
20772          */
20773         getBestMatch: function(dds) {
20774             var winner = null;
20775             // Return null if the input is not what we expect
20776             //if (!dds || !dds.length || dds.length == 0) {
20777                // winner = null;
20778             // If there is only one item, it wins
20779             //} else if (dds.length == 1) {
20780
20781             var len = dds.length;
20782
20783             if (len == 1) {
20784                 winner = dds[0];
20785             } else {
20786                 // Loop through the targeted items
20787                 for (var i=0; i<len; ++i) {
20788                     var dd = dds[i];
20789                     // If the cursor is over the object, it wins.  If the
20790                     // cursor is over multiple matches, the first one we come
20791                     // to wins.
20792                     if (dd.cursorIsOver) {
20793                         winner = dd;
20794                         break;
20795                     // Otherwise the object with the most overlap wins
20796                     } else {
20797                         if (!winner ||
20798                             winner.overlap.getArea() < dd.overlap.getArea()) {
20799                             winner = dd;
20800                         }
20801                     }
20802                 }
20803             }
20804
20805             return winner;
20806         },
20807
20808         /**
20809          * Refreshes the cache of the top-left and bottom-right points of the
20810          * drag and drop objects in the specified group(s).  This is in the
20811          * format that is stored in the drag and drop instance, so typical
20812          * usage is:
20813          * <code>
20814          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20815          * </code>
20816          * Alternatively:
20817          * <code>
20818          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20819          * </code>
20820          * @TODO this really should be an indexed array.  Alternatively this
20821          * method could accept both.
20822          * @method refreshCache
20823          * @param {Object} groups an associative array of groups to refresh
20824          * @static
20825          */
20826         refreshCache: function(groups) {
20827             for (var sGroup in groups) {
20828                 if ("string" != typeof sGroup) {
20829                     continue;
20830                 }
20831                 for (var i in this.ids[sGroup]) {
20832                     var oDD = this.ids[sGroup][i];
20833
20834                     if (this.isTypeOfDD(oDD)) {
20835                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20836                         var loc = this.getLocation(oDD);
20837                         if (loc) {
20838                             this.locationCache[oDD.id] = loc;
20839                         } else {
20840                             delete this.locationCache[oDD.id];
20841                             // this will unregister the drag and drop object if
20842                             // the element is not in a usable state
20843                             // oDD.unreg();
20844                         }
20845                     }
20846                 }
20847             }
20848         },
20849
20850         /**
20851          * This checks to make sure an element exists and is in the DOM.  The
20852          * main purpose is to handle cases where innerHTML is used to remove
20853          * drag and drop objects from the DOM.  IE provides an 'unspecified
20854          * error' when trying to access the offsetParent of such an element
20855          * @method verifyEl
20856          * @param {HTMLElement} el the element to check
20857          * @return {boolean} true if the element looks usable
20858          * @static
20859          */
20860         verifyEl: function(el) {
20861             if (el) {
20862                 var parent;
20863                 if(Roo.isIE){
20864                     try{
20865                         parent = el.offsetParent;
20866                     }catch(e){}
20867                 }else{
20868                     parent = el.offsetParent;
20869                 }
20870                 if (parent) {
20871                     return true;
20872                 }
20873             }
20874
20875             return false;
20876         },
20877
20878         /**
20879          * Returns a Region object containing the drag and drop element's position
20880          * and size, including the padding configured for it
20881          * @method getLocation
20882          * @param {DragDrop} oDD the drag and drop object to get the
20883          *                       location for
20884          * @return {Roo.lib.Region} a Region object representing the total area
20885          *                             the element occupies, including any padding
20886          *                             the instance is configured for.
20887          * @static
20888          */
20889         getLocation: function(oDD) {
20890             if (! this.isTypeOfDD(oDD)) {
20891                 return null;
20892             }
20893
20894             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20895
20896             try {
20897                 pos= Roo.lib.Dom.getXY(el);
20898             } catch (e) { }
20899
20900             if (!pos) {
20901                 return null;
20902             }
20903
20904             x1 = pos[0];
20905             x2 = x1 + el.offsetWidth;
20906             y1 = pos[1];
20907             y2 = y1 + el.offsetHeight;
20908
20909             t = y1 - oDD.padding[0];
20910             r = x2 + oDD.padding[1];
20911             b = y2 + oDD.padding[2];
20912             l = x1 - oDD.padding[3];
20913
20914             return new Roo.lib.Region( t, r, b, l );
20915         },
20916
20917         /**
20918          * Checks the cursor location to see if it over the target
20919          * @method isOverTarget
20920          * @param {Roo.lib.Point} pt The point to evaluate
20921          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20922          * @return {boolean} true if the mouse is over the target
20923          * @private
20924          * @static
20925          */
20926         isOverTarget: function(pt, oTarget, intersect) {
20927             // use cache if available
20928             var loc = this.locationCache[oTarget.id];
20929             if (!loc || !this.useCache) {
20930                 loc = this.getLocation(oTarget);
20931                 this.locationCache[oTarget.id] = loc;
20932
20933             }
20934
20935             if (!loc) {
20936                 return false;
20937             }
20938
20939             oTarget.cursorIsOver = loc.contains( pt );
20940
20941             // DragDrop is using this as a sanity check for the initial mousedown
20942             // in this case we are done.  In POINT mode, if the drag obj has no
20943             // contraints, we are also done. Otherwise we need to evaluate the
20944             // location of the target as related to the actual location of the
20945             // dragged element.
20946             var dc = this.dragCurrent;
20947             if (!dc || !dc.getTargetCoord ||
20948                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20949                 return oTarget.cursorIsOver;
20950             }
20951
20952             oTarget.overlap = null;
20953
20954             // Get the current location of the drag element, this is the
20955             // location of the mouse event less the delta that represents
20956             // where the original mousedown happened on the element.  We
20957             // need to consider constraints and ticks as well.
20958             var pos = dc.getTargetCoord(pt.x, pt.y);
20959
20960             var el = dc.getDragEl();
20961             var curRegion = new Roo.lib.Region( pos.y,
20962                                                    pos.x + el.offsetWidth,
20963                                                    pos.y + el.offsetHeight,
20964                                                    pos.x );
20965
20966             var overlap = curRegion.intersect(loc);
20967
20968             if (overlap) {
20969                 oTarget.overlap = overlap;
20970                 return (intersect) ? true : oTarget.cursorIsOver;
20971             } else {
20972                 return false;
20973             }
20974         },
20975
20976         /**
20977          * unload event handler
20978          * @method _onUnload
20979          * @private
20980          * @static
20981          */
20982         _onUnload: function(e, me) {
20983             Roo.dd.DragDropMgr.unregAll();
20984         },
20985
20986         /**
20987          * Cleans up the drag and drop events and objects.
20988          * @method unregAll
20989          * @private
20990          * @static
20991          */
20992         unregAll: function() {
20993
20994             if (this.dragCurrent) {
20995                 this.stopDrag();
20996                 this.dragCurrent = null;
20997             }
20998
20999             this._execOnAll("unreg", []);
21000
21001             for (i in this.elementCache) {
21002                 delete this.elementCache[i];
21003             }
21004
21005             this.elementCache = {};
21006             this.ids = {};
21007         },
21008
21009         /**
21010          * A cache of DOM elements
21011          * @property elementCache
21012          * @private
21013          * @static
21014          */
21015         elementCache: {},
21016
21017         /**
21018          * Get the wrapper for the DOM element specified
21019          * @method getElWrapper
21020          * @param {String} id the id of the element to get
21021          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21022          * @private
21023          * @deprecated This wrapper isn't that useful
21024          * @static
21025          */
21026         getElWrapper: function(id) {
21027             var oWrapper = this.elementCache[id];
21028             if (!oWrapper || !oWrapper.el) {
21029                 oWrapper = this.elementCache[id] =
21030                     new this.ElementWrapper(Roo.getDom(id));
21031             }
21032             return oWrapper;
21033         },
21034
21035         /**
21036          * Returns the actual DOM element
21037          * @method getElement
21038          * @param {String} id the id of the elment to get
21039          * @return {Object} The element
21040          * @deprecated use Roo.getDom instead
21041          * @static
21042          */
21043         getElement: function(id) {
21044             return Roo.getDom(id);
21045         },
21046
21047         /**
21048          * Returns the style property for the DOM element (i.e.,
21049          * document.getElById(id).style)
21050          * @method getCss
21051          * @param {String} id the id of the elment to get
21052          * @return {Object} The style property of the element
21053          * @deprecated use Roo.getDom instead
21054          * @static
21055          */
21056         getCss: function(id) {
21057             var el = Roo.getDom(id);
21058             return (el) ? el.style : null;
21059         },
21060
21061         /**
21062          * Inner class for cached elements
21063          * @class DragDropMgr.ElementWrapper
21064          * @for DragDropMgr
21065          * @private
21066          * @deprecated
21067          */
21068         ElementWrapper: function(el) {
21069                 /**
21070                  * The element
21071                  * @property el
21072                  */
21073                 this.el = el || null;
21074                 /**
21075                  * The element id
21076                  * @property id
21077                  */
21078                 this.id = this.el && el.id;
21079                 /**
21080                  * A reference to the style property
21081                  * @property css
21082                  */
21083                 this.css = this.el && el.style;
21084             },
21085
21086         /**
21087          * Returns the X position of an html element
21088          * @method getPosX
21089          * @param el the element for which to get the position
21090          * @return {int} the X coordinate
21091          * @for DragDropMgr
21092          * @deprecated use Roo.lib.Dom.getX instead
21093          * @static
21094          */
21095         getPosX: function(el) {
21096             return Roo.lib.Dom.getX(el);
21097         },
21098
21099         /**
21100          * Returns the Y position of an html element
21101          * @method getPosY
21102          * @param el the element for which to get the position
21103          * @return {int} the Y coordinate
21104          * @deprecated use Roo.lib.Dom.getY instead
21105          * @static
21106          */
21107         getPosY: function(el) {
21108             return Roo.lib.Dom.getY(el);
21109         },
21110
21111         /**
21112          * Swap two nodes.  In IE, we use the native method, for others we
21113          * emulate the IE behavior
21114          * @method swapNode
21115          * @param n1 the first node to swap
21116          * @param n2 the other node to swap
21117          * @static
21118          */
21119         swapNode: function(n1, n2) {
21120             if (n1.swapNode) {
21121                 n1.swapNode(n2);
21122             } else {
21123                 var p = n2.parentNode;
21124                 var s = n2.nextSibling;
21125
21126                 if (s == n1) {
21127                     p.insertBefore(n1, n2);
21128                 } else if (n2 == n1.nextSibling) {
21129                     p.insertBefore(n2, n1);
21130                 } else {
21131                     n1.parentNode.replaceChild(n2, n1);
21132                     p.insertBefore(n1, s);
21133                 }
21134             }
21135         },
21136
21137         /**
21138          * Returns the current scroll position
21139          * @method getScroll
21140          * @private
21141          * @static
21142          */
21143         getScroll: function () {
21144             var t, l, dde=document.documentElement, db=document.body;
21145             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21146                 t = dde.scrollTop;
21147                 l = dde.scrollLeft;
21148             } else if (db) {
21149                 t = db.scrollTop;
21150                 l = db.scrollLeft;
21151             } else {
21152
21153             }
21154             return { top: t, left: l };
21155         },
21156
21157         /**
21158          * Returns the specified element style property
21159          * @method getStyle
21160          * @param {HTMLElement} el          the element
21161          * @param {string}      styleProp   the style property
21162          * @return {string} The value of the style property
21163          * @deprecated use Roo.lib.Dom.getStyle
21164          * @static
21165          */
21166         getStyle: function(el, styleProp) {
21167             return Roo.fly(el).getStyle(styleProp);
21168         },
21169
21170         /**
21171          * Gets the scrollTop
21172          * @method getScrollTop
21173          * @return {int} the document's scrollTop
21174          * @static
21175          */
21176         getScrollTop: function () { return this.getScroll().top; },
21177
21178         /**
21179          * Gets the scrollLeft
21180          * @method getScrollLeft
21181          * @return {int} the document's scrollTop
21182          * @static
21183          */
21184         getScrollLeft: function () { return this.getScroll().left; },
21185
21186         /**
21187          * Sets the x/y position of an element to the location of the
21188          * target element.
21189          * @method moveToEl
21190          * @param {HTMLElement} moveEl      The element to move
21191          * @param {HTMLElement} targetEl    The position reference element
21192          * @static
21193          */
21194         moveToEl: function (moveEl, targetEl) {
21195             var aCoord = Roo.lib.Dom.getXY(targetEl);
21196             Roo.lib.Dom.setXY(moveEl, aCoord);
21197         },
21198
21199         /**
21200          * Numeric array sort function
21201          * @method numericSort
21202          * @static
21203          */
21204         numericSort: function(a, b) { return (a - b); },
21205
21206         /**
21207          * Internal counter
21208          * @property _timeoutCount
21209          * @private
21210          * @static
21211          */
21212         _timeoutCount: 0,
21213
21214         /**
21215          * Trying to make the load order less important.  Without this we get
21216          * an error if this file is loaded before the Event Utility.
21217          * @method _addListeners
21218          * @private
21219          * @static
21220          */
21221         _addListeners: function() {
21222             var DDM = Roo.dd.DDM;
21223             if ( Roo.lib.Event && document ) {
21224                 DDM._onLoad();
21225             } else {
21226                 if (DDM._timeoutCount > 2000) {
21227                 } else {
21228                     setTimeout(DDM._addListeners, 10);
21229                     if (document && document.body) {
21230                         DDM._timeoutCount += 1;
21231                     }
21232                 }
21233             }
21234         },
21235
21236         /**
21237          * Recursively searches the immediate parent and all child nodes for
21238          * the handle element in order to determine wheter or not it was
21239          * clicked.
21240          * @method handleWasClicked
21241          * @param node the html element to inspect
21242          * @static
21243          */
21244         handleWasClicked: function(node, id) {
21245             if (this.isHandle(id, node.id)) {
21246                 return true;
21247             } else {
21248                 // check to see if this is a text node child of the one we want
21249                 var p = node.parentNode;
21250
21251                 while (p) {
21252                     if (this.isHandle(id, p.id)) {
21253                         return true;
21254                     } else {
21255                         p = p.parentNode;
21256                     }
21257                 }
21258             }
21259
21260             return false;
21261         }
21262
21263     };
21264
21265 }();
21266
21267 // shorter alias, save a few bytes
21268 Roo.dd.DDM = Roo.dd.DragDropMgr;
21269 Roo.dd.DDM._addListeners();
21270
21271 }/*
21272  * Based on:
21273  * Ext JS Library 1.1.1
21274  * Copyright(c) 2006-2007, Ext JS, LLC.
21275  *
21276  * Originally Released Under LGPL - original licence link has changed is not relivant.
21277  *
21278  * Fork - LGPL
21279  * <script type="text/javascript">
21280  */
21281
21282 /**
21283  * @class Roo.dd.DD
21284  * A DragDrop implementation where the linked element follows the
21285  * mouse cursor during a drag.
21286  * @extends Roo.dd.DragDrop
21287  * @constructor
21288  * @param {String} id the id of the linked element
21289  * @param {String} sGroup the group of related DragDrop items
21290  * @param {object} config an object containing configurable attributes
21291  *                Valid properties for DD:
21292  *                    scroll
21293  */
21294 Roo.dd.DD = function(id, sGroup, config) {
21295     if (id) {
21296         this.init(id, sGroup, config);
21297     }
21298 };
21299
21300 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21301
21302     /**
21303      * When set to true, the utility automatically tries to scroll the browser
21304      * window wehn a drag and drop element is dragged near the viewport boundary.
21305      * Defaults to true.
21306      * @property scroll
21307      * @type boolean
21308      */
21309     scroll: true,
21310
21311     /**
21312      * Sets the pointer offset to the distance between the linked element's top
21313      * left corner and the location the element was clicked
21314      * @method autoOffset
21315      * @param {int} iPageX the X coordinate of the click
21316      * @param {int} iPageY the Y coordinate of the click
21317      */
21318     autoOffset: function(iPageX, iPageY) {
21319         var x = iPageX - this.startPageX;
21320         var y = iPageY - this.startPageY;
21321         this.setDelta(x, y);
21322     },
21323
21324     /**
21325      * Sets the pointer offset.  You can call this directly to force the
21326      * offset to be in a particular location (e.g., pass in 0,0 to set it
21327      * to the center of the object)
21328      * @method setDelta
21329      * @param {int} iDeltaX the distance from the left
21330      * @param {int} iDeltaY the distance from the top
21331      */
21332     setDelta: function(iDeltaX, iDeltaY) {
21333         this.deltaX = iDeltaX;
21334         this.deltaY = iDeltaY;
21335     },
21336
21337     /**
21338      * Sets the drag element to the location of the mousedown or click event,
21339      * maintaining the cursor location relative to the location on the element
21340      * that was clicked.  Override this if you want to place the element in a
21341      * location other than where the cursor is.
21342      * @method setDragElPos
21343      * @param {int} iPageX the X coordinate of the mousedown or drag event
21344      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21345      */
21346     setDragElPos: function(iPageX, iPageY) {
21347         // the first time we do this, we are going to check to make sure
21348         // the element has css positioning
21349
21350         var el = this.getDragEl();
21351         this.alignElWithMouse(el, iPageX, iPageY);
21352     },
21353
21354     /**
21355      * Sets the element to the location of the mousedown or click event,
21356      * maintaining the cursor location relative to the location on the element
21357      * that was clicked.  Override this if you want to place the element in a
21358      * location other than where the cursor is.
21359      * @method alignElWithMouse
21360      * @param {HTMLElement} el the element to move
21361      * @param {int} iPageX the X coordinate of the mousedown or drag event
21362      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21363      */
21364     alignElWithMouse: function(el, iPageX, iPageY) {
21365         var oCoord = this.getTargetCoord(iPageX, iPageY);
21366         var fly = el.dom ? el : Roo.fly(el);
21367         if (!this.deltaSetXY) {
21368             var aCoord = [oCoord.x, oCoord.y];
21369             fly.setXY(aCoord);
21370             var newLeft = fly.getLeft(true);
21371             var newTop  = fly.getTop(true);
21372             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21373         } else {
21374             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21375         }
21376
21377         this.cachePosition(oCoord.x, oCoord.y);
21378         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21379         return oCoord;
21380     },
21381
21382     /**
21383      * Saves the most recent position so that we can reset the constraints and
21384      * tick marks on-demand.  We need to know this so that we can calculate the
21385      * number of pixels the element is offset from its original position.
21386      * @method cachePosition
21387      * @param iPageX the current x position (optional, this just makes it so we
21388      * don't have to look it up again)
21389      * @param iPageY the current y position (optional, this just makes it so we
21390      * don't have to look it up again)
21391      */
21392     cachePosition: function(iPageX, iPageY) {
21393         if (iPageX) {
21394             this.lastPageX = iPageX;
21395             this.lastPageY = iPageY;
21396         } else {
21397             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21398             this.lastPageX = aCoord[0];
21399             this.lastPageY = aCoord[1];
21400         }
21401     },
21402
21403     /**
21404      * Auto-scroll the window if the dragged object has been moved beyond the
21405      * visible window boundary.
21406      * @method autoScroll
21407      * @param {int} x the drag element's x position
21408      * @param {int} y the drag element's y position
21409      * @param {int} h the height of the drag element
21410      * @param {int} w the width of the drag element
21411      * @private
21412      */
21413     autoScroll: function(x, y, h, w) {
21414
21415         if (this.scroll) {
21416             // The client height
21417             var clientH = Roo.lib.Dom.getViewWidth();
21418
21419             // The client width
21420             var clientW = Roo.lib.Dom.getViewHeight();
21421
21422             // The amt scrolled down
21423             var st = this.DDM.getScrollTop();
21424
21425             // The amt scrolled right
21426             var sl = this.DDM.getScrollLeft();
21427
21428             // Location of the bottom of the element
21429             var bot = h + y;
21430
21431             // Location of the right of the element
21432             var right = w + x;
21433
21434             // The distance from the cursor to the bottom of the visible area,
21435             // adjusted so that we don't scroll if the cursor is beyond the
21436             // element drag constraints
21437             var toBot = (clientH + st - y - this.deltaY);
21438
21439             // The distance from the cursor to the right of the visible area
21440             var toRight = (clientW + sl - x - this.deltaX);
21441
21442
21443             // How close to the edge the cursor must be before we scroll
21444             // var thresh = (document.all) ? 100 : 40;
21445             var thresh = 40;
21446
21447             // How many pixels to scroll per autoscroll op.  This helps to reduce
21448             // clunky scrolling. IE is more sensitive about this ... it needs this
21449             // value to be higher.
21450             var scrAmt = (document.all) ? 80 : 30;
21451
21452             // Scroll down if we are near the bottom of the visible page and the
21453             // obj extends below the crease
21454             if ( bot > clientH && toBot < thresh ) {
21455                 window.scrollTo(sl, st + scrAmt);
21456             }
21457
21458             // Scroll up if the window is scrolled down and the top of the object
21459             // goes above the top border
21460             if ( y < st && st > 0 && y - st < thresh ) {
21461                 window.scrollTo(sl, st - scrAmt);
21462             }
21463
21464             // Scroll right if the obj is beyond the right border and the cursor is
21465             // near the border.
21466             if ( right > clientW && toRight < thresh ) {
21467                 window.scrollTo(sl + scrAmt, st);
21468             }
21469
21470             // Scroll left if the window has been scrolled to the right and the obj
21471             // extends past the left border
21472             if ( x < sl && sl > 0 && x - sl < thresh ) {
21473                 window.scrollTo(sl - scrAmt, st);
21474             }
21475         }
21476     },
21477
21478     /**
21479      * Finds the location the element should be placed if we want to move
21480      * it to where the mouse location less the click offset would place us.
21481      * @method getTargetCoord
21482      * @param {int} iPageX the X coordinate of the click
21483      * @param {int} iPageY the Y coordinate of the click
21484      * @return an object that contains the coordinates (Object.x and Object.y)
21485      * @private
21486      */
21487     getTargetCoord: function(iPageX, iPageY) {
21488
21489
21490         var x = iPageX - this.deltaX;
21491         var y = iPageY - this.deltaY;
21492
21493         if (this.constrainX) {
21494             if (x < this.minX) { x = this.minX; }
21495             if (x > this.maxX) { x = this.maxX; }
21496         }
21497
21498         if (this.constrainY) {
21499             if (y < this.minY) { y = this.minY; }
21500             if (y > this.maxY) { y = this.maxY; }
21501         }
21502
21503         x = this.getTick(x, this.xTicks);
21504         y = this.getTick(y, this.yTicks);
21505
21506
21507         return {x:x, y:y};
21508     },
21509
21510     /*
21511      * Sets up config options specific to this class. Overrides
21512      * Roo.dd.DragDrop, but all versions of this method through the
21513      * inheritance chain are called
21514      */
21515     applyConfig: function() {
21516         Roo.dd.DD.superclass.applyConfig.call(this);
21517         this.scroll = (this.config.scroll !== false);
21518     },
21519
21520     /*
21521      * Event that fires prior to the onMouseDown event.  Overrides
21522      * Roo.dd.DragDrop.
21523      */
21524     b4MouseDown: function(e) {
21525         // this.resetConstraints();
21526         this.autoOffset(e.getPageX(),
21527                             e.getPageY());
21528     },
21529
21530     /*
21531      * Event that fires prior to the onDrag event.  Overrides
21532      * Roo.dd.DragDrop.
21533      */
21534     b4Drag: function(e) {
21535         this.setDragElPos(e.getPageX(),
21536                             e.getPageY());
21537     },
21538
21539     toString: function() {
21540         return ("DD " + this.id);
21541     }
21542
21543     //////////////////////////////////////////////////////////////////////////
21544     // Debugging ygDragDrop events that can be overridden
21545     //////////////////////////////////////////////////////////////////////////
21546     /*
21547     startDrag: function(x, y) {
21548     },
21549
21550     onDrag: function(e) {
21551     },
21552
21553     onDragEnter: function(e, id) {
21554     },
21555
21556     onDragOver: function(e, id) {
21557     },
21558
21559     onDragOut: function(e, id) {
21560     },
21561
21562     onDragDrop: function(e, id) {
21563     },
21564
21565     endDrag: function(e) {
21566     }
21567
21568     */
21569
21570 });/*
21571  * Based on:
21572  * Ext JS Library 1.1.1
21573  * Copyright(c) 2006-2007, Ext JS, LLC.
21574  *
21575  * Originally Released Under LGPL - original licence link has changed is not relivant.
21576  *
21577  * Fork - LGPL
21578  * <script type="text/javascript">
21579  */
21580
21581 /**
21582  * @class Roo.dd.DDProxy
21583  * A DragDrop implementation that inserts an empty, bordered div into
21584  * the document that follows the cursor during drag operations.  At the time of
21585  * the click, the frame div is resized to the dimensions of the linked html
21586  * element, and moved to the exact location of the linked element.
21587  *
21588  * References to the "frame" element refer to the single proxy element that
21589  * was created to be dragged in place of all DDProxy elements on the
21590  * page.
21591  *
21592  * @extends Roo.dd.DD
21593  * @constructor
21594  * @param {String} id the id of the linked html element
21595  * @param {String} sGroup the group of related DragDrop objects
21596  * @param {object} config an object containing configurable attributes
21597  *                Valid properties for DDProxy in addition to those in DragDrop:
21598  *                   resizeFrame, centerFrame, dragElId
21599  */
21600 Roo.dd.DDProxy = function(id, sGroup, config) {
21601     if (id) {
21602         this.init(id, sGroup, config);
21603         this.initFrame();
21604     }
21605 };
21606
21607 /**
21608  * The default drag frame div id
21609  * @property Roo.dd.DDProxy.dragElId
21610  * @type String
21611  * @static
21612  */
21613 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21614
21615 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21616
21617     /**
21618      * By default we resize the drag frame to be the same size as the element
21619      * we want to drag (this is to get the frame effect).  We can turn it off
21620      * if we want a different behavior.
21621      * @property resizeFrame
21622      * @type boolean
21623      */
21624     resizeFrame: true,
21625
21626     /**
21627      * By default the frame is positioned exactly where the drag element is, so
21628      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21629      * you do not have constraints on the obj is to have the drag frame centered
21630      * around the cursor.  Set centerFrame to true for this effect.
21631      * @property centerFrame
21632      * @type boolean
21633      */
21634     centerFrame: false,
21635
21636     /**
21637      * Creates the proxy element if it does not yet exist
21638      * @method createFrame
21639      */
21640     createFrame: function() {
21641         var self = this;
21642         var body = document.body;
21643
21644         if (!body || !body.firstChild) {
21645             setTimeout( function() { self.createFrame(); }, 50 );
21646             return;
21647         }
21648
21649         var div = this.getDragEl();
21650
21651         if (!div) {
21652             div    = document.createElement("div");
21653             div.id = this.dragElId;
21654             var s  = div.style;
21655
21656             s.position   = "absolute";
21657             s.visibility = "hidden";
21658             s.cursor     = "move";
21659             s.border     = "2px solid #aaa";
21660             s.zIndex     = 999;
21661
21662             // appendChild can blow up IE if invoked prior to the window load event
21663             // while rendering a table.  It is possible there are other scenarios
21664             // that would cause this to happen as well.
21665             body.insertBefore(div, body.firstChild);
21666         }
21667     },
21668
21669     /**
21670      * Initialization for the drag frame element.  Must be called in the
21671      * constructor of all subclasses
21672      * @method initFrame
21673      */
21674     initFrame: function() {
21675         this.createFrame();
21676     },
21677
21678     applyConfig: function() {
21679         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21680
21681         this.resizeFrame = (this.config.resizeFrame !== false);
21682         this.centerFrame = (this.config.centerFrame);
21683         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21684     },
21685
21686     /**
21687      * Resizes the drag frame to the dimensions of the clicked object, positions
21688      * it over the object, and finally displays it
21689      * @method showFrame
21690      * @param {int} iPageX X click position
21691      * @param {int} iPageY Y click position
21692      * @private
21693      */
21694     showFrame: function(iPageX, iPageY) {
21695         var el = this.getEl();
21696         var dragEl = this.getDragEl();
21697         var s = dragEl.style;
21698
21699         this._resizeProxy();
21700
21701         if (this.centerFrame) {
21702             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21703                            Math.round(parseInt(s.height, 10)/2) );
21704         }
21705
21706         this.setDragElPos(iPageX, iPageY);
21707
21708         Roo.fly(dragEl).show();
21709     },
21710
21711     /**
21712      * The proxy is automatically resized to the dimensions of the linked
21713      * element when a drag is initiated, unless resizeFrame is set to false
21714      * @method _resizeProxy
21715      * @private
21716      */
21717     _resizeProxy: function() {
21718         if (this.resizeFrame) {
21719             var el = this.getEl();
21720             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21721         }
21722     },
21723
21724     // overrides Roo.dd.DragDrop
21725     b4MouseDown: function(e) {
21726         var x = e.getPageX();
21727         var y = e.getPageY();
21728         this.autoOffset(x, y);
21729         this.setDragElPos(x, y);
21730     },
21731
21732     // overrides Roo.dd.DragDrop
21733     b4StartDrag: function(x, y) {
21734         // show the drag frame
21735         this.showFrame(x, y);
21736     },
21737
21738     // overrides Roo.dd.DragDrop
21739     b4EndDrag: function(e) {
21740         Roo.fly(this.getDragEl()).hide();
21741     },
21742
21743     // overrides Roo.dd.DragDrop
21744     // By default we try to move the element to the last location of the frame.
21745     // This is so that the default behavior mirrors that of Roo.dd.DD.
21746     endDrag: function(e) {
21747
21748         var lel = this.getEl();
21749         var del = this.getDragEl();
21750
21751         // Show the drag frame briefly so we can get its position
21752         del.style.visibility = "";
21753
21754         this.beforeMove();
21755         // Hide the linked element before the move to get around a Safari
21756         // rendering bug.
21757         lel.style.visibility = "hidden";
21758         Roo.dd.DDM.moveToEl(lel, del);
21759         del.style.visibility = "hidden";
21760         lel.style.visibility = "";
21761
21762         this.afterDrag();
21763     },
21764
21765     beforeMove : function(){
21766
21767     },
21768
21769     afterDrag : function(){
21770
21771     },
21772
21773     toString: function() {
21774         return ("DDProxy " + this.id);
21775     }
21776
21777 });
21778 /*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788
21789  /**
21790  * @class Roo.dd.DDTarget
21791  * A DragDrop implementation that does not move, but can be a drop
21792  * target.  You would get the same result by simply omitting implementation
21793  * for the event callbacks, but this way we reduce the processing cost of the
21794  * event listener and the callbacks.
21795  * @extends Roo.dd.DragDrop
21796  * @constructor
21797  * @param {String} id the id of the element that is a drop target
21798  * @param {String} sGroup the group of related DragDrop objects
21799  * @param {object} config an object containing configurable attributes
21800  *                 Valid properties for DDTarget in addition to those in
21801  *                 DragDrop:
21802  *                    none
21803  */
21804 Roo.dd.DDTarget = function(id, sGroup, config) {
21805     if (id) {
21806         this.initTarget(id, sGroup, config);
21807     }
21808     if (config && (config.listeners || config.events)) { 
21809         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21810             listeners : config.listeners || {}, 
21811             events : config.events || {} 
21812         });    
21813     }
21814 };
21815
21816 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21817 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21818     toString: function() {
21819         return ("DDTarget " + this.id);
21820     }
21821 });
21822 /*
21823  * Based on:
21824  * Ext JS Library 1.1.1
21825  * Copyright(c) 2006-2007, Ext JS, LLC.
21826  *
21827  * Originally Released Under LGPL - original licence link has changed is not relivant.
21828  *
21829  * Fork - LGPL
21830  * <script type="text/javascript">
21831  */
21832  
21833
21834 /**
21835  * @class Roo.dd.ScrollManager
21836  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21837  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21838  * @singleton
21839  */
21840 Roo.dd.ScrollManager = function(){
21841     var ddm = Roo.dd.DragDropMgr;
21842     var els = {};
21843     var dragEl = null;
21844     var proc = {};
21845     
21846     
21847     
21848     var onStop = function(e){
21849         dragEl = null;
21850         clearProc();
21851     };
21852     
21853     var triggerRefresh = function(){
21854         if(ddm.dragCurrent){
21855              ddm.refreshCache(ddm.dragCurrent.groups);
21856         }
21857     };
21858     
21859     var doScroll = function(){
21860         if(ddm.dragCurrent){
21861             var dds = Roo.dd.ScrollManager;
21862             if(!dds.animate){
21863                 if(proc.el.scroll(proc.dir, dds.increment)){
21864                     triggerRefresh();
21865                 }
21866             }else{
21867                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21868             }
21869         }
21870     };
21871     
21872     var clearProc = function(){
21873         if(proc.id){
21874             clearInterval(proc.id);
21875         }
21876         proc.id = 0;
21877         proc.el = null;
21878         proc.dir = "";
21879     };
21880     
21881     var startProc = function(el, dir){
21882          Roo.log('scroll startproc');
21883         clearProc();
21884         proc.el = el;
21885         proc.dir = dir;
21886         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21887     };
21888     
21889     var onFire = function(e, isDrop){
21890        
21891         if(isDrop || !ddm.dragCurrent){ return; }
21892         var dds = Roo.dd.ScrollManager;
21893         if(!dragEl || dragEl != ddm.dragCurrent){
21894             dragEl = ddm.dragCurrent;
21895             // refresh regions on drag start
21896             dds.refreshCache();
21897         }
21898         
21899         var xy = Roo.lib.Event.getXY(e);
21900         var pt = new Roo.lib.Point(xy[0], xy[1]);
21901         for(var id in els){
21902             var el = els[id], r = el._region;
21903             if(r && r.contains(pt) && el.isScrollable()){
21904                 if(r.bottom - pt.y <= dds.thresh){
21905                     if(proc.el != el){
21906                         startProc(el, "down");
21907                     }
21908                     return;
21909                 }else if(r.right - pt.x <= dds.thresh){
21910                     if(proc.el != el){
21911                         startProc(el, "left");
21912                     }
21913                     return;
21914                 }else if(pt.y - r.top <= dds.thresh){
21915                     if(proc.el != el){
21916                         startProc(el, "up");
21917                     }
21918                     return;
21919                 }else if(pt.x - r.left <= dds.thresh){
21920                     if(proc.el != el){
21921                         startProc(el, "right");
21922                     }
21923                     return;
21924                 }
21925             }
21926         }
21927         clearProc();
21928     };
21929     
21930     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21931     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21932     
21933     return {
21934         /**
21935          * Registers new overflow element(s) to auto scroll
21936          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21937          */
21938         register : function(el){
21939             if(el instanceof Array){
21940                 for(var i = 0, len = el.length; i < len; i++) {
21941                         this.register(el[i]);
21942                 }
21943             }else{
21944                 el = Roo.get(el);
21945                 els[el.id] = el;
21946             }
21947             Roo.dd.ScrollManager.els = els;
21948         },
21949         
21950         /**
21951          * Unregisters overflow element(s) so they are no longer scrolled
21952          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21953          */
21954         unregister : function(el){
21955             if(el instanceof Array){
21956                 for(var i = 0, len = el.length; i < len; i++) {
21957                         this.unregister(el[i]);
21958                 }
21959             }else{
21960                 el = Roo.get(el);
21961                 delete els[el.id];
21962             }
21963         },
21964         
21965         /**
21966          * The number of pixels from the edge of a container the pointer needs to be to 
21967          * trigger scrolling (defaults to 25)
21968          * @type Number
21969          */
21970         thresh : 25,
21971         
21972         /**
21973          * The number of pixels to scroll in each scroll increment (defaults to 50)
21974          * @type Number
21975          */
21976         increment : 100,
21977         
21978         /**
21979          * The frequency of scrolls in milliseconds (defaults to 500)
21980          * @type Number
21981          */
21982         frequency : 500,
21983         
21984         /**
21985          * True to animate the scroll (defaults to true)
21986          * @type Boolean
21987          */
21988         animate: true,
21989         
21990         /**
21991          * The animation duration in seconds - 
21992          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21993          * @type Number
21994          */
21995         animDuration: .4,
21996         
21997         /**
21998          * Manually trigger a cache refresh.
21999          */
22000         refreshCache : function(){
22001             for(var id in els){
22002                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22003                     els[id]._region = els[id].getRegion();
22004                 }
22005             }
22006         }
22007     };
22008 }();/*
22009  * Based on:
22010  * Ext JS Library 1.1.1
22011  * Copyright(c) 2006-2007, Ext JS, LLC.
22012  *
22013  * Originally Released Under LGPL - original licence link has changed is not relivant.
22014  *
22015  * Fork - LGPL
22016  * <script type="text/javascript">
22017  */
22018  
22019
22020 /**
22021  * @class Roo.dd.Registry
22022  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22023  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22024  * @singleton
22025  */
22026 Roo.dd.Registry = function(){
22027     var elements = {}; 
22028     var handles = {}; 
22029     var autoIdSeed = 0;
22030
22031     var getId = function(el, autogen){
22032         if(typeof el == "string"){
22033             return el;
22034         }
22035         var id = el.id;
22036         if(!id && autogen !== false){
22037             id = "roodd-" + (++autoIdSeed);
22038             el.id = id;
22039         }
22040         return id;
22041     };
22042     
22043     return {
22044     /**
22045      * Register a drag drop element
22046      * @param {String|HTMLElement} element The id or DOM node to register
22047      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22048      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22049      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22050      * populated in the data object (if applicable):
22051      * <pre>
22052 Value      Description<br />
22053 ---------  ------------------------------------------<br />
22054 handles    Array of DOM nodes that trigger dragging<br />
22055            for the element being registered<br />
22056 isHandle   True if the element passed in triggers<br />
22057            dragging itself, else false
22058 </pre>
22059      */
22060         register : function(el, data){
22061             data = data || {};
22062             if(typeof el == "string"){
22063                 el = document.getElementById(el);
22064             }
22065             data.ddel = el;
22066             elements[getId(el)] = data;
22067             if(data.isHandle !== false){
22068                 handles[data.ddel.id] = data;
22069             }
22070             if(data.handles){
22071                 var hs = data.handles;
22072                 for(var i = 0, len = hs.length; i < len; i++){
22073                         handles[getId(hs[i])] = data;
22074                 }
22075             }
22076         },
22077
22078     /**
22079      * Unregister a drag drop element
22080      * @param {String|HTMLElement}  element The id or DOM node to unregister
22081      */
22082         unregister : function(el){
22083             var id = getId(el, false);
22084             var data = elements[id];
22085             if(data){
22086                 delete elements[id];
22087                 if(data.handles){
22088                     var hs = data.handles;
22089                     for(var i = 0, len = hs.length; i < len; i++){
22090                         delete handles[getId(hs[i], false)];
22091                     }
22092                 }
22093             }
22094         },
22095
22096     /**
22097      * Returns the handle registered for a DOM Node by id
22098      * @param {String|HTMLElement} id The DOM node or id to look up
22099      * @return {Object} handle The custom handle data
22100      */
22101         getHandle : function(id){
22102             if(typeof id != "string"){ // must be element?
22103                 id = id.id;
22104             }
22105             return handles[id];
22106         },
22107
22108     /**
22109      * Returns the handle that is registered for the DOM node that is the target of the event
22110      * @param {Event} e The event
22111      * @return {Object} handle The custom handle data
22112      */
22113         getHandleFromEvent : function(e){
22114             var t = Roo.lib.Event.getTarget(e);
22115             return t ? handles[t.id] : null;
22116         },
22117
22118     /**
22119      * Returns a custom data object that is registered for a DOM node by id
22120      * @param {String|HTMLElement} id The DOM node or id to look up
22121      * @return {Object} data The custom data
22122      */
22123         getTarget : function(id){
22124             if(typeof id != "string"){ // must be element?
22125                 id = id.id;
22126             }
22127             return elements[id];
22128         },
22129
22130     /**
22131      * Returns a custom data object that is registered for the DOM node that is the target of the event
22132      * @param {Event} e The event
22133      * @return {Object} data The custom data
22134      */
22135         getTargetFromEvent : function(e){
22136             var t = Roo.lib.Event.getTarget(e);
22137             return t ? elements[t.id] || handles[t.id] : null;
22138         }
22139     };
22140 }();/*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150  
22151
22152 /**
22153  * @class Roo.dd.StatusProxy
22154  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22155  * default drag proxy used by all Roo.dd components.
22156  * @constructor
22157  * @param {Object} config
22158  */
22159 Roo.dd.StatusProxy = function(config){
22160     Roo.apply(this, config);
22161     this.id = this.id || Roo.id();
22162     this.el = new Roo.Layer({
22163         dh: {
22164             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22165                 {tag: "div", cls: "x-dd-drop-icon"},
22166                 {tag: "div", cls: "x-dd-drag-ghost"}
22167             ]
22168         }, 
22169         shadow: !config || config.shadow !== false
22170     });
22171     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22172     this.dropStatus = this.dropNotAllowed;
22173 };
22174
22175 Roo.dd.StatusProxy.prototype = {
22176     /**
22177      * @cfg {String} dropAllowed
22178      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22179      */
22180     dropAllowed : "x-dd-drop-ok",
22181     /**
22182      * @cfg {String} dropNotAllowed
22183      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22184      */
22185     dropNotAllowed : "x-dd-drop-nodrop",
22186
22187     /**
22188      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22189      * over the current target element.
22190      * @param {String} cssClass The css class for the new drop status indicator image
22191      */
22192     setStatus : function(cssClass){
22193         cssClass = cssClass || this.dropNotAllowed;
22194         if(this.dropStatus != cssClass){
22195             this.el.replaceClass(this.dropStatus, cssClass);
22196             this.dropStatus = cssClass;
22197         }
22198     },
22199
22200     /**
22201      * Resets the status indicator to the default dropNotAllowed value
22202      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22203      */
22204     reset : function(clearGhost){
22205         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22206         this.dropStatus = this.dropNotAllowed;
22207         if(clearGhost){
22208             this.ghost.update("");
22209         }
22210     },
22211
22212     /**
22213      * Updates the contents of the ghost element
22214      * @param {String} html The html that will replace the current innerHTML of the ghost element
22215      */
22216     update : function(html){
22217         if(typeof html == "string"){
22218             this.ghost.update(html);
22219         }else{
22220             this.ghost.update("");
22221             html.style.margin = "0";
22222             this.ghost.dom.appendChild(html);
22223         }
22224         // ensure float = none set?? cant remember why though.
22225         var el = this.ghost.dom.firstChild;
22226                 if(el){
22227                         Roo.fly(el).setStyle('float', 'none');
22228                 }
22229     },
22230     
22231     /**
22232      * Returns the underlying proxy {@link Roo.Layer}
22233      * @return {Roo.Layer} el
22234     */
22235     getEl : function(){
22236         return this.el;
22237     },
22238
22239     /**
22240      * Returns the ghost element
22241      * @return {Roo.Element} el
22242      */
22243     getGhost : function(){
22244         return this.ghost;
22245     },
22246
22247     /**
22248      * Hides the proxy
22249      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22250      */
22251     hide : function(clear){
22252         this.el.hide();
22253         if(clear){
22254             this.reset(true);
22255         }
22256     },
22257
22258     /**
22259      * Stops the repair animation if it's currently running
22260      */
22261     stop : function(){
22262         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22263             this.anim.stop();
22264         }
22265     },
22266
22267     /**
22268      * Displays this proxy
22269      */
22270     show : function(){
22271         this.el.show();
22272     },
22273
22274     /**
22275      * Force the Layer to sync its shadow and shim positions to the element
22276      */
22277     sync : function(){
22278         this.el.sync();
22279     },
22280
22281     /**
22282      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22283      * invalid drop operation by the item being dragged.
22284      * @param {Array} xy The XY position of the element ([x, y])
22285      * @param {Function} callback The function to call after the repair is complete
22286      * @param {Object} scope The scope in which to execute the callback
22287      */
22288     repair : function(xy, callback, scope){
22289         this.callback = callback;
22290         this.scope = scope;
22291         if(xy && this.animRepair !== false){
22292             this.el.addClass("x-dd-drag-repair");
22293             this.el.hideUnders(true);
22294             this.anim = this.el.shift({
22295                 duration: this.repairDuration || .5,
22296                 easing: 'easeOut',
22297                 xy: xy,
22298                 stopFx: true,
22299                 callback: this.afterRepair,
22300                 scope: this
22301             });
22302         }else{
22303             this.afterRepair();
22304         }
22305     },
22306
22307     // private
22308     afterRepair : function(){
22309         this.hide(true);
22310         if(typeof this.callback == "function"){
22311             this.callback.call(this.scope || this);
22312         }
22313         this.callback = null;
22314         this.scope = null;
22315     }
22316 };/*
22317  * Based on:
22318  * Ext JS Library 1.1.1
22319  * Copyright(c) 2006-2007, Ext JS, LLC.
22320  *
22321  * Originally Released Under LGPL - original licence link has changed is not relivant.
22322  *
22323  * Fork - LGPL
22324  * <script type="text/javascript">
22325  */
22326
22327 /**
22328  * @class Roo.dd.DragSource
22329  * @extends Roo.dd.DDProxy
22330  * A simple class that provides the basic implementation needed to make any element draggable.
22331  * @constructor
22332  * @param {String/HTMLElement/Element} el The container element
22333  * @param {Object} config
22334  */
22335 Roo.dd.DragSource = function(el, config){
22336     this.el = Roo.get(el);
22337     this.dragData = {};
22338     
22339     Roo.apply(this, config);
22340     
22341     if(!this.proxy){
22342         this.proxy = new Roo.dd.StatusProxy();
22343     }
22344
22345     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22346           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22347     
22348     this.dragging = false;
22349 };
22350
22351 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22352     /**
22353      * @cfg {String} dropAllowed
22354      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22355      */
22356     dropAllowed : "x-dd-drop-ok",
22357     /**
22358      * @cfg {String} dropNotAllowed
22359      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22360      */
22361     dropNotAllowed : "x-dd-drop-nodrop",
22362
22363     /**
22364      * Returns the data object associated with this drag source
22365      * @return {Object} data An object containing arbitrary data
22366      */
22367     getDragData : function(e){
22368         return this.dragData;
22369     },
22370
22371     // private
22372     onDragEnter : function(e, id){
22373         var target = Roo.dd.DragDropMgr.getDDById(id);
22374         this.cachedTarget = target;
22375         if(this.beforeDragEnter(target, e, id) !== false){
22376             if(target.isNotifyTarget){
22377                 var status = target.notifyEnter(this, e, this.dragData);
22378                 this.proxy.setStatus(status);
22379             }else{
22380                 this.proxy.setStatus(this.dropAllowed);
22381             }
22382             
22383             if(this.afterDragEnter){
22384                 /**
22385                  * An empty function by default, but provided so that you can perform a custom action
22386                  * when the dragged item enters the drop target by providing an implementation.
22387                  * @param {Roo.dd.DragDrop} target The drop target
22388                  * @param {Event} e The event object
22389                  * @param {String} id The id of the dragged element
22390                  * @method afterDragEnter
22391                  */
22392                 this.afterDragEnter(target, e, id);
22393             }
22394         }
22395     },
22396
22397     /**
22398      * An empty function by default, but provided so that you can perform a custom action
22399      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22400      * @param {Roo.dd.DragDrop} target The drop target
22401      * @param {Event} e The event object
22402      * @param {String} id The id of the dragged element
22403      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22404      */
22405     beforeDragEnter : function(target, e, id){
22406         return true;
22407     },
22408
22409     // private
22410     alignElWithMouse: function() {
22411         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22412         this.proxy.sync();
22413     },
22414
22415     // private
22416     onDragOver : function(e, id){
22417         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22418         if(this.beforeDragOver(target, e, id) !== false){
22419             if(target.isNotifyTarget){
22420                 var status = target.notifyOver(this, e, this.dragData);
22421                 this.proxy.setStatus(status);
22422             }
22423
22424             if(this.afterDragOver){
22425                 /**
22426                  * An empty function by default, but provided so that you can perform a custom action
22427                  * while the dragged item is over the drop target by providing an implementation.
22428                  * @param {Roo.dd.DragDrop} target The drop target
22429                  * @param {Event} e The event object
22430                  * @param {String} id The id of the dragged element
22431                  * @method afterDragOver
22432                  */
22433                 this.afterDragOver(target, e, id);
22434             }
22435         }
22436     },
22437
22438     /**
22439      * An empty function by default, but provided so that you can perform a custom action
22440      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22441      * @param {Roo.dd.DragDrop} target The drop target
22442      * @param {Event} e The event object
22443      * @param {String} id The id of the dragged element
22444      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22445      */
22446     beforeDragOver : function(target, e, id){
22447         return true;
22448     },
22449
22450     // private
22451     onDragOut : function(e, id){
22452         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22453         if(this.beforeDragOut(target, e, id) !== false){
22454             if(target.isNotifyTarget){
22455                 target.notifyOut(this, e, this.dragData);
22456             }
22457             this.proxy.reset();
22458             if(this.afterDragOut){
22459                 /**
22460                  * An empty function by default, but provided so that you can perform a custom action
22461                  * after the dragged item is dragged out of the target without dropping.
22462                  * @param {Roo.dd.DragDrop} target The drop target
22463                  * @param {Event} e The event object
22464                  * @param {String} id The id of the dragged element
22465                  * @method afterDragOut
22466                  */
22467                 this.afterDragOut(target, e, id);
22468             }
22469         }
22470         this.cachedTarget = null;
22471     },
22472
22473     /**
22474      * An empty function by default, but provided so that you can perform a custom action before the dragged
22475      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22476      * @param {Roo.dd.DragDrop} target The drop target
22477      * @param {Event} e The event object
22478      * @param {String} id The id of the dragged element
22479      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22480      */
22481     beforeDragOut : function(target, e, id){
22482         return true;
22483     },
22484     
22485     // private
22486     onDragDrop : function(e, id){
22487         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22488         if(this.beforeDragDrop(target, e, id) !== false){
22489             if(target.isNotifyTarget){
22490                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22491                     this.onValidDrop(target, e, id);
22492                 }else{
22493                     this.onInvalidDrop(target, e, id);
22494                 }
22495             }else{
22496                 this.onValidDrop(target, e, id);
22497             }
22498             
22499             if(this.afterDragDrop){
22500                 /**
22501                  * An empty function by default, but provided so that you can perform a custom action
22502                  * after a valid drag drop has occurred by providing an implementation.
22503                  * @param {Roo.dd.DragDrop} target The drop target
22504                  * @param {Event} e The event object
22505                  * @param {String} id The id of the dropped element
22506                  * @method afterDragDrop
22507                  */
22508                 this.afterDragDrop(target, e, id);
22509             }
22510         }
22511         delete this.cachedTarget;
22512     },
22513
22514     /**
22515      * An empty function by default, but provided so that you can perform a custom action before the dragged
22516      * item is dropped onto the target and optionally cancel the onDragDrop.
22517      * @param {Roo.dd.DragDrop} target The drop target
22518      * @param {Event} e The event object
22519      * @param {String} id The id of the dragged element
22520      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22521      */
22522     beforeDragDrop : function(target, e, id){
22523         return true;
22524     },
22525
22526     // private
22527     onValidDrop : function(target, e, id){
22528         this.hideProxy();
22529         if(this.afterValidDrop){
22530             /**
22531              * An empty function by default, but provided so that you can perform a custom action
22532              * after a valid drop has occurred by providing an implementation.
22533              * @param {Object} target The target DD 
22534              * @param {Event} e The event object
22535              * @param {String} id The id of the dropped element
22536              * @method afterInvalidDrop
22537              */
22538             this.afterValidDrop(target, e, id);
22539         }
22540     },
22541
22542     // private
22543     getRepairXY : function(e, data){
22544         return this.el.getXY();  
22545     },
22546
22547     // private
22548     onInvalidDrop : function(target, e, id){
22549         this.beforeInvalidDrop(target, e, id);
22550         if(this.cachedTarget){
22551             if(this.cachedTarget.isNotifyTarget){
22552                 this.cachedTarget.notifyOut(this, e, this.dragData);
22553             }
22554             this.cacheTarget = null;
22555         }
22556         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22557
22558         if(this.afterInvalidDrop){
22559             /**
22560              * An empty function by default, but provided so that you can perform a custom action
22561              * after an invalid drop has occurred by providing an implementation.
22562              * @param {Event} e The event object
22563              * @param {String} id The id of the dropped element
22564              * @method afterInvalidDrop
22565              */
22566             this.afterInvalidDrop(e, id);
22567         }
22568     },
22569
22570     // private
22571     afterRepair : function(){
22572         if(Roo.enableFx){
22573             this.el.highlight(this.hlColor || "c3daf9");
22574         }
22575         this.dragging = false;
22576     },
22577
22578     /**
22579      * An empty function by default, but provided so that you can perform a custom action after an invalid
22580      * drop has occurred.
22581      * @param {Roo.dd.DragDrop} target The drop target
22582      * @param {Event} e The event object
22583      * @param {String} id The id of the dragged element
22584      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22585      */
22586     beforeInvalidDrop : function(target, e, id){
22587         return true;
22588     },
22589
22590     // private
22591     handleMouseDown : function(e){
22592         if(this.dragging) {
22593             return;
22594         }
22595         var data = this.getDragData(e);
22596         if(data && this.onBeforeDrag(data, e) !== false){
22597             this.dragData = data;
22598             this.proxy.stop();
22599             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22600         } 
22601     },
22602
22603     /**
22604      * An empty function by default, but provided so that you can perform a custom action before the initial
22605      * drag event begins and optionally cancel it.
22606      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22607      * @param {Event} e The event object
22608      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22609      */
22610     onBeforeDrag : function(data, e){
22611         return true;
22612     },
22613
22614     /**
22615      * An empty function by default, but provided so that you can perform a custom action once the initial
22616      * drag event has begun.  The drag cannot be canceled from this function.
22617      * @param {Number} x The x position of the click on the dragged object
22618      * @param {Number} y The y position of the click on the dragged object
22619      */
22620     onStartDrag : Roo.emptyFn,
22621
22622     // private - YUI override
22623     startDrag : function(x, y){
22624         this.proxy.reset();
22625         this.dragging = true;
22626         this.proxy.update("");
22627         this.onInitDrag(x, y);
22628         this.proxy.show();
22629     },
22630
22631     // private
22632     onInitDrag : function(x, y){
22633         var clone = this.el.dom.cloneNode(true);
22634         clone.id = Roo.id(); // prevent duplicate ids
22635         this.proxy.update(clone);
22636         this.onStartDrag(x, y);
22637         return true;
22638     },
22639
22640     /**
22641      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22642      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22643      */
22644     getProxy : function(){
22645         return this.proxy;  
22646     },
22647
22648     /**
22649      * Hides the drag source's {@link Roo.dd.StatusProxy}
22650      */
22651     hideProxy : function(){
22652         this.proxy.hide();  
22653         this.proxy.reset(true);
22654         this.dragging = false;
22655     },
22656
22657     // private
22658     triggerCacheRefresh : function(){
22659         Roo.dd.DDM.refreshCache(this.groups);
22660     },
22661
22662     // private - override to prevent hiding
22663     b4EndDrag: function(e) {
22664     },
22665
22666     // private - override to prevent moving
22667     endDrag : function(e){
22668         this.onEndDrag(this.dragData, e);
22669     },
22670
22671     // private
22672     onEndDrag : function(data, e){
22673     },
22674     
22675     // private - pin to cursor
22676     autoOffset : function(x, y) {
22677         this.setDelta(-12, -20);
22678     }    
22679 });/*
22680  * Based on:
22681  * Ext JS Library 1.1.1
22682  * Copyright(c) 2006-2007, Ext JS, LLC.
22683  *
22684  * Originally Released Under LGPL - original licence link has changed is not relivant.
22685  *
22686  * Fork - LGPL
22687  * <script type="text/javascript">
22688  */
22689
22690
22691 /**
22692  * @class Roo.dd.DropTarget
22693  * @extends Roo.dd.DDTarget
22694  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22695  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22696  * @constructor
22697  * @param {String/HTMLElement/Element} el The container element
22698  * @param {Object} config
22699  */
22700 Roo.dd.DropTarget = function(el, config){
22701     this.el = Roo.get(el);
22702     
22703     var listeners = false; ;
22704     if (config && config.listeners) {
22705         listeners= config.listeners;
22706         delete config.listeners;
22707     }
22708     Roo.apply(this, config);
22709     
22710     if(this.containerScroll){
22711         Roo.dd.ScrollManager.register(this.el);
22712     }
22713     this.addEvents( {
22714          /**
22715          * @scope Roo.dd.DropTarget
22716          */
22717          
22718          /**
22719          * @event enter
22720          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22721          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22722          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22723          * 
22724          * IMPORTANT : it should set  this.valid to true|false
22725          * 
22726          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22727          * @param {Event} e The event
22728          * @param {Object} data An object containing arbitrary data supplied by the drag source
22729          */
22730         "enter" : true,
22731         
22732          /**
22733          * @event over
22734          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22735          * This method will be called on every mouse movement while the drag source is over the drop target.
22736          * This default implementation simply returns the dropAllowed config value.
22737          * 
22738          * IMPORTANT : it should set  this.valid to true|false
22739          * 
22740          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22741          * @param {Event} e The event
22742          * @param {Object} data An object containing arbitrary data supplied by the drag source
22743          
22744          */
22745         "over" : true,
22746         /**
22747          * @event out
22748          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22749          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22750          * overClass (if any) from the drop element.
22751          * 
22752          * 
22753          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22754          * @param {Event} e The event
22755          * @param {Object} data An object containing arbitrary data supplied by the drag source
22756          */
22757          "out" : true,
22758          
22759         /**
22760          * @event drop
22761          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22762          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22763          * implementation that does something to process the drop event and returns true so that the drag source's
22764          * repair action does not run.
22765          * 
22766          * IMPORTANT : it should set this.success
22767          * 
22768          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22769          * @param {Event} e The event
22770          * @param {Object} data An object containing arbitrary data supplied by the drag source
22771         */
22772          "drop" : true
22773     });
22774             
22775      
22776     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22777         this.el.dom, 
22778         this.ddGroup || this.group,
22779         {
22780             isTarget: true,
22781             listeners : listeners || {} 
22782            
22783         
22784         }
22785     );
22786
22787 };
22788
22789 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22790     /**
22791      * @cfg {String} overClass
22792      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22793      */
22794      /**
22795      * @cfg {String} ddGroup
22796      * The drag drop group to handle drop events for
22797      */
22798      
22799     /**
22800      * @cfg {String} dropAllowed
22801      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22802      */
22803     dropAllowed : "x-dd-drop-ok",
22804     /**
22805      * @cfg {String} dropNotAllowed
22806      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22807      */
22808     dropNotAllowed : "x-dd-drop-nodrop",
22809     /**
22810      * @cfg {boolean} success
22811      * set this after drop listener.. 
22812      */
22813     success : false,
22814     /**
22815      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22816      * if the drop point is valid for over/enter..
22817      */
22818     valid : false,
22819     // private
22820     isTarget : true,
22821
22822     // private
22823     isNotifyTarget : true,
22824     
22825     /**
22826      * @hide
22827      */
22828     notifyEnter : function(dd, e, data)
22829     {
22830         this.valid = true;
22831         this.fireEvent('enter', dd, e, data);
22832         if(this.overClass){
22833             this.el.addClass(this.overClass);
22834         }
22835         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22836             this.valid ? this.dropAllowed : this.dropNotAllowed
22837         );
22838     },
22839
22840     /**
22841      * @hide
22842      */
22843     notifyOver : function(dd, e, data)
22844     {
22845         this.valid = true;
22846         this.fireEvent('over', dd, e, data);
22847         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22848             this.valid ? this.dropAllowed : this.dropNotAllowed
22849         );
22850     },
22851
22852     /**
22853      * @hide
22854      */
22855     notifyOut : function(dd, e, data)
22856     {
22857         this.fireEvent('out', dd, e, data);
22858         if(this.overClass){
22859             this.el.removeClass(this.overClass);
22860         }
22861     },
22862
22863     /**
22864      * @hide
22865      */
22866     notifyDrop : function(dd, e, data)
22867     {
22868         this.success = false;
22869         this.fireEvent('drop', dd, e, data);
22870         return this.success;
22871     }
22872 });/*
22873  * Based on:
22874  * Ext JS Library 1.1.1
22875  * Copyright(c) 2006-2007, Ext JS, LLC.
22876  *
22877  * Originally Released Under LGPL - original licence link has changed is not relivant.
22878  *
22879  * Fork - LGPL
22880  * <script type="text/javascript">
22881  */
22882
22883
22884 /**
22885  * @class Roo.dd.DragZone
22886  * @extends Roo.dd.DragSource
22887  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22888  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22889  * @constructor
22890  * @param {String/HTMLElement/Element} el The container element
22891  * @param {Object} config
22892  */
22893 Roo.dd.DragZone = function(el, config){
22894     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22895     if(this.containerScroll){
22896         Roo.dd.ScrollManager.register(this.el);
22897     }
22898 };
22899
22900 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22901     /**
22902      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22903      * for auto scrolling during drag operations.
22904      */
22905     /**
22906      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22907      * method after a failed drop (defaults to "c3daf9" - light blue)
22908      */
22909
22910     /**
22911      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22912      * for a valid target to drag based on the mouse down. Override this method
22913      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22914      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22915      * @param {EventObject} e The mouse down event
22916      * @return {Object} The dragData
22917      */
22918     getDragData : function(e){
22919         return Roo.dd.Registry.getHandleFromEvent(e);
22920     },
22921     
22922     /**
22923      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22924      * this.dragData.ddel
22925      * @param {Number} x The x position of the click on the dragged object
22926      * @param {Number} y The y position of the click on the dragged object
22927      * @return {Boolean} true to continue the drag, false to cancel
22928      */
22929     onInitDrag : function(x, y){
22930         this.proxy.update(this.dragData.ddel.cloneNode(true));
22931         this.onStartDrag(x, y);
22932         return true;
22933     },
22934     
22935     /**
22936      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22937      */
22938     afterRepair : function(){
22939         if(Roo.enableFx){
22940             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22941         }
22942         this.dragging = false;
22943     },
22944
22945     /**
22946      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22947      * the XY of this.dragData.ddel
22948      * @param {EventObject} e The mouse up event
22949      * @return {Array} The xy location (e.g. [100, 200])
22950      */
22951     getRepairXY : function(e){
22952         return Roo.Element.fly(this.dragData.ddel).getXY();  
22953     }
22954 });/*
22955  * Based on:
22956  * Ext JS Library 1.1.1
22957  * Copyright(c) 2006-2007, Ext JS, LLC.
22958  *
22959  * Originally Released Under LGPL - original licence link has changed is not relivant.
22960  *
22961  * Fork - LGPL
22962  * <script type="text/javascript">
22963  */
22964 /**
22965  * @class Roo.dd.DropZone
22966  * @extends Roo.dd.DropTarget
22967  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22968  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22969  * @constructor
22970  * @param {String/HTMLElement/Element} el The container element
22971  * @param {Object} config
22972  */
22973 Roo.dd.DropZone = function(el, config){
22974     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22975 };
22976
22977 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22978     /**
22979      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22980      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22981      * provide your own custom lookup.
22982      * @param {Event} e The event
22983      * @return {Object} data The custom data
22984      */
22985     getTargetFromEvent : function(e){
22986         return Roo.dd.Registry.getTargetFromEvent(e);
22987     },
22988
22989     /**
22990      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22991      * that it has registered.  This method has no default implementation and should be overridden to provide
22992      * node-specific processing if necessary.
22993      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22994      * {@link #getTargetFromEvent} for this node)
22995      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22996      * @param {Event} e The event
22997      * @param {Object} data An object containing arbitrary data supplied by the drag source
22998      */
22999     onNodeEnter : function(n, dd, e, data){
23000         
23001     },
23002
23003     /**
23004      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23005      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23006      * overridden to provide the proper feedback.
23007      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23008      * {@link #getTargetFromEvent} for this node)
23009      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23010      * @param {Event} e The event
23011      * @param {Object} data An object containing arbitrary data supplied by the drag source
23012      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23013      * underlying {@link Roo.dd.StatusProxy} can be updated
23014      */
23015     onNodeOver : function(n, dd, e, data){
23016         return this.dropAllowed;
23017     },
23018
23019     /**
23020      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23021      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23022      * node-specific processing if necessary.
23023      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23024      * {@link #getTargetFromEvent} for this node)
23025      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23026      * @param {Event} e The event
23027      * @param {Object} data An object containing arbitrary data supplied by the drag source
23028      */
23029     onNodeOut : function(n, dd, e, data){
23030         
23031     },
23032
23033     /**
23034      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23035      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23036      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23037      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23038      * {@link #getTargetFromEvent} for this node)
23039      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23040      * @param {Event} e The event
23041      * @param {Object} data An object containing arbitrary data supplied by the drag source
23042      * @return {Boolean} True if the drop was valid, else false
23043      */
23044     onNodeDrop : function(n, dd, e, data){
23045         return false;
23046     },
23047
23048     /**
23049      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23050      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23051      * it should be overridden to provide the proper feedback if necessary.
23052      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23053      * @param {Event} e The event
23054      * @param {Object} data An object containing arbitrary data supplied by the drag source
23055      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23056      * underlying {@link Roo.dd.StatusProxy} can be updated
23057      */
23058     onContainerOver : function(dd, e, data){
23059         return this.dropNotAllowed;
23060     },
23061
23062     /**
23063      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23064      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23065      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23066      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23067      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23068      * @param {Event} e The event
23069      * @param {Object} data An object containing arbitrary data supplied by the drag source
23070      * @return {Boolean} True if the drop was valid, else false
23071      */
23072     onContainerDrop : function(dd, e, data){
23073         return false;
23074     },
23075
23076     /**
23077      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23078      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23079      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23080      * you should override this method and provide a custom implementation.
23081      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23082      * @param {Event} e The event
23083      * @param {Object} data An object containing arbitrary data supplied by the drag source
23084      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23085      * underlying {@link Roo.dd.StatusProxy} can be updated
23086      */
23087     notifyEnter : function(dd, e, data){
23088         return this.dropNotAllowed;
23089     },
23090
23091     /**
23092      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23093      * This method will be called on every mouse movement while the drag source is over the drop zone.
23094      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23095      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23096      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23097      * registered node, it will call {@link #onContainerOver}.
23098      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23099      * @param {Event} e The event
23100      * @param {Object} data An object containing arbitrary data supplied by the drag source
23101      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23102      * underlying {@link Roo.dd.StatusProxy} can be updated
23103      */
23104     notifyOver : function(dd, e, data){
23105         var n = this.getTargetFromEvent(e);
23106         if(!n){ // not over valid drop target
23107             if(this.lastOverNode){
23108                 this.onNodeOut(this.lastOverNode, dd, e, data);
23109                 this.lastOverNode = null;
23110             }
23111             return this.onContainerOver(dd, e, data);
23112         }
23113         if(this.lastOverNode != n){
23114             if(this.lastOverNode){
23115                 this.onNodeOut(this.lastOverNode, dd, e, data);
23116             }
23117             this.onNodeEnter(n, dd, e, data);
23118             this.lastOverNode = n;
23119         }
23120         return this.onNodeOver(n, dd, e, data);
23121     },
23122
23123     /**
23124      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23125      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23126      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23127      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23128      * @param {Event} e The event
23129      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23130      */
23131     notifyOut : function(dd, e, data){
23132         if(this.lastOverNode){
23133             this.onNodeOut(this.lastOverNode, dd, e, data);
23134             this.lastOverNode = null;
23135         }
23136     },
23137
23138     /**
23139      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23140      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23141      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23142      * otherwise it will call {@link #onContainerDrop}.
23143      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23144      * @param {Event} e The event
23145      * @param {Object} data An object containing arbitrary data supplied by the drag source
23146      * @return {Boolean} True if the drop was valid, else false
23147      */
23148     notifyDrop : function(dd, e, data){
23149         if(this.lastOverNode){
23150             this.onNodeOut(this.lastOverNode, dd, e, data);
23151             this.lastOverNode = null;
23152         }
23153         var n = this.getTargetFromEvent(e);
23154         return n ?
23155             this.onNodeDrop(n, dd, e, data) :
23156             this.onContainerDrop(dd, e, data);
23157     },
23158
23159     // private
23160     triggerCacheRefresh : function(){
23161         Roo.dd.DDM.refreshCache(this.groups);
23162     }  
23163 });/*
23164  * Based on:
23165  * Ext JS Library 1.1.1
23166  * Copyright(c) 2006-2007, Ext JS, LLC.
23167  *
23168  * Originally Released Under LGPL - original licence link has changed is not relivant.
23169  *
23170  * Fork - LGPL
23171  * <script type="text/javascript">
23172  */
23173
23174
23175 /**
23176  * @class Roo.data.SortTypes
23177  * @singleton
23178  * Defines the default sorting (casting?) comparison functions used when sorting data.
23179  */
23180 Roo.data.SortTypes = {
23181     /**
23182      * Default sort that does nothing
23183      * @param {Mixed} s The value being converted
23184      * @return {Mixed} The comparison value
23185      */
23186     none : function(s){
23187         return s;
23188     },
23189     
23190     /**
23191      * The regular expression used to strip tags
23192      * @type {RegExp}
23193      * @property
23194      */
23195     stripTagsRE : /<\/?[^>]+>/gi,
23196     
23197     /**
23198      * Strips all HTML tags to sort on text only
23199      * @param {Mixed} s The value being converted
23200      * @return {String} The comparison value
23201      */
23202     asText : function(s){
23203         return String(s).replace(this.stripTagsRE, "");
23204     },
23205     
23206     /**
23207      * Strips all HTML tags to sort on text only - Case insensitive
23208      * @param {Mixed} s The value being converted
23209      * @return {String} The comparison value
23210      */
23211     asUCText : function(s){
23212         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23213     },
23214     
23215     /**
23216      * Case insensitive string
23217      * @param {Mixed} s The value being converted
23218      * @return {String} The comparison value
23219      */
23220     asUCString : function(s) {
23221         return String(s).toUpperCase();
23222     },
23223     
23224     /**
23225      * Date sorting
23226      * @param {Mixed} s The value being converted
23227      * @return {Number} The comparison value
23228      */
23229     asDate : function(s) {
23230         if(!s){
23231             return 0;
23232         }
23233         if(s instanceof Date){
23234             return s.getTime();
23235         }
23236         return Date.parse(String(s));
23237     },
23238     
23239     /**
23240      * Float sorting
23241      * @param {Mixed} s The value being converted
23242      * @return {Float} The comparison value
23243      */
23244     asFloat : function(s) {
23245         var val = parseFloat(String(s).replace(/,/g, ""));
23246         if(isNaN(val)) {
23247             val = 0;
23248         }
23249         return val;
23250     },
23251     
23252     /**
23253      * Integer sorting
23254      * @param {Mixed} s The value being converted
23255      * @return {Number} The comparison value
23256      */
23257     asInt : function(s) {
23258         var val = parseInt(String(s).replace(/,/g, ""));
23259         if(isNaN(val)) {
23260             val = 0;
23261         }
23262         return val;
23263     }
23264 };/*
23265  * Based on:
23266  * Ext JS Library 1.1.1
23267  * Copyright(c) 2006-2007, Ext JS, LLC.
23268  *
23269  * Originally Released Under LGPL - original licence link has changed is not relivant.
23270  *
23271  * Fork - LGPL
23272  * <script type="text/javascript">
23273  */
23274
23275 /**
23276 * @class Roo.data.Record
23277  * Instances of this class encapsulate both record <em>definition</em> information, and record
23278  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23279  * to access Records cached in an {@link Roo.data.Store} object.<br>
23280  * <p>
23281  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23282  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23283  * objects.<br>
23284  * <p>
23285  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23286  * @constructor
23287  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23288  * {@link #create}. The parameters are the same.
23289  * @param {Array} data An associative Array of data values keyed by the field name.
23290  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23291  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23292  * not specified an integer id is generated.
23293  */
23294 Roo.data.Record = function(data, id){
23295     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23296     this.data = data;
23297 };
23298
23299 /**
23300  * Generate a constructor for a specific record layout.
23301  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23302  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23303  * Each field definition object may contain the following properties: <ul>
23304  * <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,
23305  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23306  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23307  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23308  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23309  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23310  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23311  * this may be omitted.</p></li>
23312  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23313  * <ul><li>auto (Default, implies no conversion)</li>
23314  * <li>string</li>
23315  * <li>int</li>
23316  * <li>float</li>
23317  * <li>boolean</li>
23318  * <li>date</li></ul></p></li>
23319  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23320  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23321  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23322  * by the Reader into an object that will be stored in the Record. It is passed the
23323  * following parameters:<ul>
23324  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23325  * </ul></p></li>
23326  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23327  * </ul>
23328  * <br>usage:<br><pre><code>
23329 var TopicRecord = Roo.data.Record.create(
23330     {name: 'title', mapping: 'topic_title'},
23331     {name: 'author', mapping: 'username'},
23332     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23333     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23334     {name: 'lastPoster', mapping: 'user2'},
23335     {name: 'excerpt', mapping: 'post_text'}
23336 );
23337
23338 var myNewRecord = new TopicRecord({
23339     title: 'Do my job please',
23340     author: 'noobie',
23341     totalPosts: 1,
23342     lastPost: new Date(),
23343     lastPoster: 'Animal',
23344     excerpt: 'No way dude!'
23345 });
23346 myStore.add(myNewRecord);
23347 </code></pre>
23348  * @method create
23349  * @static
23350  */
23351 Roo.data.Record.create = function(o){
23352     var f = function(){
23353         f.superclass.constructor.apply(this, arguments);
23354     };
23355     Roo.extend(f, Roo.data.Record);
23356     var p = f.prototype;
23357     p.fields = new Roo.util.MixedCollection(false, function(field){
23358         return field.name;
23359     });
23360     for(var i = 0, len = o.length; i < len; i++){
23361         p.fields.add(new Roo.data.Field(o[i]));
23362     }
23363     f.getField = function(name){
23364         return p.fields.get(name);  
23365     };
23366     return f;
23367 };
23368
23369 Roo.data.Record.AUTO_ID = 1000;
23370 Roo.data.Record.EDIT = 'edit';
23371 Roo.data.Record.REJECT = 'reject';
23372 Roo.data.Record.COMMIT = 'commit';
23373
23374 Roo.data.Record.prototype = {
23375     /**
23376      * Readonly flag - true if this record has been modified.
23377      * @type Boolean
23378      */
23379     dirty : false,
23380     editing : false,
23381     error: null,
23382     modified: null,
23383
23384     // private
23385     join : function(store){
23386         this.store = store;
23387     },
23388
23389     /**
23390      * Set the named field to the specified value.
23391      * @param {String} name The name of the field to set.
23392      * @param {Object} value The value to set the field to.
23393      */
23394     set : function(name, value){
23395         if(this.data[name] == value){
23396             return;
23397         }
23398         this.dirty = true;
23399         if(!this.modified){
23400             this.modified = {};
23401         }
23402         if(typeof this.modified[name] == 'undefined'){
23403             this.modified[name] = this.data[name];
23404         }
23405         this.data[name] = value;
23406         if(!this.editing && this.store){
23407             this.store.afterEdit(this);
23408         }       
23409     },
23410
23411     /**
23412      * Get the value of the named field.
23413      * @param {String} name The name of the field to get the value of.
23414      * @return {Object} The value of the field.
23415      */
23416     get : function(name){
23417         return this.data[name]; 
23418     },
23419
23420     // private
23421     beginEdit : function(){
23422         this.editing = true;
23423         this.modified = {}; 
23424     },
23425
23426     // private
23427     cancelEdit : function(){
23428         this.editing = false;
23429         delete this.modified;
23430     },
23431
23432     // private
23433     endEdit : function(){
23434         this.editing = false;
23435         if(this.dirty && this.store){
23436             this.store.afterEdit(this);
23437         }
23438     },
23439
23440     /**
23441      * Usually called by the {@link Roo.data.Store} which owns the Record.
23442      * Rejects all changes made to the Record since either creation, or the last commit operation.
23443      * Modified fields are reverted to their original values.
23444      * <p>
23445      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23446      * of reject operations.
23447      */
23448     reject : function(){
23449         var m = this.modified;
23450         for(var n in m){
23451             if(typeof m[n] != "function"){
23452                 this.data[n] = m[n];
23453             }
23454         }
23455         this.dirty = false;
23456         delete this.modified;
23457         this.editing = false;
23458         if(this.store){
23459             this.store.afterReject(this);
23460         }
23461     },
23462
23463     /**
23464      * Usually called by the {@link Roo.data.Store} which owns the Record.
23465      * Commits all changes made to the Record since either creation, or the last commit operation.
23466      * <p>
23467      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23468      * of commit operations.
23469      */
23470     commit : function(){
23471         this.dirty = false;
23472         delete this.modified;
23473         this.editing = false;
23474         if(this.store){
23475             this.store.afterCommit(this);
23476         }
23477     },
23478
23479     // private
23480     hasError : function(){
23481         return this.error != null;
23482     },
23483
23484     // private
23485     clearError : function(){
23486         this.error = null;
23487     },
23488
23489     /**
23490      * Creates a copy of this record.
23491      * @param {String} id (optional) A new record id if you don't want to use this record's id
23492      * @return {Record}
23493      */
23494     copy : function(newId) {
23495         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23496     }
23497 };/*
23498  * Based on:
23499  * Ext JS Library 1.1.1
23500  * Copyright(c) 2006-2007, Ext JS, LLC.
23501  *
23502  * Originally Released Under LGPL - original licence link has changed is not relivant.
23503  *
23504  * Fork - LGPL
23505  * <script type="text/javascript">
23506  */
23507
23508
23509
23510 /**
23511  * @class Roo.data.Store
23512  * @extends Roo.util.Observable
23513  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23514  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23515  * <p>
23516  * 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
23517  * has no knowledge of the format of the data returned by the Proxy.<br>
23518  * <p>
23519  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23520  * instances from the data object. These records are cached and made available through accessor functions.
23521  * @constructor
23522  * Creates a new Store.
23523  * @param {Object} config A config object containing the objects needed for the Store to access data,
23524  * and read the data into Records.
23525  */
23526 Roo.data.Store = function(config){
23527     this.data = new Roo.util.MixedCollection(false);
23528     this.data.getKey = function(o){
23529         return o.id;
23530     };
23531     this.baseParams = {};
23532     // private
23533     this.paramNames = {
23534         "start" : "start",
23535         "limit" : "limit",
23536         "sort" : "sort",
23537         "dir" : "dir",
23538         "multisort" : "_multisort"
23539     };
23540
23541     if(config && config.data){
23542         this.inlineData = config.data;
23543         delete config.data;
23544     }
23545
23546     Roo.apply(this, config);
23547     
23548     if(this.reader){ // reader passed
23549         this.reader = Roo.factory(this.reader, Roo.data);
23550         this.reader.xmodule = this.xmodule || false;
23551         if(!this.recordType){
23552             this.recordType = this.reader.recordType;
23553         }
23554         if(this.reader.onMetaChange){
23555             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23556         }
23557     }
23558
23559     if(this.recordType){
23560         this.fields = this.recordType.prototype.fields;
23561     }
23562     this.modified = [];
23563
23564     this.addEvents({
23565         /**
23566          * @event datachanged
23567          * Fires when the data cache has changed, and a widget which is using this Store
23568          * as a Record cache should refresh its view.
23569          * @param {Store} this
23570          */
23571         datachanged : true,
23572         /**
23573          * @event metachange
23574          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23575          * @param {Store} this
23576          * @param {Object} meta The JSON metadata
23577          */
23578         metachange : true,
23579         /**
23580          * @event add
23581          * Fires when Records have been added to the Store
23582          * @param {Store} this
23583          * @param {Roo.data.Record[]} records The array of Records added
23584          * @param {Number} index The index at which the record(s) were added
23585          */
23586         add : true,
23587         /**
23588          * @event remove
23589          * Fires when a Record has been removed from the Store
23590          * @param {Store} this
23591          * @param {Roo.data.Record} record The Record that was removed
23592          * @param {Number} index The index at which the record was removed
23593          */
23594         remove : true,
23595         /**
23596          * @event update
23597          * Fires when a Record has been updated
23598          * @param {Store} this
23599          * @param {Roo.data.Record} record The Record that was updated
23600          * @param {String} operation The update operation being performed.  Value may be one of:
23601          * <pre><code>
23602  Roo.data.Record.EDIT
23603  Roo.data.Record.REJECT
23604  Roo.data.Record.COMMIT
23605          * </code></pre>
23606          */
23607         update : true,
23608         /**
23609          * @event clear
23610          * Fires when the data cache has been cleared.
23611          * @param {Store} this
23612          */
23613         clear : true,
23614         /**
23615          * @event beforeload
23616          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23617          * the load action will be canceled.
23618          * @param {Store} this
23619          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23620          */
23621         beforeload : true,
23622         /**
23623          * @event beforeloadadd
23624          * Fires after a new set of Records has been loaded.
23625          * @param {Store} this
23626          * @param {Roo.data.Record[]} records The Records that were loaded
23627          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23628          */
23629         beforeloadadd : true,
23630         /**
23631          * @event load
23632          * Fires after a new set of Records has been loaded, before they are added to the store.
23633          * @param {Store} this
23634          * @param {Roo.data.Record[]} records The Records that were loaded
23635          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23636          * @params {Object} return from reader
23637          */
23638         load : true,
23639         /**
23640          * @event loadexception
23641          * Fires if an exception occurs in the Proxy during loading.
23642          * Called with the signature of the Proxy's "loadexception" event.
23643          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23644          * 
23645          * @param {Proxy} 
23646          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23647          * @param {Object} load options 
23648          * @param {Object} jsonData from your request (normally this contains the Exception)
23649          */
23650         loadexception : true
23651     });
23652     
23653     if(this.proxy){
23654         this.proxy = Roo.factory(this.proxy, Roo.data);
23655         this.proxy.xmodule = this.xmodule || false;
23656         this.relayEvents(this.proxy,  ["loadexception"]);
23657     }
23658     this.sortToggle = {};
23659     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23660
23661     Roo.data.Store.superclass.constructor.call(this);
23662
23663     if(this.inlineData){
23664         this.loadData(this.inlineData);
23665         delete this.inlineData;
23666     }
23667 };
23668
23669 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23670      /**
23671     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23672     * without a remote query - used by combo/forms at present.
23673     */
23674     
23675     /**
23676     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23677     */
23678     /**
23679     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23680     */
23681     /**
23682     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23683     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23684     */
23685     /**
23686     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23687     * on any HTTP request
23688     */
23689     /**
23690     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23691     */
23692     /**
23693     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23694     */
23695     multiSort: false,
23696     /**
23697     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23698     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23699     */
23700     remoteSort : false,
23701
23702     /**
23703     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23704      * loaded or when a record is removed. (defaults to false).
23705     */
23706     pruneModifiedRecords : false,
23707
23708     // private
23709     lastOptions : null,
23710
23711     /**
23712      * Add Records to the Store and fires the add event.
23713      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23714      */
23715     add : function(records){
23716         records = [].concat(records);
23717         for(var i = 0, len = records.length; i < len; i++){
23718             records[i].join(this);
23719         }
23720         var index = this.data.length;
23721         this.data.addAll(records);
23722         this.fireEvent("add", this, records, index);
23723     },
23724
23725     /**
23726      * Remove a Record from the Store and fires the remove event.
23727      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23728      */
23729     remove : function(record){
23730         var index = this.data.indexOf(record);
23731         this.data.removeAt(index);
23732  
23733         if(this.pruneModifiedRecords){
23734             this.modified.remove(record);
23735         }
23736         this.fireEvent("remove", this, record, index);
23737     },
23738
23739     /**
23740      * Remove all Records from the Store and fires the clear event.
23741      */
23742     removeAll : function(){
23743         this.data.clear();
23744         if(this.pruneModifiedRecords){
23745             this.modified = [];
23746         }
23747         this.fireEvent("clear", this);
23748     },
23749
23750     /**
23751      * Inserts Records to the Store at the given index and fires the add event.
23752      * @param {Number} index The start index at which to insert the passed Records.
23753      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23754      */
23755     insert : function(index, records){
23756         records = [].concat(records);
23757         for(var i = 0, len = records.length; i < len; i++){
23758             this.data.insert(index, records[i]);
23759             records[i].join(this);
23760         }
23761         this.fireEvent("add", this, records, index);
23762     },
23763
23764     /**
23765      * Get the index within the cache of the passed Record.
23766      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23767      * @return {Number} The index of the passed Record. Returns -1 if not found.
23768      */
23769     indexOf : function(record){
23770         return this.data.indexOf(record);
23771     },
23772
23773     /**
23774      * Get the index within the cache of the Record with the passed id.
23775      * @param {String} id The id of the Record to find.
23776      * @return {Number} The index of the Record. Returns -1 if not found.
23777      */
23778     indexOfId : function(id){
23779         return this.data.indexOfKey(id);
23780     },
23781
23782     /**
23783      * Get the Record with the specified id.
23784      * @param {String} id The id of the Record to find.
23785      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23786      */
23787     getById : function(id){
23788         return this.data.key(id);
23789     },
23790
23791     /**
23792      * Get the Record at the specified index.
23793      * @param {Number} index The index of the Record to find.
23794      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23795      */
23796     getAt : function(index){
23797         return this.data.itemAt(index);
23798     },
23799
23800     /**
23801      * Returns a range of Records between specified indices.
23802      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23803      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23804      * @return {Roo.data.Record[]} An array of Records
23805      */
23806     getRange : function(start, end){
23807         return this.data.getRange(start, end);
23808     },
23809
23810     // private
23811     storeOptions : function(o){
23812         o = Roo.apply({}, o);
23813         delete o.callback;
23814         delete o.scope;
23815         this.lastOptions = o;
23816     },
23817
23818     /**
23819      * Loads the Record cache from the configured Proxy using the configured Reader.
23820      * <p>
23821      * If using remote paging, then the first load call must specify the <em>start</em>
23822      * and <em>limit</em> properties in the options.params property to establish the initial
23823      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23824      * <p>
23825      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23826      * and this call will return before the new data has been loaded. Perform any post-processing
23827      * in a callback function, or in a "load" event handler.</strong>
23828      * <p>
23829      * @param {Object} options An object containing properties which control loading options:<ul>
23830      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23831      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23832      * passed the following arguments:<ul>
23833      * <li>r : Roo.data.Record[]</li>
23834      * <li>options: Options object from the load call</li>
23835      * <li>success: Boolean success indicator</li></ul></li>
23836      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23837      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23838      * </ul>
23839      */
23840     load : function(options){
23841         options = options || {};
23842         if(this.fireEvent("beforeload", this, options) !== false){
23843             this.storeOptions(options);
23844             var p = Roo.apply(options.params || {}, this.baseParams);
23845             // if meta was not loaded from remote source.. try requesting it.
23846             if (!this.reader.metaFromRemote) {
23847                 p._requestMeta = 1;
23848             }
23849             if(this.sortInfo && this.remoteSort){
23850                 var pn = this.paramNames;
23851                 p[pn["sort"]] = this.sortInfo.field;
23852                 p[pn["dir"]] = this.sortInfo.direction;
23853             }
23854             if (this.multiSort) {
23855                 var pn = this.paramNames;
23856                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23857             }
23858             
23859             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23860         }
23861     },
23862
23863     /**
23864      * Reloads the Record cache from the configured Proxy using the configured Reader and
23865      * the options from the last load operation performed.
23866      * @param {Object} options (optional) An object containing properties which may override the options
23867      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23868      * the most recently used options are reused).
23869      */
23870     reload : function(options){
23871         this.load(Roo.applyIf(options||{}, this.lastOptions));
23872     },
23873
23874     // private
23875     // Called as a callback by the Reader during a load operation.
23876     loadRecords : function(o, options, success){
23877         if(!o || success === false){
23878             if(success !== false){
23879                 this.fireEvent("load", this, [], options, o);
23880             }
23881             if(options.callback){
23882                 options.callback.call(options.scope || this, [], options, false);
23883             }
23884             return;
23885         }
23886         // if data returned failure - throw an exception.
23887         if (o.success === false) {
23888             // show a message if no listener is registered.
23889             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23890                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23891             }
23892             // loadmask wil be hooked into this..
23893             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23894             return;
23895         }
23896         var r = o.records, t = o.totalRecords || r.length;
23897         
23898         this.fireEvent("beforeloadadd", this, r, options, o);
23899         
23900         if(!options || options.add !== true){
23901             if(this.pruneModifiedRecords){
23902                 this.modified = [];
23903             }
23904             for(var i = 0, len = r.length; i < len; i++){
23905                 r[i].join(this);
23906             }
23907             if(this.snapshot){
23908                 this.data = this.snapshot;
23909                 delete this.snapshot;
23910             }
23911             this.data.clear();
23912             this.data.addAll(r);
23913             this.totalLength = t;
23914             this.applySort();
23915             this.fireEvent("datachanged", this);
23916         }else{
23917             this.totalLength = Math.max(t, this.data.length+r.length);
23918             this.add(r);
23919         }
23920         
23921         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23922                 
23923             var e = new Roo.data.Record({});
23924
23925             e.set(this.parent.displayField, this.parent.emptyTitle);
23926             e.set(this.parent.valueField, '');
23927
23928             this.insert(0, e);
23929         }
23930             
23931         this.fireEvent("load", this, r, options, o);
23932         if(options.callback){
23933             options.callback.call(options.scope || this, r, options, true);
23934         }
23935     },
23936
23937
23938     /**
23939      * Loads data from a passed data block. A Reader which understands the format of the data
23940      * must have been configured in the constructor.
23941      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23942      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23943      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23944      */
23945     loadData : function(o, append){
23946         var r = this.reader.readRecords(o);
23947         this.loadRecords(r, {add: append}, true);
23948     },
23949     
23950      /**
23951      * using 'cn' the nested child reader read the child array into it's child stores.
23952      * @param {Object} rec The record with a 'children array
23953      */
23954     loadDataFromChildren : function(rec)
23955     {
23956         this.loadData(this.reader.toLoadData(rec));
23957     },
23958     
23959
23960     /**
23961      * Gets the number of cached records.
23962      * <p>
23963      * <em>If using paging, this may not be the total size of the dataset. If the data object
23964      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23965      * the data set size</em>
23966      */
23967     getCount : function(){
23968         return this.data.length || 0;
23969     },
23970
23971     /**
23972      * Gets the total number of records in the dataset as returned by the server.
23973      * <p>
23974      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23975      * the dataset size</em>
23976      */
23977     getTotalCount : function(){
23978         return this.totalLength || 0;
23979     },
23980
23981     /**
23982      * Returns the sort state of the Store as an object with two properties:
23983      * <pre><code>
23984  field {String} The name of the field by which the Records are sorted
23985  direction {String} The sort order, "ASC" or "DESC"
23986      * </code></pre>
23987      */
23988     getSortState : function(){
23989         return this.sortInfo;
23990     },
23991
23992     // private
23993     applySort : function(){
23994         if(this.sortInfo && !this.remoteSort){
23995             var s = this.sortInfo, f = s.field;
23996             var st = this.fields.get(f).sortType;
23997             var fn = function(r1, r2){
23998                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23999                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24000             };
24001             this.data.sort(s.direction, fn);
24002             if(this.snapshot && this.snapshot != this.data){
24003                 this.snapshot.sort(s.direction, fn);
24004             }
24005         }
24006     },
24007
24008     /**
24009      * Sets the default sort column and order to be used by the next load operation.
24010      * @param {String} fieldName The name of the field to sort by.
24011      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24012      */
24013     setDefaultSort : function(field, dir){
24014         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24015     },
24016
24017     /**
24018      * Sort the Records.
24019      * If remote sorting is used, the sort is performed on the server, and the cache is
24020      * reloaded. If local sorting is used, the cache is sorted internally.
24021      * @param {String} fieldName The name of the field to sort by.
24022      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24023      */
24024     sort : function(fieldName, dir){
24025         var f = this.fields.get(fieldName);
24026         if(!dir){
24027             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24028             
24029             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24030                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24031             }else{
24032                 dir = f.sortDir;
24033             }
24034         }
24035         this.sortToggle[f.name] = dir;
24036         this.sortInfo = {field: f.name, direction: dir};
24037         if(!this.remoteSort){
24038             this.applySort();
24039             this.fireEvent("datachanged", this);
24040         }else{
24041             this.load(this.lastOptions);
24042         }
24043     },
24044
24045     /**
24046      * Calls the specified function for each of the Records in the cache.
24047      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24048      * Returning <em>false</em> aborts and exits the iteration.
24049      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24050      */
24051     each : function(fn, scope){
24052         this.data.each(fn, scope);
24053     },
24054
24055     /**
24056      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24057      * (e.g., during paging).
24058      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24059      */
24060     getModifiedRecords : function(){
24061         return this.modified;
24062     },
24063
24064     // private
24065     createFilterFn : function(property, value, anyMatch){
24066         if(!value.exec){ // not a regex
24067             value = String(value);
24068             if(value.length == 0){
24069                 return false;
24070             }
24071             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24072         }
24073         return function(r){
24074             return value.test(r.data[property]);
24075         };
24076     },
24077
24078     /**
24079      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24080      * @param {String} property A field on your records
24081      * @param {Number} start The record index to start at (defaults to 0)
24082      * @param {Number} end The last record index to include (defaults to length - 1)
24083      * @return {Number} The sum
24084      */
24085     sum : function(property, start, end){
24086         var rs = this.data.items, v = 0;
24087         start = start || 0;
24088         end = (end || end === 0) ? end : rs.length-1;
24089
24090         for(var i = start; i <= end; i++){
24091             v += (rs[i].data[property] || 0);
24092         }
24093         return v;
24094     },
24095
24096     /**
24097      * Filter the records by a specified property.
24098      * @param {String} field A field on your records
24099      * @param {String/RegExp} value Either a string that the field
24100      * should start with or a RegExp to test against the field
24101      * @param {Boolean} anyMatch True to match any part not just the beginning
24102      */
24103     filter : function(property, value, anyMatch){
24104         var fn = this.createFilterFn(property, value, anyMatch);
24105         return fn ? this.filterBy(fn) : this.clearFilter();
24106     },
24107
24108     /**
24109      * Filter by a function. The specified function will be called with each
24110      * record in this data source. If the function returns true the record is included,
24111      * otherwise it is filtered.
24112      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24113      * @param {Object} scope (optional) The scope of the function (defaults to this)
24114      */
24115     filterBy : function(fn, scope){
24116         this.snapshot = this.snapshot || this.data;
24117         this.data = this.queryBy(fn, scope||this);
24118         this.fireEvent("datachanged", this);
24119     },
24120
24121     /**
24122      * Query the records by a specified property.
24123      * @param {String} field A field on your records
24124      * @param {String/RegExp} value Either a string that the field
24125      * should start with or a RegExp to test against the field
24126      * @param {Boolean} anyMatch True to match any part not just the beginning
24127      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24128      */
24129     query : function(property, value, anyMatch){
24130         var fn = this.createFilterFn(property, value, anyMatch);
24131         return fn ? this.queryBy(fn) : this.data.clone();
24132     },
24133
24134     /**
24135      * Query by a function. The specified function will be called with each
24136      * record in this data source. If the function returns true the record is included
24137      * in the results.
24138      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24139      * @param {Object} scope (optional) The scope of the function (defaults to this)
24140       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24141      **/
24142     queryBy : function(fn, scope){
24143         var data = this.snapshot || this.data;
24144         return data.filterBy(fn, scope||this);
24145     },
24146
24147     /**
24148      * Collects unique values for a particular dataIndex from this store.
24149      * @param {String} dataIndex The property to collect
24150      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24151      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24152      * @return {Array} An array of the unique values
24153      **/
24154     collect : function(dataIndex, allowNull, bypassFilter){
24155         var d = (bypassFilter === true && this.snapshot) ?
24156                 this.snapshot.items : this.data.items;
24157         var v, sv, r = [], l = {};
24158         for(var i = 0, len = d.length; i < len; i++){
24159             v = d[i].data[dataIndex];
24160             sv = String(v);
24161             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24162                 l[sv] = true;
24163                 r[r.length] = v;
24164             }
24165         }
24166         return r;
24167     },
24168
24169     /**
24170      * Revert to a view of the Record cache with no filtering applied.
24171      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24172      */
24173     clearFilter : function(suppressEvent){
24174         if(this.snapshot && this.snapshot != this.data){
24175             this.data = this.snapshot;
24176             delete this.snapshot;
24177             if(suppressEvent !== true){
24178                 this.fireEvent("datachanged", this);
24179             }
24180         }
24181     },
24182
24183     // private
24184     afterEdit : function(record){
24185         if(this.modified.indexOf(record) == -1){
24186             this.modified.push(record);
24187         }
24188         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24189     },
24190     
24191     // private
24192     afterReject : function(record){
24193         this.modified.remove(record);
24194         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24195     },
24196
24197     // private
24198     afterCommit : function(record){
24199         this.modified.remove(record);
24200         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24201     },
24202
24203     /**
24204      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24205      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24206      */
24207     commitChanges : function(){
24208         var m = this.modified.slice(0);
24209         this.modified = [];
24210         for(var i = 0, len = m.length; i < len; i++){
24211             m[i].commit();
24212         }
24213     },
24214
24215     /**
24216      * Cancel outstanding changes on all changed records.
24217      */
24218     rejectChanges : function(){
24219         var m = this.modified.slice(0);
24220         this.modified = [];
24221         for(var i = 0, len = m.length; i < len; i++){
24222             m[i].reject();
24223         }
24224     },
24225
24226     onMetaChange : function(meta, rtype, o){
24227         this.recordType = rtype;
24228         this.fields = rtype.prototype.fields;
24229         delete this.snapshot;
24230         this.sortInfo = meta.sortInfo || this.sortInfo;
24231         this.modified = [];
24232         this.fireEvent('metachange', this, this.reader.meta);
24233     },
24234     
24235     moveIndex : function(data, type)
24236     {
24237         var index = this.indexOf(data);
24238         
24239         var newIndex = index + type;
24240         
24241         this.remove(data);
24242         
24243         this.insert(newIndex, data);
24244         
24245     }
24246 });/*
24247  * Based on:
24248  * Ext JS Library 1.1.1
24249  * Copyright(c) 2006-2007, Ext JS, LLC.
24250  *
24251  * Originally Released Under LGPL - original licence link has changed is not relivant.
24252  *
24253  * Fork - LGPL
24254  * <script type="text/javascript">
24255  */
24256
24257 /**
24258  * @class Roo.data.SimpleStore
24259  * @extends Roo.data.Store
24260  * Small helper class to make creating Stores from Array data easier.
24261  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24262  * @cfg {Array} fields An array of field definition objects, or field name strings.
24263  * @cfg {Object} an existing reader (eg. copied from another store)
24264  * @cfg {Array} data The multi-dimensional array of data
24265  * @constructor
24266  * @param {Object} config
24267  */
24268 Roo.data.SimpleStore = function(config)
24269 {
24270     Roo.data.SimpleStore.superclass.constructor.call(this, {
24271         isLocal : true,
24272         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24273                 id: config.id
24274             },
24275             Roo.data.Record.create(config.fields)
24276         ),
24277         proxy : new Roo.data.MemoryProxy(config.data)
24278     });
24279     this.load();
24280 };
24281 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24282  * Based on:
24283  * Ext JS Library 1.1.1
24284  * Copyright(c) 2006-2007, Ext JS, LLC.
24285  *
24286  * Originally Released Under LGPL - original licence link has changed is not relivant.
24287  *
24288  * Fork - LGPL
24289  * <script type="text/javascript">
24290  */
24291
24292 /**
24293 /**
24294  * @extends Roo.data.Store
24295  * @class Roo.data.JsonStore
24296  * Small helper class to make creating Stores for JSON data easier. <br/>
24297 <pre><code>
24298 var store = new Roo.data.JsonStore({
24299     url: 'get-images.php',
24300     root: 'images',
24301     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24302 });
24303 </code></pre>
24304  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24305  * JsonReader and HttpProxy (unless inline data is provided).</b>
24306  * @cfg {Array} fields An array of field definition objects, or field name strings.
24307  * @constructor
24308  * @param {Object} config
24309  */
24310 Roo.data.JsonStore = function(c){
24311     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24312         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24313         reader: new Roo.data.JsonReader(c, c.fields)
24314     }));
24315 };
24316 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24317  * Based on:
24318  * Ext JS Library 1.1.1
24319  * Copyright(c) 2006-2007, Ext JS, LLC.
24320  *
24321  * Originally Released Under LGPL - original licence link has changed is not relivant.
24322  *
24323  * Fork - LGPL
24324  * <script type="text/javascript">
24325  */
24326
24327  
24328 Roo.data.Field = function(config){
24329     if(typeof config == "string"){
24330         config = {name: config};
24331     }
24332     Roo.apply(this, config);
24333     
24334     if(!this.type){
24335         this.type = "auto";
24336     }
24337     
24338     var st = Roo.data.SortTypes;
24339     // named sortTypes are supported, here we look them up
24340     if(typeof this.sortType == "string"){
24341         this.sortType = st[this.sortType];
24342     }
24343     
24344     // set default sortType for strings and dates
24345     if(!this.sortType){
24346         switch(this.type){
24347             case "string":
24348                 this.sortType = st.asUCString;
24349                 break;
24350             case "date":
24351                 this.sortType = st.asDate;
24352                 break;
24353             default:
24354                 this.sortType = st.none;
24355         }
24356     }
24357
24358     // define once
24359     var stripRe = /[\$,%]/g;
24360
24361     // prebuilt conversion function for this field, instead of
24362     // switching every time we're reading a value
24363     if(!this.convert){
24364         var cv, dateFormat = this.dateFormat;
24365         switch(this.type){
24366             case "":
24367             case "auto":
24368             case undefined:
24369                 cv = function(v){ return v; };
24370                 break;
24371             case "string":
24372                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24373                 break;
24374             case "int":
24375                 cv = function(v){
24376                     return v !== undefined && v !== null && v !== '' ?
24377                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24378                     };
24379                 break;
24380             case "float":
24381                 cv = function(v){
24382                     return v !== undefined && v !== null && v !== '' ?
24383                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24384                     };
24385                 break;
24386             case "bool":
24387             case "boolean":
24388                 cv = function(v){ return v === true || v === "true" || v == 1; };
24389                 break;
24390             case "date":
24391                 cv = function(v){
24392                     if(!v){
24393                         return '';
24394                     }
24395                     if(v instanceof Date){
24396                         return v;
24397                     }
24398                     if(dateFormat){
24399                         if(dateFormat == "timestamp"){
24400                             return new Date(v*1000);
24401                         }
24402                         return Date.parseDate(v, dateFormat);
24403                     }
24404                     var parsed = Date.parse(v);
24405                     return parsed ? new Date(parsed) : null;
24406                 };
24407              break;
24408             
24409         }
24410         this.convert = cv;
24411     }
24412 };
24413
24414 Roo.data.Field.prototype = {
24415     dateFormat: null,
24416     defaultValue: "",
24417     mapping: null,
24418     sortType : null,
24419     sortDir : "ASC"
24420 };/*
24421  * Based on:
24422  * Ext JS Library 1.1.1
24423  * Copyright(c) 2006-2007, Ext JS, LLC.
24424  *
24425  * Originally Released Under LGPL - original licence link has changed is not relivant.
24426  *
24427  * Fork - LGPL
24428  * <script type="text/javascript">
24429  */
24430  
24431 // Base class for reading structured data from a data source.  This class is intended to be
24432 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24433
24434 /**
24435  * @class Roo.data.DataReader
24436  * Base class for reading structured data from a data source.  This class is intended to be
24437  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24438  */
24439
24440 Roo.data.DataReader = function(meta, recordType){
24441     
24442     this.meta = meta;
24443     
24444     this.recordType = recordType instanceof Array ? 
24445         Roo.data.Record.create(recordType) : recordType;
24446 };
24447
24448 Roo.data.DataReader.prototype = {
24449     
24450     
24451     readerType : 'Data',
24452      /**
24453      * Create an empty record
24454      * @param {Object} data (optional) - overlay some values
24455      * @return {Roo.data.Record} record created.
24456      */
24457     newRow :  function(d) {
24458         var da =  {};
24459         this.recordType.prototype.fields.each(function(c) {
24460             switch( c.type) {
24461                 case 'int' : da[c.name] = 0; break;
24462                 case 'date' : da[c.name] = new Date(); break;
24463                 case 'float' : da[c.name] = 0.0; break;
24464                 case 'boolean' : da[c.name] = false; break;
24465                 default : da[c.name] = ""; break;
24466             }
24467             
24468         });
24469         return new this.recordType(Roo.apply(da, d));
24470     }
24471     
24472     
24473 };/*
24474  * Based on:
24475  * Ext JS Library 1.1.1
24476  * Copyright(c) 2006-2007, Ext JS, LLC.
24477  *
24478  * Originally Released Under LGPL - original licence link has changed is not relivant.
24479  *
24480  * Fork - LGPL
24481  * <script type="text/javascript">
24482  */
24483
24484 /**
24485  * @class Roo.data.DataProxy
24486  * @extends Roo.data.Observable
24487  * This class is an abstract base class for implementations which provide retrieval of
24488  * unformatted data objects.<br>
24489  * <p>
24490  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24491  * (of the appropriate type which knows how to parse the data object) to provide a block of
24492  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24493  * <p>
24494  * Custom implementations must implement the load method as described in
24495  * {@link Roo.data.HttpProxy#load}.
24496  */
24497 Roo.data.DataProxy = function(){
24498     this.addEvents({
24499         /**
24500          * @event beforeload
24501          * Fires before a network request is made to retrieve a data object.
24502          * @param {Object} This DataProxy object.
24503          * @param {Object} params The params parameter to the load function.
24504          */
24505         beforeload : true,
24506         /**
24507          * @event load
24508          * Fires before the load method's callback is called.
24509          * @param {Object} This DataProxy object.
24510          * @param {Object} o The data object.
24511          * @param {Object} arg The callback argument object passed to the load function.
24512          */
24513         load : true,
24514         /**
24515          * @event loadexception
24516          * Fires if an Exception occurs during data retrieval.
24517          * @param {Object} This DataProxy object.
24518          * @param {Object} o The data object.
24519          * @param {Object} arg The callback argument object passed to the load function.
24520          * @param {Object} e The Exception.
24521          */
24522         loadexception : true
24523     });
24524     Roo.data.DataProxy.superclass.constructor.call(this);
24525 };
24526
24527 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24528
24529     /**
24530      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24531      */
24532 /*
24533  * Based on:
24534  * Ext JS Library 1.1.1
24535  * Copyright(c) 2006-2007, Ext JS, LLC.
24536  *
24537  * Originally Released Under LGPL - original licence link has changed is not relivant.
24538  *
24539  * Fork - LGPL
24540  * <script type="text/javascript">
24541  */
24542 /**
24543  * @class Roo.data.MemoryProxy
24544  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24545  * to the Reader when its load method is called.
24546  * @constructor
24547  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24548  */
24549 Roo.data.MemoryProxy = function(data){
24550     if (data.data) {
24551         data = data.data;
24552     }
24553     Roo.data.MemoryProxy.superclass.constructor.call(this);
24554     this.data = data;
24555 };
24556
24557 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24558     
24559     /**
24560      * Load data from the requested source (in this case an in-memory
24561      * data object passed to the constructor), read the data object into
24562      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24563      * process that block using the passed callback.
24564      * @param {Object} params This parameter is not used by the MemoryProxy class.
24565      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24566      * object into a block of Roo.data.Records.
24567      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24568      * The function must be passed <ul>
24569      * <li>The Record block object</li>
24570      * <li>The "arg" argument from the load function</li>
24571      * <li>A boolean success indicator</li>
24572      * </ul>
24573      * @param {Object} scope The scope in which to call the callback
24574      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24575      */
24576     load : function(params, reader, callback, scope, arg){
24577         params = params || {};
24578         var result;
24579         try {
24580             result = reader.readRecords(params.data ? params.data :this.data);
24581         }catch(e){
24582             this.fireEvent("loadexception", this, arg, null, e);
24583             callback.call(scope, null, arg, false);
24584             return;
24585         }
24586         callback.call(scope, result, arg, true);
24587     },
24588     
24589     // private
24590     update : function(params, records){
24591         
24592     }
24593 });/*
24594  * Based on:
24595  * Ext JS Library 1.1.1
24596  * Copyright(c) 2006-2007, Ext JS, LLC.
24597  *
24598  * Originally Released Under LGPL - original licence link has changed is not relivant.
24599  *
24600  * Fork - LGPL
24601  * <script type="text/javascript">
24602  */
24603 /**
24604  * @class Roo.data.HttpProxy
24605  * @extends Roo.data.DataProxy
24606  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24607  * configured to reference a certain URL.<br><br>
24608  * <p>
24609  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24610  * from which the running page was served.<br><br>
24611  * <p>
24612  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24613  * <p>
24614  * Be aware that to enable the browser to parse an XML document, the server must set
24615  * the Content-Type header in the HTTP response to "text/xml".
24616  * @constructor
24617  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24618  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24619  * will be used to make the request.
24620  */
24621 Roo.data.HttpProxy = function(conn){
24622     Roo.data.HttpProxy.superclass.constructor.call(this);
24623     // is conn a conn config or a real conn?
24624     this.conn = conn;
24625     this.useAjax = !conn || !conn.events;
24626   
24627 };
24628
24629 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24630     // thse are take from connection...
24631     
24632     /**
24633      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24634      */
24635     /**
24636      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24637      * extra parameters to each request made by this object. (defaults to undefined)
24638      */
24639     /**
24640      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24641      *  to each request made by this object. (defaults to undefined)
24642      */
24643     /**
24644      * @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)
24645      */
24646     /**
24647      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24648      */
24649      /**
24650      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24651      * @type Boolean
24652      */
24653   
24654
24655     /**
24656      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24657      * @type Boolean
24658      */
24659     /**
24660      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24661      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24662      * a finer-grained basis than the DataProxy events.
24663      */
24664     getConnection : function(){
24665         return this.useAjax ? Roo.Ajax : this.conn;
24666     },
24667
24668     /**
24669      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24670      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24671      * process that block using the passed callback.
24672      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24673      * for the request to the remote server.
24674      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24675      * object into a block of Roo.data.Records.
24676      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24677      * The function must be passed <ul>
24678      * <li>The Record block object</li>
24679      * <li>The "arg" argument from the load function</li>
24680      * <li>A boolean success indicator</li>
24681      * </ul>
24682      * @param {Object} scope The scope in which to call the callback
24683      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24684      */
24685     load : function(params, reader, callback, scope, arg){
24686         if(this.fireEvent("beforeload", this, params) !== false){
24687             var  o = {
24688                 params : params || {},
24689                 request: {
24690                     callback : callback,
24691                     scope : scope,
24692                     arg : arg
24693                 },
24694                 reader: reader,
24695                 callback : this.loadResponse,
24696                 scope: this
24697             };
24698             if(this.useAjax){
24699                 Roo.applyIf(o, this.conn);
24700                 if(this.activeRequest){
24701                     Roo.Ajax.abort(this.activeRequest);
24702                 }
24703                 this.activeRequest = Roo.Ajax.request(o);
24704             }else{
24705                 this.conn.request(o);
24706             }
24707         }else{
24708             callback.call(scope||this, null, arg, false);
24709         }
24710     },
24711
24712     // private
24713     loadResponse : function(o, success, response){
24714         delete this.activeRequest;
24715         if(!success){
24716             this.fireEvent("loadexception", this, o, response);
24717             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24718             return;
24719         }
24720         var result;
24721         try {
24722             result = o.reader.read(response);
24723         }catch(e){
24724             this.fireEvent("loadexception", this, o, response, e);
24725             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24726             return;
24727         }
24728         
24729         this.fireEvent("load", this, o, o.request.arg);
24730         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24731     },
24732
24733     // private
24734     update : function(dataSet){
24735
24736     },
24737
24738     // private
24739     updateResponse : function(dataSet){
24740
24741     }
24742 });/*
24743  * Based on:
24744  * Ext JS Library 1.1.1
24745  * Copyright(c) 2006-2007, Ext JS, LLC.
24746  *
24747  * Originally Released Under LGPL - original licence link has changed is not relivant.
24748  *
24749  * Fork - LGPL
24750  * <script type="text/javascript">
24751  */
24752
24753 /**
24754  * @class Roo.data.ScriptTagProxy
24755  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24756  * other than the originating domain of the running page.<br><br>
24757  * <p>
24758  * <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
24759  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24760  * <p>
24761  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24762  * source code that is used as the source inside a &lt;script> tag.<br><br>
24763  * <p>
24764  * In order for the browser to process the returned data, the server must wrap the data object
24765  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24766  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24767  * depending on whether the callback name was passed:
24768  * <p>
24769  * <pre><code>
24770 boolean scriptTag = false;
24771 String cb = request.getParameter("callback");
24772 if (cb != null) {
24773     scriptTag = true;
24774     response.setContentType("text/javascript");
24775 } else {
24776     response.setContentType("application/x-json");
24777 }
24778 Writer out = response.getWriter();
24779 if (scriptTag) {
24780     out.write(cb + "(");
24781 }
24782 out.print(dataBlock.toJsonString());
24783 if (scriptTag) {
24784     out.write(");");
24785 }
24786 </pre></code>
24787  *
24788  * @constructor
24789  * @param {Object} config A configuration object.
24790  */
24791 Roo.data.ScriptTagProxy = function(config){
24792     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24793     Roo.apply(this, config);
24794     this.head = document.getElementsByTagName("head")[0];
24795 };
24796
24797 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24798
24799 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24800     /**
24801      * @cfg {String} url The URL from which to request the data object.
24802      */
24803     /**
24804      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24805      */
24806     timeout : 30000,
24807     /**
24808      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24809      * the server the name of the callback function set up by the load call to process the returned data object.
24810      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24811      * javascript output which calls this named function passing the data object as its only parameter.
24812      */
24813     callbackParam : "callback",
24814     /**
24815      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24816      * name to the request.
24817      */
24818     nocache : true,
24819
24820     /**
24821      * Load data from the configured URL, read the data object into
24822      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24823      * process that block using the passed callback.
24824      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24825      * for the request to the remote server.
24826      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24827      * object into a block of Roo.data.Records.
24828      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24829      * The function must be passed <ul>
24830      * <li>The Record block object</li>
24831      * <li>The "arg" argument from the load function</li>
24832      * <li>A boolean success indicator</li>
24833      * </ul>
24834      * @param {Object} scope The scope in which to call the callback
24835      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24836      */
24837     load : function(params, reader, callback, scope, arg){
24838         if(this.fireEvent("beforeload", this, params) !== false){
24839
24840             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24841
24842             var url = this.url;
24843             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24844             if(this.nocache){
24845                 url += "&_dc=" + (new Date().getTime());
24846             }
24847             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24848             var trans = {
24849                 id : transId,
24850                 cb : "stcCallback"+transId,
24851                 scriptId : "stcScript"+transId,
24852                 params : params,
24853                 arg : arg,
24854                 url : url,
24855                 callback : callback,
24856                 scope : scope,
24857                 reader : reader
24858             };
24859             var conn = this;
24860
24861             window[trans.cb] = function(o){
24862                 conn.handleResponse(o, trans);
24863             };
24864
24865             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24866
24867             if(this.autoAbort !== false){
24868                 this.abort();
24869             }
24870
24871             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24872
24873             var script = document.createElement("script");
24874             script.setAttribute("src", url);
24875             script.setAttribute("type", "text/javascript");
24876             script.setAttribute("id", trans.scriptId);
24877             this.head.appendChild(script);
24878
24879             this.trans = trans;
24880         }else{
24881             callback.call(scope||this, null, arg, false);
24882         }
24883     },
24884
24885     // private
24886     isLoading : function(){
24887         return this.trans ? true : false;
24888     },
24889
24890     /**
24891      * Abort the current server request.
24892      */
24893     abort : function(){
24894         if(this.isLoading()){
24895             this.destroyTrans(this.trans);
24896         }
24897     },
24898
24899     // private
24900     destroyTrans : function(trans, isLoaded){
24901         this.head.removeChild(document.getElementById(trans.scriptId));
24902         clearTimeout(trans.timeoutId);
24903         if(isLoaded){
24904             window[trans.cb] = undefined;
24905             try{
24906                 delete window[trans.cb];
24907             }catch(e){}
24908         }else{
24909             // if hasn't been loaded, wait for load to remove it to prevent script error
24910             window[trans.cb] = function(){
24911                 window[trans.cb] = undefined;
24912                 try{
24913                     delete window[trans.cb];
24914                 }catch(e){}
24915             };
24916         }
24917     },
24918
24919     // private
24920     handleResponse : function(o, trans){
24921         this.trans = false;
24922         this.destroyTrans(trans, true);
24923         var result;
24924         try {
24925             result = trans.reader.readRecords(o);
24926         }catch(e){
24927             this.fireEvent("loadexception", this, o, trans.arg, e);
24928             trans.callback.call(trans.scope||window, null, trans.arg, false);
24929             return;
24930         }
24931         this.fireEvent("load", this, o, trans.arg);
24932         trans.callback.call(trans.scope||window, result, trans.arg, true);
24933     },
24934
24935     // private
24936     handleFailure : function(trans){
24937         this.trans = false;
24938         this.destroyTrans(trans, false);
24939         this.fireEvent("loadexception", this, null, trans.arg);
24940         trans.callback.call(trans.scope||window, null, trans.arg, false);
24941     }
24942 });/*
24943  * Based on:
24944  * Ext JS Library 1.1.1
24945  * Copyright(c) 2006-2007, Ext JS, LLC.
24946  *
24947  * Originally Released Under LGPL - original licence link has changed is not relivant.
24948  *
24949  * Fork - LGPL
24950  * <script type="text/javascript">
24951  */
24952
24953 /**
24954  * @class Roo.data.JsonReader
24955  * @extends Roo.data.DataReader
24956  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24957  * based on mappings in a provided Roo.data.Record constructor.
24958  * 
24959  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24960  * in the reply previously. 
24961  * 
24962  * <p>
24963  * Example code:
24964  * <pre><code>
24965 var RecordDef = Roo.data.Record.create([
24966     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24967     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24968 ]);
24969 var myReader = new Roo.data.JsonReader({
24970     totalProperty: "results",    // The property which contains the total dataset size (optional)
24971     root: "rows",                // The property which contains an Array of row objects
24972     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24973 }, RecordDef);
24974 </code></pre>
24975  * <p>
24976  * This would consume a JSON file like this:
24977  * <pre><code>
24978 { 'results': 2, 'rows': [
24979     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24980     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24981 }
24982 </code></pre>
24983  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24984  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24985  * paged from the remote server.
24986  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24987  * @cfg {String} root name of the property which contains the Array of row objects.
24988  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24989  * @cfg {Array} fields Array of field definition objects
24990  * @constructor
24991  * Create a new JsonReader
24992  * @param {Object} meta Metadata configuration options
24993  * @param {Object} recordType Either an Array of field definition objects,
24994  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24995  */
24996 Roo.data.JsonReader = function(meta, recordType){
24997     
24998     meta = meta || {};
24999     // set some defaults:
25000     Roo.applyIf(meta, {
25001         totalProperty: 'total',
25002         successProperty : 'success',
25003         root : 'data',
25004         id : 'id'
25005     });
25006     
25007     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25008 };
25009 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25010     
25011     readerType : 'Json',
25012     
25013     /**
25014      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25015      * Used by Store query builder to append _requestMeta to params.
25016      * 
25017      */
25018     metaFromRemote : false,
25019     /**
25020      * This method is only used by a DataProxy which has retrieved data from a remote server.
25021      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25022      * @return {Object} data A data block which is used by an Roo.data.Store object as
25023      * a cache of Roo.data.Records.
25024      */
25025     read : function(response){
25026         var json = response.responseText;
25027        
25028         var o = /* eval:var:o */ eval("("+json+")");
25029         if(!o) {
25030             throw {message: "JsonReader.read: Json object not found"};
25031         }
25032         
25033         if(o.metaData){
25034             
25035             delete this.ef;
25036             this.metaFromRemote = true;
25037             this.meta = o.metaData;
25038             this.recordType = Roo.data.Record.create(o.metaData.fields);
25039             this.onMetaChange(this.meta, this.recordType, o);
25040         }
25041         return this.readRecords(o);
25042     },
25043
25044     // private function a store will implement
25045     onMetaChange : function(meta, recordType, o){
25046
25047     },
25048
25049     /**
25050          * @ignore
25051          */
25052     simpleAccess: function(obj, subsc) {
25053         return obj[subsc];
25054     },
25055
25056         /**
25057          * @ignore
25058          */
25059     getJsonAccessor: function(){
25060         var re = /[\[\.]/;
25061         return function(expr) {
25062             try {
25063                 return(re.test(expr))
25064                     ? new Function("obj", "return obj." + expr)
25065                     : function(obj){
25066                         return obj[expr];
25067                     };
25068             } catch(e){}
25069             return Roo.emptyFn;
25070         };
25071     }(),
25072
25073     /**
25074      * Create a data block containing Roo.data.Records from an XML document.
25075      * @param {Object} o An object which contains an Array of row objects in the property specified
25076      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25077      * which contains the total size of the dataset.
25078      * @return {Object} data A data block which is used by an Roo.data.Store object as
25079      * a cache of Roo.data.Records.
25080      */
25081     readRecords : function(o){
25082         /**
25083          * After any data loads, the raw JSON data is available for further custom processing.
25084          * @type Object
25085          */
25086         this.o = o;
25087         var s = this.meta, Record = this.recordType,
25088             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25089
25090 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25091         if (!this.ef) {
25092             if(s.totalProperty) {
25093                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25094                 }
25095                 if(s.successProperty) {
25096                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25097                 }
25098                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25099                 if (s.id) {
25100                         var g = this.getJsonAccessor(s.id);
25101                         this.getId = function(rec) {
25102                                 var r = g(rec);  
25103                                 return (r === undefined || r === "") ? null : r;
25104                         };
25105                 } else {
25106                         this.getId = function(){return null;};
25107                 }
25108             this.ef = [];
25109             for(var jj = 0; jj < fl; jj++){
25110                 f = fi[jj];
25111                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25112                 this.ef[jj] = this.getJsonAccessor(map);
25113             }
25114         }
25115
25116         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25117         if(s.totalProperty){
25118             var vt = parseInt(this.getTotal(o), 10);
25119             if(!isNaN(vt)){
25120                 totalRecords = vt;
25121             }
25122         }
25123         if(s.successProperty){
25124             var vs = this.getSuccess(o);
25125             if(vs === false || vs === 'false'){
25126                 success = false;
25127             }
25128         }
25129         var records = [];
25130         for(var i = 0; i < c; i++){
25131                 var n = root[i];
25132             var values = {};
25133             var id = this.getId(n);
25134             for(var j = 0; j < fl; j++){
25135                 f = fi[j];
25136             var v = this.ef[j](n);
25137             if (!f.convert) {
25138                 Roo.log('missing convert for ' + f.name);
25139                 Roo.log(f);
25140                 continue;
25141             }
25142             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25143             }
25144             var record = new Record(values, id);
25145             record.json = n;
25146             records[i] = record;
25147         }
25148         return {
25149             raw : o,
25150             success : success,
25151             records : records,
25152             totalRecords : totalRecords
25153         };
25154     },
25155     // used when loading children.. @see loadDataFromChildren
25156     toLoadData: function(rec)
25157     {
25158         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25159         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25160         return { data : data, total : data.length };
25161         
25162     }
25163 });/*
25164  * Based on:
25165  * Ext JS Library 1.1.1
25166  * Copyright(c) 2006-2007, Ext JS, LLC.
25167  *
25168  * Originally Released Under LGPL - original licence link has changed is not relivant.
25169  *
25170  * Fork - LGPL
25171  * <script type="text/javascript">
25172  */
25173
25174 /**
25175  * @class Roo.data.XmlReader
25176  * @extends Roo.data.DataReader
25177  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25178  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25179  * <p>
25180  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25181  * header in the HTTP response must be set to "text/xml".</em>
25182  * <p>
25183  * Example code:
25184  * <pre><code>
25185 var RecordDef = Roo.data.Record.create([
25186    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25187    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25188 ]);
25189 var myReader = new Roo.data.XmlReader({
25190    totalRecords: "results", // The element which contains the total dataset size (optional)
25191    record: "row",           // The repeated element which contains row information
25192    id: "id"                 // The element within the row that provides an ID for the record (optional)
25193 }, RecordDef);
25194 </code></pre>
25195  * <p>
25196  * This would consume an XML file like this:
25197  * <pre><code>
25198 &lt;?xml?>
25199 &lt;dataset>
25200  &lt;results>2&lt;/results>
25201  &lt;row>
25202    &lt;id>1&lt;/id>
25203    &lt;name>Bill&lt;/name>
25204    &lt;occupation>Gardener&lt;/occupation>
25205  &lt;/row>
25206  &lt;row>
25207    &lt;id>2&lt;/id>
25208    &lt;name>Ben&lt;/name>
25209    &lt;occupation>Horticulturalist&lt;/occupation>
25210  &lt;/row>
25211 &lt;/dataset>
25212 </code></pre>
25213  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25214  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25215  * paged from the remote server.
25216  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25217  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25218  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25219  * a record identifier value.
25220  * @constructor
25221  * Create a new XmlReader
25222  * @param {Object} meta Metadata configuration options
25223  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25224  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25225  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25226  */
25227 Roo.data.XmlReader = function(meta, recordType){
25228     meta = meta || {};
25229     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25230 };
25231 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25232     
25233     readerType : 'Xml',
25234     
25235     /**
25236      * This method is only used by a DataProxy which has retrieved data from a remote server.
25237          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25238          * to contain a method called 'responseXML' that returns an XML document object.
25239      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25240      * a cache of Roo.data.Records.
25241      */
25242     read : function(response){
25243         var doc = response.responseXML;
25244         if(!doc) {
25245             throw {message: "XmlReader.read: XML Document not available"};
25246         }
25247         return this.readRecords(doc);
25248     },
25249
25250     /**
25251      * Create a data block containing Roo.data.Records from an XML document.
25252          * @param {Object} doc A parsed XML document.
25253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25254      * a cache of Roo.data.Records.
25255      */
25256     readRecords : function(doc){
25257         /**
25258          * After any data loads/reads, the raw XML Document is available for further custom processing.
25259          * @type XMLDocument
25260          */
25261         this.xmlData = doc;
25262         var root = doc.documentElement || doc;
25263         var q = Roo.DomQuery;
25264         var recordType = this.recordType, fields = recordType.prototype.fields;
25265         var sid = this.meta.id;
25266         var totalRecords = 0, success = true;
25267         if(this.meta.totalRecords){
25268             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25269         }
25270         
25271         if(this.meta.success){
25272             var sv = q.selectValue(this.meta.success, root, true);
25273             success = sv !== false && sv !== 'false';
25274         }
25275         var records = [];
25276         var ns = q.select(this.meta.record, root);
25277         for(var i = 0, len = ns.length; i < len; i++) {
25278                 var n = ns[i];
25279                 var values = {};
25280                 var id = sid ? q.selectValue(sid, n) : undefined;
25281                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25282                     var f = fields.items[j];
25283                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25284                     v = f.convert(v);
25285                     values[f.name] = v;
25286                 }
25287                 var record = new recordType(values, id);
25288                 record.node = n;
25289                 records[records.length] = record;
25290             }
25291
25292             return {
25293                 success : success,
25294                 records : records,
25295                 totalRecords : totalRecords || records.length
25296             };
25297     }
25298 });/*
25299  * Based on:
25300  * Ext JS Library 1.1.1
25301  * Copyright(c) 2006-2007, Ext JS, LLC.
25302  *
25303  * Originally Released Under LGPL - original licence link has changed is not relivant.
25304  *
25305  * Fork - LGPL
25306  * <script type="text/javascript">
25307  */
25308
25309 /**
25310  * @class Roo.data.ArrayReader
25311  * @extends Roo.data.DataReader
25312  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25313  * Each element of that Array represents a row of data fields. The
25314  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25315  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25316  * <p>
25317  * Example code:.
25318  * <pre><code>
25319 var RecordDef = Roo.data.Record.create([
25320     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25321     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25322 ]);
25323 var myReader = new Roo.data.ArrayReader({
25324     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25325 }, RecordDef);
25326 </code></pre>
25327  * <p>
25328  * This would consume an Array like this:
25329  * <pre><code>
25330 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25331   </code></pre>
25332  
25333  * @constructor
25334  * Create a new JsonReader
25335  * @param {Object} meta Metadata configuration options.
25336  * @param {Object|Array} recordType Either an Array of field definition objects
25337  * 
25338  * @cfg {Array} fields Array of field definition objects
25339  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25340  * as specified to {@link Roo.data.Record#create},
25341  * or an {@link Roo.data.Record} object
25342  *
25343  * 
25344  * created using {@link Roo.data.Record#create}.
25345  */
25346 Roo.data.ArrayReader = function(meta, recordType)
25347 {    
25348     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25349 };
25350
25351 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25352     
25353       /**
25354      * Create a data block containing Roo.data.Records from an XML document.
25355      * @param {Object} o An Array of row objects which represents the dataset.
25356      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25357      * a cache of Roo.data.Records.
25358      */
25359     readRecords : function(o)
25360     {
25361         var sid = this.meta ? this.meta.id : null;
25362         var recordType = this.recordType, fields = recordType.prototype.fields;
25363         var records = [];
25364         var root = o;
25365         for(var i = 0; i < root.length; i++){
25366             var n = root[i];
25367             var values = {};
25368             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25369             for(var j = 0, jlen = fields.length; j < jlen; j++){
25370                 var f = fields.items[j];
25371                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25372                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25373                 v = f.convert(v);
25374                 values[f.name] = v;
25375             }
25376             var record = new recordType(values, id);
25377             record.json = n;
25378             records[records.length] = record;
25379         }
25380         return {
25381             records : records,
25382             totalRecords : records.length
25383         };
25384     },
25385     // used when loading children.. @see loadDataFromChildren
25386     toLoadData: function(rec)
25387     {
25388         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25389         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25390         
25391     }
25392     
25393     
25394 });/*
25395  * Based on:
25396  * Ext JS Library 1.1.1
25397  * Copyright(c) 2006-2007, Ext JS, LLC.
25398  *
25399  * Originally Released Under LGPL - original licence link has changed is not relivant.
25400  *
25401  * Fork - LGPL
25402  * <script type="text/javascript">
25403  */
25404
25405
25406 /**
25407  * @class Roo.data.Tree
25408  * @extends Roo.util.Observable
25409  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25410  * in the tree have most standard DOM functionality.
25411  * @constructor
25412  * @param {Node} root (optional) The root node
25413  */
25414 Roo.data.Tree = function(root){
25415    this.nodeHash = {};
25416    /**
25417     * The root node for this tree
25418     * @type Node
25419     */
25420    this.root = null;
25421    if(root){
25422        this.setRootNode(root);
25423    }
25424    this.addEvents({
25425        /**
25426         * @event append
25427         * Fires when a new child node is appended to a node in this tree.
25428         * @param {Tree} tree The owner tree
25429         * @param {Node} parent The parent node
25430         * @param {Node} node The newly appended node
25431         * @param {Number} index The index of the newly appended node
25432         */
25433        "append" : true,
25434        /**
25435         * @event remove
25436         * Fires when a child node is removed from a node in this tree.
25437         * @param {Tree} tree The owner tree
25438         * @param {Node} parent The parent node
25439         * @param {Node} node The child node removed
25440         */
25441        "remove" : true,
25442        /**
25443         * @event move
25444         * Fires when a node is moved to a new location in the tree
25445         * @param {Tree} tree The owner tree
25446         * @param {Node} node The node moved
25447         * @param {Node} oldParent The old parent of this node
25448         * @param {Node} newParent The new parent of this node
25449         * @param {Number} index The index it was moved to
25450         */
25451        "move" : true,
25452        /**
25453         * @event insert
25454         * Fires when a new child node is inserted in a node in this tree.
25455         * @param {Tree} tree The owner tree
25456         * @param {Node} parent The parent node
25457         * @param {Node} node The child node inserted
25458         * @param {Node} refNode The child node the node was inserted before
25459         */
25460        "insert" : true,
25461        /**
25462         * @event beforeappend
25463         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25464         * @param {Tree} tree The owner tree
25465         * @param {Node} parent The parent node
25466         * @param {Node} node The child node to be appended
25467         */
25468        "beforeappend" : true,
25469        /**
25470         * @event beforeremove
25471         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25472         * @param {Tree} tree The owner tree
25473         * @param {Node} parent The parent node
25474         * @param {Node} node The child node to be removed
25475         */
25476        "beforeremove" : true,
25477        /**
25478         * @event beforemove
25479         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25480         * @param {Tree} tree The owner tree
25481         * @param {Node} node The node being moved
25482         * @param {Node} oldParent The parent of the node
25483         * @param {Node} newParent The new parent the node is moving to
25484         * @param {Number} index The index it is being moved to
25485         */
25486        "beforemove" : true,
25487        /**
25488         * @event beforeinsert
25489         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25490         * @param {Tree} tree The owner tree
25491         * @param {Node} parent The parent node
25492         * @param {Node} node The child node to be inserted
25493         * @param {Node} refNode The child node the node is being inserted before
25494         */
25495        "beforeinsert" : true
25496    });
25497
25498     Roo.data.Tree.superclass.constructor.call(this);
25499 };
25500
25501 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25502     pathSeparator: "/",
25503
25504     proxyNodeEvent : function(){
25505         return this.fireEvent.apply(this, arguments);
25506     },
25507
25508     /**
25509      * Returns the root node for this tree.
25510      * @return {Node}
25511      */
25512     getRootNode : function(){
25513         return this.root;
25514     },
25515
25516     /**
25517      * Sets the root node for this tree.
25518      * @param {Node} node
25519      * @return {Node}
25520      */
25521     setRootNode : function(node){
25522         this.root = node;
25523         node.ownerTree = this;
25524         node.isRoot = true;
25525         this.registerNode(node);
25526         return node;
25527     },
25528
25529     /**
25530      * Gets a node in this tree by its id.
25531      * @param {String} id
25532      * @return {Node}
25533      */
25534     getNodeById : function(id){
25535         return this.nodeHash[id];
25536     },
25537
25538     registerNode : function(node){
25539         this.nodeHash[node.id] = node;
25540     },
25541
25542     unregisterNode : function(node){
25543         delete this.nodeHash[node.id];
25544     },
25545
25546     toString : function(){
25547         return "[Tree"+(this.id?" "+this.id:"")+"]";
25548     }
25549 });
25550
25551 /**
25552  * @class Roo.data.Node
25553  * @extends Roo.util.Observable
25554  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25555  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25556  * @constructor
25557  * @param {Object} attributes The attributes/config for the node
25558  */
25559 Roo.data.Node = function(attributes){
25560     /**
25561      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25562      * @type {Object}
25563      */
25564     this.attributes = attributes || {};
25565     this.leaf = this.attributes.leaf;
25566     /**
25567      * The node id. @type String
25568      */
25569     this.id = this.attributes.id;
25570     if(!this.id){
25571         this.id = Roo.id(null, "ynode-");
25572         this.attributes.id = this.id;
25573     }
25574      
25575     
25576     /**
25577      * All child nodes of this node. @type Array
25578      */
25579     this.childNodes = [];
25580     if(!this.childNodes.indexOf){ // indexOf is a must
25581         this.childNodes.indexOf = function(o){
25582             for(var i = 0, len = this.length; i < len; i++){
25583                 if(this[i] == o) {
25584                     return i;
25585                 }
25586             }
25587             return -1;
25588         };
25589     }
25590     /**
25591      * The parent node for this node. @type Node
25592      */
25593     this.parentNode = null;
25594     /**
25595      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25596      */
25597     this.firstChild = null;
25598     /**
25599      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25600      */
25601     this.lastChild = null;
25602     /**
25603      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25604      */
25605     this.previousSibling = null;
25606     /**
25607      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25608      */
25609     this.nextSibling = null;
25610
25611     this.addEvents({
25612        /**
25613         * @event append
25614         * Fires when a new child node is appended
25615         * @param {Tree} tree The owner tree
25616         * @param {Node} this This node
25617         * @param {Node} node The newly appended node
25618         * @param {Number} index The index of the newly appended node
25619         */
25620        "append" : true,
25621        /**
25622         * @event remove
25623         * Fires when a child node is removed
25624         * @param {Tree} tree The owner tree
25625         * @param {Node} this This node
25626         * @param {Node} node The removed node
25627         */
25628        "remove" : true,
25629        /**
25630         * @event move
25631         * Fires when this node is moved to a new location in the tree
25632         * @param {Tree} tree The owner tree
25633         * @param {Node} this This node
25634         * @param {Node} oldParent The old parent of this node
25635         * @param {Node} newParent The new parent of this node
25636         * @param {Number} index The index it was moved to
25637         */
25638        "move" : true,
25639        /**
25640         * @event insert
25641         * Fires when a new child node is inserted.
25642         * @param {Tree} tree The owner tree
25643         * @param {Node} this This node
25644         * @param {Node} node The child node inserted
25645         * @param {Node} refNode The child node the node was inserted before
25646         */
25647        "insert" : true,
25648        /**
25649         * @event beforeappend
25650         * Fires before a new child is appended, return false to cancel the append.
25651         * @param {Tree} tree The owner tree
25652         * @param {Node} this This node
25653         * @param {Node} node The child node to be appended
25654         */
25655        "beforeappend" : true,
25656        /**
25657         * @event beforeremove
25658         * Fires before a child is removed, return false to cancel the remove.
25659         * @param {Tree} tree The owner tree
25660         * @param {Node} this This node
25661         * @param {Node} node The child node to be removed
25662         */
25663        "beforeremove" : true,
25664        /**
25665         * @event beforemove
25666         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25667         * @param {Tree} tree The owner tree
25668         * @param {Node} this This node
25669         * @param {Node} oldParent The parent of this node
25670         * @param {Node} newParent The new parent this node is moving to
25671         * @param {Number} index The index it is being moved to
25672         */
25673        "beforemove" : true,
25674        /**
25675         * @event beforeinsert
25676         * Fires before a new child is inserted, return false to cancel the insert.
25677         * @param {Tree} tree The owner tree
25678         * @param {Node} this This node
25679         * @param {Node} node The child node to be inserted
25680         * @param {Node} refNode The child node the node is being inserted before
25681         */
25682        "beforeinsert" : true
25683    });
25684     this.listeners = this.attributes.listeners;
25685     Roo.data.Node.superclass.constructor.call(this);
25686 };
25687
25688 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25689     fireEvent : function(evtName){
25690         // first do standard event for this node
25691         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25692             return false;
25693         }
25694         // then bubble it up to the tree if the event wasn't cancelled
25695         var ot = this.getOwnerTree();
25696         if(ot){
25697             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25698                 return false;
25699             }
25700         }
25701         return true;
25702     },
25703
25704     /**
25705      * Returns true if this node is a leaf
25706      * @return {Boolean}
25707      */
25708     isLeaf : function(){
25709         return this.leaf === true;
25710     },
25711
25712     // private
25713     setFirstChild : function(node){
25714         this.firstChild = node;
25715     },
25716
25717     //private
25718     setLastChild : function(node){
25719         this.lastChild = node;
25720     },
25721
25722
25723     /**
25724      * Returns true if this node is the last child of its parent
25725      * @return {Boolean}
25726      */
25727     isLast : function(){
25728        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25729     },
25730
25731     /**
25732      * Returns true if this node is the first child of its parent
25733      * @return {Boolean}
25734      */
25735     isFirst : function(){
25736        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25737     },
25738
25739     hasChildNodes : function(){
25740         return !this.isLeaf() && this.childNodes.length > 0;
25741     },
25742
25743     /**
25744      * Insert node(s) as the last child node of this node.
25745      * @param {Node/Array} node The node or Array of nodes to append
25746      * @return {Node} The appended node if single append, or null if an array was passed
25747      */
25748     appendChild : function(node){
25749         var multi = false;
25750         if(node instanceof Array){
25751             multi = node;
25752         }else if(arguments.length > 1){
25753             multi = arguments;
25754         }
25755         
25756         // if passed an array or multiple args do them one by one
25757         if(multi){
25758             for(var i = 0, len = multi.length; i < len; i++) {
25759                 this.appendChild(multi[i]);
25760             }
25761         }else{
25762             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25763                 return false;
25764             }
25765             var index = this.childNodes.length;
25766             var oldParent = node.parentNode;
25767             // it's a move, make sure we move it cleanly
25768             if(oldParent){
25769                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25770                     return false;
25771                 }
25772                 oldParent.removeChild(node);
25773             }
25774             
25775             index = this.childNodes.length;
25776             if(index == 0){
25777                 this.setFirstChild(node);
25778             }
25779             this.childNodes.push(node);
25780             node.parentNode = this;
25781             var ps = this.childNodes[index-1];
25782             if(ps){
25783                 node.previousSibling = ps;
25784                 ps.nextSibling = node;
25785             }else{
25786                 node.previousSibling = null;
25787             }
25788             node.nextSibling = null;
25789             this.setLastChild(node);
25790             node.setOwnerTree(this.getOwnerTree());
25791             this.fireEvent("append", this.ownerTree, this, node, index);
25792             if(this.ownerTree) {
25793                 this.ownerTree.fireEvent("appendnode", this, node, index);
25794             }
25795             if(oldParent){
25796                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25797             }
25798             return node;
25799         }
25800     },
25801
25802     /**
25803      * Removes a child node from this node.
25804      * @param {Node} node The node to remove
25805      * @return {Node} The removed node
25806      */
25807     removeChild : function(node){
25808         var index = this.childNodes.indexOf(node);
25809         if(index == -1){
25810             return false;
25811         }
25812         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25813             return false;
25814         }
25815
25816         // remove it from childNodes collection
25817         this.childNodes.splice(index, 1);
25818
25819         // update siblings
25820         if(node.previousSibling){
25821             node.previousSibling.nextSibling = node.nextSibling;
25822         }
25823         if(node.nextSibling){
25824             node.nextSibling.previousSibling = node.previousSibling;
25825         }
25826
25827         // update child refs
25828         if(this.firstChild == node){
25829             this.setFirstChild(node.nextSibling);
25830         }
25831         if(this.lastChild == node){
25832             this.setLastChild(node.previousSibling);
25833         }
25834
25835         node.setOwnerTree(null);
25836         // clear any references from the node
25837         node.parentNode = null;
25838         node.previousSibling = null;
25839         node.nextSibling = null;
25840         this.fireEvent("remove", this.ownerTree, this, node);
25841         return node;
25842     },
25843
25844     /**
25845      * Inserts the first node before the second node in this nodes childNodes collection.
25846      * @param {Node} node The node to insert
25847      * @param {Node} refNode The node to insert before (if null the node is appended)
25848      * @return {Node} The inserted node
25849      */
25850     insertBefore : function(node, refNode){
25851         if(!refNode){ // like standard Dom, refNode can be null for append
25852             return this.appendChild(node);
25853         }
25854         // nothing to do
25855         if(node == refNode){
25856             return false;
25857         }
25858
25859         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25860             return false;
25861         }
25862         var index = this.childNodes.indexOf(refNode);
25863         var oldParent = node.parentNode;
25864         var refIndex = index;
25865
25866         // when moving internally, indexes will change after remove
25867         if(oldParent == this && this.childNodes.indexOf(node) < index){
25868             refIndex--;
25869         }
25870
25871         // it's a move, make sure we move it cleanly
25872         if(oldParent){
25873             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25874                 return false;
25875             }
25876             oldParent.removeChild(node);
25877         }
25878         if(refIndex == 0){
25879             this.setFirstChild(node);
25880         }
25881         this.childNodes.splice(refIndex, 0, node);
25882         node.parentNode = this;
25883         var ps = this.childNodes[refIndex-1];
25884         if(ps){
25885             node.previousSibling = ps;
25886             ps.nextSibling = node;
25887         }else{
25888             node.previousSibling = null;
25889         }
25890         node.nextSibling = refNode;
25891         refNode.previousSibling = node;
25892         node.setOwnerTree(this.getOwnerTree());
25893         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25894         if(oldParent){
25895             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25896         }
25897         return node;
25898     },
25899
25900     /**
25901      * Returns the child node at the specified index.
25902      * @param {Number} index
25903      * @return {Node}
25904      */
25905     item : function(index){
25906         return this.childNodes[index];
25907     },
25908
25909     /**
25910      * Replaces one child node in this node with another.
25911      * @param {Node} newChild The replacement node
25912      * @param {Node} oldChild The node to replace
25913      * @return {Node} The replaced node
25914      */
25915     replaceChild : function(newChild, oldChild){
25916         this.insertBefore(newChild, oldChild);
25917         this.removeChild(oldChild);
25918         return oldChild;
25919     },
25920
25921     /**
25922      * Returns the index of a child node
25923      * @param {Node} node
25924      * @return {Number} The index of the node or -1 if it was not found
25925      */
25926     indexOf : function(child){
25927         return this.childNodes.indexOf(child);
25928     },
25929
25930     /**
25931      * Returns the tree this node is in.
25932      * @return {Tree}
25933      */
25934     getOwnerTree : function(){
25935         // if it doesn't have one, look for one
25936         if(!this.ownerTree){
25937             var p = this;
25938             while(p){
25939                 if(p.ownerTree){
25940                     this.ownerTree = p.ownerTree;
25941                     break;
25942                 }
25943                 p = p.parentNode;
25944             }
25945         }
25946         return this.ownerTree;
25947     },
25948
25949     /**
25950      * Returns depth of this node (the root node has a depth of 0)
25951      * @return {Number}
25952      */
25953     getDepth : function(){
25954         var depth = 0;
25955         var p = this;
25956         while(p.parentNode){
25957             ++depth;
25958             p = p.parentNode;
25959         }
25960         return depth;
25961     },
25962
25963     // private
25964     setOwnerTree : function(tree){
25965         // if it's move, we need to update everyone
25966         if(tree != this.ownerTree){
25967             if(this.ownerTree){
25968                 this.ownerTree.unregisterNode(this);
25969             }
25970             this.ownerTree = tree;
25971             var cs = this.childNodes;
25972             for(var i = 0, len = cs.length; i < len; i++) {
25973                 cs[i].setOwnerTree(tree);
25974             }
25975             if(tree){
25976                 tree.registerNode(this);
25977             }
25978         }
25979     },
25980
25981     /**
25982      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25983      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25984      * @return {String} The path
25985      */
25986     getPath : function(attr){
25987         attr = attr || "id";
25988         var p = this.parentNode;
25989         var b = [this.attributes[attr]];
25990         while(p){
25991             b.unshift(p.attributes[attr]);
25992             p = p.parentNode;
25993         }
25994         var sep = this.getOwnerTree().pathSeparator;
25995         return sep + b.join(sep);
25996     },
25997
25998     /**
25999      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26000      * function call will be the scope provided or the current node. The arguments to the function
26001      * will be the args provided or the current node. If the function returns false at any point,
26002      * the bubble is stopped.
26003      * @param {Function} fn The function to call
26004      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26005      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26006      */
26007     bubble : function(fn, scope, args){
26008         var p = this;
26009         while(p){
26010             if(fn.call(scope || p, args || p) === false){
26011                 break;
26012             }
26013             p = p.parentNode;
26014         }
26015     },
26016
26017     /**
26018      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26019      * function call will be the scope provided or the current node. The arguments to the function
26020      * will be the args provided or the current node. If the function returns false at any point,
26021      * the cascade is stopped on that branch.
26022      * @param {Function} fn The function to call
26023      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26024      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26025      */
26026     cascade : function(fn, scope, args){
26027         if(fn.call(scope || this, args || this) !== false){
26028             var cs = this.childNodes;
26029             for(var i = 0, len = cs.length; i < len; i++) {
26030                 cs[i].cascade(fn, scope, args);
26031             }
26032         }
26033     },
26034
26035     /**
26036      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26037      * function call will be the scope provided or the current node. The arguments to the function
26038      * will be the args provided or the current node. If the function returns false at any point,
26039      * the iteration stops.
26040      * @param {Function} fn The function to call
26041      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26042      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26043      */
26044     eachChild : function(fn, scope, args){
26045         var cs = this.childNodes;
26046         for(var i = 0, len = cs.length; i < len; i++) {
26047                 if(fn.call(scope || this, args || cs[i]) === false){
26048                     break;
26049                 }
26050         }
26051     },
26052
26053     /**
26054      * Finds the first child that has the attribute with the specified value.
26055      * @param {String} attribute The attribute name
26056      * @param {Mixed} value The value to search for
26057      * @return {Node} The found child or null if none was found
26058      */
26059     findChild : function(attribute, value){
26060         var cs = this.childNodes;
26061         for(var i = 0, len = cs.length; i < len; i++) {
26062                 if(cs[i].attributes[attribute] == value){
26063                     return cs[i];
26064                 }
26065         }
26066         return null;
26067     },
26068
26069     /**
26070      * Finds the first child by a custom function. The child matches if the function passed
26071      * returns true.
26072      * @param {Function} fn
26073      * @param {Object} scope (optional)
26074      * @return {Node} The found child or null if none was found
26075      */
26076     findChildBy : function(fn, scope){
26077         var cs = this.childNodes;
26078         for(var i = 0, len = cs.length; i < len; i++) {
26079                 if(fn.call(scope||cs[i], cs[i]) === true){
26080                     return cs[i];
26081                 }
26082         }
26083         return null;
26084     },
26085
26086     /**
26087      * Sorts this nodes children using the supplied sort function
26088      * @param {Function} fn
26089      * @param {Object} scope (optional)
26090      */
26091     sort : function(fn, scope){
26092         var cs = this.childNodes;
26093         var len = cs.length;
26094         if(len > 0){
26095             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26096             cs.sort(sortFn);
26097             for(var i = 0; i < len; i++){
26098                 var n = cs[i];
26099                 n.previousSibling = cs[i-1];
26100                 n.nextSibling = cs[i+1];
26101                 if(i == 0){
26102                     this.setFirstChild(n);
26103                 }
26104                 if(i == len-1){
26105                     this.setLastChild(n);
26106                 }
26107             }
26108         }
26109     },
26110
26111     /**
26112      * Returns true if this node is an ancestor (at any point) of the passed node.
26113      * @param {Node} node
26114      * @return {Boolean}
26115      */
26116     contains : function(node){
26117         return node.isAncestor(this);
26118     },
26119
26120     /**
26121      * Returns true if the passed node is an ancestor (at any point) of this node.
26122      * @param {Node} node
26123      * @return {Boolean}
26124      */
26125     isAncestor : function(node){
26126         var p = this.parentNode;
26127         while(p){
26128             if(p == node){
26129                 return true;
26130             }
26131             p = p.parentNode;
26132         }
26133         return false;
26134     },
26135
26136     toString : function(){
26137         return "[Node"+(this.id?" "+this.id:"")+"]";
26138     }
26139 });/*
26140  * Based on:
26141  * Ext JS Library 1.1.1
26142  * Copyright(c) 2006-2007, Ext JS, LLC.
26143  *
26144  * Originally Released Under LGPL - original licence link has changed is not relivant.
26145  *
26146  * Fork - LGPL
26147  * <script type="text/javascript">
26148  */
26149
26150
26151 /**
26152  * @class Roo.Shadow
26153  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26154  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26155  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26156  * @constructor
26157  * Create a new Shadow
26158  * @param {Object} config The config object
26159  */
26160 Roo.Shadow = function(config){
26161     Roo.apply(this, config);
26162     if(typeof this.mode != "string"){
26163         this.mode = this.defaultMode;
26164     }
26165     var o = this.offset, a = {h: 0};
26166     var rad = Math.floor(this.offset/2);
26167     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26168         case "drop":
26169             a.w = 0;
26170             a.l = a.t = o;
26171             a.t -= 1;
26172             if(Roo.isIE){
26173                 a.l -= this.offset + rad;
26174                 a.t -= this.offset + rad;
26175                 a.w -= rad;
26176                 a.h -= rad;
26177                 a.t += 1;
26178             }
26179         break;
26180         case "sides":
26181             a.w = (o*2);
26182             a.l = -o;
26183             a.t = o-1;
26184             if(Roo.isIE){
26185                 a.l -= (this.offset - rad);
26186                 a.t -= this.offset + rad;
26187                 a.l += 1;
26188                 a.w -= (this.offset - rad)*2;
26189                 a.w -= rad + 1;
26190                 a.h -= 1;
26191             }
26192         break;
26193         case "frame":
26194             a.w = a.h = (o*2);
26195             a.l = a.t = -o;
26196             a.t += 1;
26197             a.h -= 2;
26198             if(Roo.isIE){
26199                 a.l -= (this.offset - rad);
26200                 a.t -= (this.offset - rad);
26201                 a.l += 1;
26202                 a.w -= (this.offset + rad + 1);
26203                 a.h -= (this.offset + rad);
26204                 a.h += 1;
26205             }
26206         break;
26207     };
26208
26209     this.adjusts = a;
26210 };
26211
26212 Roo.Shadow.prototype = {
26213     /**
26214      * @cfg {String} mode
26215      * The shadow display mode.  Supports the following options:<br />
26216      * sides: Shadow displays on both sides and bottom only<br />
26217      * frame: Shadow displays equally on all four sides<br />
26218      * drop: Traditional bottom-right drop shadow (default)
26219      */
26220     mode: false,
26221     /**
26222      * @cfg {String} offset
26223      * The number of pixels to offset the shadow from the element (defaults to 4)
26224      */
26225     offset: 4,
26226
26227     // private
26228     defaultMode: "drop",
26229
26230     /**
26231      * Displays the shadow under the target element
26232      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26233      */
26234     show : function(target){
26235         target = Roo.get(target);
26236         if(!this.el){
26237             this.el = Roo.Shadow.Pool.pull();
26238             if(this.el.dom.nextSibling != target.dom){
26239                 this.el.insertBefore(target);
26240             }
26241         }
26242         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26243         if(Roo.isIE){
26244             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26245         }
26246         this.realign(
26247             target.getLeft(true),
26248             target.getTop(true),
26249             target.getWidth(),
26250             target.getHeight()
26251         );
26252         this.el.dom.style.display = "block";
26253     },
26254
26255     /**
26256      * Returns true if the shadow is visible, else false
26257      */
26258     isVisible : function(){
26259         return this.el ? true : false;  
26260     },
26261
26262     /**
26263      * Direct alignment when values are already available. Show must be called at least once before
26264      * calling this method to ensure it is initialized.
26265      * @param {Number} left The target element left position
26266      * @param {Number} top The target element top position
26267      * @param {Number} width The target element width
26268      * @param {Number} height The target element height
26269      */
26270     realign : function(l, t, w, h){
26271         if(!this.el){
26272             return;
26273         }
26274         var a = this.adjusts, d = this.el.dom, s = d.style;
26275         var iea = 0;
26276         s.left = (l+a.l)+"px";
26277         s.top = (t+a.t)+"px";
26278         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26279  
26280         if(s.width != sws || s.height != shs){
26281             s.width = sws;
26282             s.height = shs;
26283             if(!Roo.isIE){
26284                 var cn = d.childNodes;
26285                 var sww = Math.max(0, (sw-12))+"px";
26286                 cn[0].childNodes[1].style.width = sww;
26287                 cn[1].childNodes[1].style.width = sww;
26288                 cn[2].childNodes[1].style.width = sww;
26289                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26290             }
26291         }
26292     },
26293
26294     /**
26295      * Hides this shadow
26296      */
26297     hide : function(){
26298         if(this.el){
26299             this.el.dom.style.display = "none";
26300             Roo.Shadow.Pool.push(this.el);
26301             delete this.el;
26302         }
26303     },
26304
26305     /**
26306      * Adjust the z-index of this shadow
26307      * @param {Number} zindex The new z-index
26308      */
26309     setZIndex : function(z){
26310         this.zIndex = z;
26311         if(this.el){
26312             this.el.setStyle("z-index", z);
26313         }
26314     }
26315 };
26316
26317 // Private utility class that manages the internal Shadow cache
26318 Roo.Shadow.Pool = function(){
26319     var p = [];
26320     var markup = Roo.isIE ?
26321                  '<div class="x-ie-shadow"></div>' :
26322                  '<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>';
26323     return {
26324         pull : function(){
26325             var sh = p.shift();
26326             if(!sh){
26327                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26328                 sh.autoBoxAdjust = false;
26329             }
26330             return sh;
26331         },
26332
26333         push : function(sh){
26334             p.push(sh);
26335         }
26336     };
26337 }();/*
26338  * Based on:
26339  * Ext JS Library 1.1.1
26340  * Copyright(c) 2006-2007, Ext JS, LLC.
26341  *
26342  * Originally Released Under LGPL - original licence link has changed is not relivant.
26343  *
26344  * Fork - LGPL
26345  * <script type="text/javascript">
26346  */
26347
26348
26349 /**
26350  * @class Roo.SplitBar
26351  * @extends Roo.util.Observable
26352  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26353  * <br><br>
26354  * Usage:
26355  * <pre><code>
26356 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26357                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26358 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26359 split.minSize = 100;
26360 split.maxSize = 600;
26361 split.animate = true;
26362 split.on('moved', splitterMoved);
26363 </code></pre>
26364  * @constructor
26365  * Create a new SplitBar
26366  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26367  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26368  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26369  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26370                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26371                         position of the SplitBar).
26372  */
26373 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26374     
26375     /** @private */
26376     this.el = Roo.get(dragElement, true);
26377     this.el.dom.unselectable = "on";
26378     /** @private */
26379     this.resizingEl = Roo.get(resizingElement, true);
26380
26381     /**
26382      * @private
26383      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26384      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26385      * @type Number
26386      */
26387     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26388     
26389     /**
26390      * The minimum size of the resizing element. (Defaults to 0)
26391      * @type Number
26392      */
26393     this.minSize = 0;
26394     
26395     /**
26396      * The maximum size of the resizing element. (Defaults to 2000)
26397      * @type Number
26398      */
26399     this.maxSize = 2000;
26400     
26401     /**
26402      * Whether to animate the transition to the new size
26403      * @type Boolean
26404      */
26405     this.animate = false;
26406     
26407     /**
26408      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26409      * @type Boolean
26410      */
26411     this.useShim = false;
26412     
26413     /** @private */
26414     this.shim = null;
26415     
26416     if(!existingProxy){
26417         /** @private */
26418         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26419     }else{
26420         this.proxy = Roo.get(existingProxy).dom;
26421     }
26422     /** @private */
26423     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26424     
26425     /** @private */
26426     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26427     
26428     /** @private */
26429     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26430     
26431     /** @private */
26432     this.dragSpecs = {};
26433     
26434     /**
26435      * @private The adapter to use to positon and resize elements
26436      */
26437     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26438     this.adapter.init(this);
26439     
26440     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26441         /** @private */
26442         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26443         this.el.addClass("x-splitbar-h");
26444     }else{
26445         /** @private */
26446         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26447         this.el.addClass("x-splitbar-v");
26448     }
26449     
26450     this.addEvents({
26451         /**
26452          * @event resize
26453          * Fires when the splitter is moved (alias for {@link #event-moved})
26454          * @param {Roo.SplitBar} this
26455          * @param {Number} newSize the new width or height
26456          */
26457         "resize" : true,
26458         /**
26459          * @event moved
26460          * Fires when the splitter is moved
26461          * @param {Roo.SplitBar} this
26462          * @param {Number} newSize the new width or height
26463          */
26464         "moved" : true,
26465         /**
26466          * @event beforeresize
26467          * Fires before the splitter is dragged
26468          * @param {Roo.SplitBar} this
26469          */
26470         "beforeresize" : true,
26471
26472         "beforeapply" : true
26473     });
26474
26475     Roo.util.Observable.call(this);
26476 };
26477
26478 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26479     onStartProxyDrag : function(x, y){
26480         this.fireEvent("beforeresize", this);
26481         if(!this.overlay){
26482             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26483             o.unselectable();
26484             o.enableDisplayMode("block");
26485             // all splitbars share the same overlay
26486             Roo.SplitBar.prototype.overlay = o;
26487         }
26488         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26489         this.overlay.show();
26490         Roo.get(this.proxy).setDisplayed("block");
26491         var size = this.adapter.getElementSize(this);
26492         this.activeMinSize = this.getMinimumSize();;
26493         this.activeMaxSize = this.getMaximumSize();;
26494         var c1 = size - this.activeMinSize;
26495         var c2 = Math.max(this.activeMaxSize - size, 0);
26496         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26497             this.dd.resetConstraints();
26498             this.dd.setXConstraint(
26499                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26500                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26501             );
26502             this.dd.setYConstraint(0, 0);
26503         }else{
26504             this.dd.resetConstraints();
26505             this.dd.setXConstraint(0, 0);
26506             this.dd.setYConstraint(
26507                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26508                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26509             );
26510          }
26511         this.dragSpecs.startSize = size;
26512         this.dragSpecs.startPoint = [x, y];
26513         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26514     },
26515     
26516     /** 
26517      * @private Called after the drag operation by the DDProxy
26518      */
26519     onEndProxyDrag : function(e){
26520         Roo.get(this.proxy).setDisplayed(false);
26521         var endPoint = Roo.lib.Event.getXY(e);
26522         if(this.overlay){
26523             this.overlay.hide();
26524         }
26525         var newSize;
26526         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26527             newSize = this.dragSpecs.startSize + 
26528                 (this.placement == Roo.SplitBar.LEFT ?
26529                     endPoint[0] - this.dragSpecs.startPoint[0] :
26530                     this.dragSpecs.startPoint[0] - endPoint[0]
26531                 );
26532         }else{
26533             newSize = this.dragSpecs.startSize + 
26534                 (this.placement == Roo.SplitBar.TOP ?
26535                     endPoint[1] - this.dragSpecs.startPoint[1] :
26536                     this.dragSpecs.startPoint[1] - endPoint[1]
26537                 );
26538         }
26539         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26540         if(newSize != this.dragSpecs.startSize){
26541             if(this.fireEvent('beforeapply', this, newSize) !== false){
26542                 this.adapter.setElementSize(this, newSize);
26543                 this.fireEvent("moved", this, newSize);
26544                 this.fireEvent("resize", this, newSize);
26545             }
26546         }
26547     },
26548     
26549     /**
26550      * Get the adapter this SplitBar uses
26551      * @return The adapter object
26552      */
26553     getAdapter : function(){
26554         return this.adapter;
26555     },
26556     
26557     /**
26558      * Set the adapter this SplitBar uses
26559      * @param {Object} adapter A SplitBar adapter object
26560      */
26561     setAdapter : function(adapter){
26562         this.adapter = adapter;
26563         this.adapter.init(this);
26564     },
26565     
26566     /**
26567      * Gets the minimum size for the resizing element
26568      * @return {Number} The minimum size
26569      */
26570     getMinimumSize : function(){
26571         return this.minSize;
26572     },
26573     
26574     /**
26575      * Sets the minimum size for the resizing element
26576      * @param {Number} minSize The minimum size
26577      */
26578     setMinimumSize : function(minSize){
26579         this.minSize = minSize;
26580     },
26581     
26582     /**
26583      * Gets the maximum size for the resizing element
26584      * @return {Number} The maximum size
26585      */
26586     getMaximumSize : function(){
26587         return this.maxSize;
26588     },
26589     
26590     /**
26591      * Sets the maximum size for the resizing element
26592      * @param {Number} maxSize The maximum size
26593      */
26594     setMaximumSize : function(maxSize){
26595         this.maxSize = maxSize;
26596     },
26597     
26598     /**
26599      * Sets the initialize size for the resizing element
26600      * @param {Number} size The initial size
26601      */
26602     setCurrentSize : function(size){
26603         var oldAnimate = this.animate;
26604         this.animate = false;
26605         this.adapter.setElementSize(this, size);
26606         this.animate = oldAnimate;
26607     },
26608     
26609     /**
26610      * Destroy this splitbar. 
26611      * @param {Boolean} removeEl True to remove the element
26612      */
26613     destroy : function(removeEl){
26614         if(this.shim){
26615             this.shim.remove();
26616         }
26617         this.dd.unreg();
26618         this.proxy.parentNode.removeChild(this.proxy);
26619         if(removeEl){
26620             this.el.remove();
26621         }
26622     }
26623 });
26624
26625 /**
26626  * @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.
26627  */
26628 Roo.SplitBar.createProxy = function(dir){
26629     var proxy = new Roo.Element(document.createElement("div"));
26630     proxy.unselectable();
26631     var cls = 'x-splitbar-proxy';
26632     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26633     document.body.appendChild(proxy.dom);
26634     return proxy.dom;
26635 };
26636
26637 /** 
26638  * @class Roo.SplitBar.BasicLayoutAdapter
26639  * Default Adapter. It assumes the splitter and resizing element are not positioned
26640  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26641  */
26642 Roo.SplitBar.BasicLayoutAdapter = function(){
26643 };
26644
26645 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26646     // do nothing for now
26647     init : function(s){
26648     
26649     },
26650     /**
26651      * Called before drag operations to get the current size of the resizing element. 
26652      * @param {Roo.SplitBar} s The SplitBar using this adapter
26653      */
26654      getElementSize : function(s){
26655         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26656             return s.resizingEl.getWidth();
26657         }else{
26658             return s.resizingEl.getHeight();
26659         }
26660     },
26661     
26662     /**
26663      * Called after drag operations to set the size of the resizing element.
26664      * @param {Roo.SplitBar} s The SplitBar using this adapter
26665      * @param {Number} newSize The new size to set
26666      * @param {Function} onComplete A function to be invoked when resizing is complete
26667      */
26668     setElementSize : function(s, newSize, onComplete){
26669         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26670             if(!s.animate){
26671                 s.resizingEl.setWidth(newSize);
26672                 if(onComplete){
26673                     onComplete(s, newSize);
26674                 }
26675             }else{
26676                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26677             }
26678         }else{
26679             
26680             if(!s.animate){
26681                 s.resizingEl.setHeight(newSize);
26682                 if(onComplete){
26683                     onComplete(s, newSize);
26684                 }
26685             }else{
26686                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26687             }
26688         }
26689     }
26690 };
26691
26692 /** 
26693  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26694  * @extends Roo.SplitBar.BasicLayoutAdapter
26695  * Adapter that  moves the splitter element to align with the resized sizing element. 
26696  * Used with an absolute positioned SplitBar.
26697  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26698  * document.body, make sure you assign an id to the body element.
26699  */
26700 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26701     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26702     this.container = Roo.get(container);
26703 };
26704
26705 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26706     init : function(s){
26707         this.basic.init(s);
26708     },
26709     
26710     getElementSize : function(s){
26711         return this.basic.getElementSize(s);
26712     },
26713     
26714     setElementSize : function(s, newSize, onComplete){
26715         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26716     },
26717     
26718     moveSplitter : function(s){
26719         var yes = Roo.SplitBar;
26720         switch(s.placement){
26721             case yes.LEFT:
26722                 s.el.setX(s.resizingEl.getRight());
26723                 break;
26724             case yes.RIGHT:
26725                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26726                 break;
26727             case yes.TOP:
26728                 s.el.setY(s.resizingEl.getBottom());
26729                 break;
26730             case yes.BOTTOM:
26731                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26732                 break;
26733         }
26734     }
26735 };
26736
26737 /**
26738  * Orientation constant - Create a vertical SplitBar
26739  * @static
26740  * @type Number
26741  */
26742 Roo.SplitBar.VERTICAL = 1;
26743
26744 /**
26745  * Orientation constant - Create a horizontal SplitBar
26746  * @static
26747  * @type Number
26748  */
26749 Roo.SplitBar.HORIZONTAL = 2;
26750
26751 /**
26752  * Placement constant - The resizing element is to the left of the splitter element
26753  * @static
26754  * @type Number
26755  */
26756 Roo.SplitBar.LEFT = 1;
26757
26758 /**
26759  * Placement constant - The resizing element is to the right of the splitter element
26760  * @static
26761  * @type Number
26762  */
26763 Roo.SplitBar.RIGHT = 2;
26764
26765 /**
26766  * Placement constant - The resizing element is positioned above the splitter element
26767  * @static
26768  * @type Number
26769  */
26770 Roo.SplitBar.TOP = 3;
26771
26772 /**
26773  * Placement constant - The resizing element is positioned under splitter element
26774  * @static
26775  * @type Number
26776  */
26777 Roo.SplitBar.BOTTOM = 4;
26778 /*
26779  * Based on:
26780  * Ext JS Library 1.1.1
26781  * Copyright(c) 2006-2007, Ext JS, LLC.
26782  *
26783  * Originally Released Under LGPL - original licence link has changed is not relivant.
26784  *
26785  * Fork - LGPL
26786  * <script type="text/javascript">
26787  */
26788
26789 /**
26790  * @class Roo.View
26791  * @extends Roo.util.Observable
26792  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26793  * This class also supports single and multi selection modes. <br>
26794  * Create a data model bound view:
26795  <pre><code>
26796  var store = new Roo.data.Store(...);
26797
26798  var view = new Roo.View({
26799     el : "my-element",
26800     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26801  
26802     singleSelect: true,
26803     selectedClass: "ydataview-selected",
26804     store: store
26805  });
26806
26807  // listen for node click?
26808  view.on("click", function(vw, index, node, e){
26809  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26810  });
26811
26812  // load XML data
26813  dataModel.load("foobar.xml");
26814  </code></pre>
26815  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26816  * <br><br>
26817  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26818  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26819  * 
26820  * Note: old style constructor is still suported (container, template, config)
26821  * 
26822  * @constructor
26823  * Create a new View
26824  * @param {Object} config The config object
26825  * 
26826  */
26827 Roo.View = function(config, depreciated_tpl, depreciated_config){
26828     
26829     this.parent = false;
26830     
26831     if (typeof(depreciated_tpl) == 'undefined') {
26832         // new way.. - universal constructor.
26833         Roo.apply(this, config);
26834         this.el  = Roo.get(this.el);
26835     } else {
26836         // old format..
26837         this.el  = Roo.get(config);
26838         this.tpl = depreciated_tpl;
26839         Roo.apply(this, depreciated_config);
26840     }
26841     this.wrapEl  = this.el.wrap().wrap();
26842     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26843     
26844     
26845     if(typeof(this.tpl) == "string"){
26846         this.tpl = new Roo.Template(this.tpl);
26847     } else {
26848         // support xtype ctors..
26849         this.tpl = new Roo.factory(this.tpl, Roo);
26850     }
26851     
26852     
26853     this.tpl.compile();
26854     
26855     /** @private */
26856     this.addEvents({
26857         /**
26858          * @event beforeclick
26859          * Fires before a click is processed. Returns false to cancel the default action.
26860          * @param {Roo.View} this
26861          * @param {Number} index The index of the target node
26862          * @param {HTMLElement} node The target node
26863          * @param {Roo.EventObject} e The raw event object
26864          */
26865             "beforeclick" : true,
26866         /**
26867          * @event click
26868          * Fires when a template node is clicked.
26869          * @param {Roo.View} this
26870          * @param {Number} index The index of the target node
26871          * @param {HTMLElement} node The target node
26872          * @param {Roo.EventObject} e The raw event object
26873          */
26874             "click" : true,
26875         /**
26876          * @event dblclick
26877          * Fires when a template node is double clicked.
26878          * @param {Roo.View} this
26879          * @param {Number} index The index of the target node
26880          * @param {HTMLElement} node The target node
26881          * @param {Roo.EventObject} e The raw event object
26882          */
26883             "dblclick" : true,
26884         /**
26885          * @event contextmenu
26886          * Fires when a template node is right clicked.
26887          * @param {Roo.View} this
26888          * @param {Number} index The index of the target node
26889          * @param {HTMLElement} node The target node
26890          * @param {Roo.EventObject} e The raw event object
26891          */
26892             "contextmenu" : true,
26893         /**
26894          * @event selectionchange
26895          * Fires when the selected nodes change.
26896          * @param {Roo.View} this
26897          * @param {Array} selections Array of the selected nodes
26898          */
26899             "selectionchange" : true,
26900     
26901         /**
26902          * @event beforeselect
26903          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26904          * @param {Roo.View} this
26905          * @param {HTMLElement} node The node to be selected
26906          * @param {Array} selections Array of currently selected nodes
26907          */
26908             "beforeselect" : true,
26909         /**
26910          * @event preparedata
26911          * Fires on every row to render, to allow you to change the data.
26912          * @param {Roo.View} this
26913          * @param {Object} data to be rendered (change this)
26914          */
26915           "preparedata" : true
26916           
26917           
26918         });
26919
26920
26921
26922     this.el.on({
26923         "click": this.onClick,
26924         "dblclick": this.onDblClick,
26925         "contextmenu": this.onContextMenu,
26926         scope:this
26927     });
26928
26929     this.selections = [];
26930     this.nodes = [];
26931     this.cmp = new Roo.CompositeElementLite([]);
26932     if(this.store){
26933         this.store = Roo.factory(this.store, Roo.data);
26934         this.setStore(this.store, true);
26935     }
26936     
26937     if ( this.footer && this.footer.xtype) {
26938            
26939          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26940         
26941         this.footer.dataSource = this.store;
26942         this.footer.container = fctr;
26943         this.footer = Roo.factory(this.footer, Roo);
26944         fctr.insertFirst(this.el);
26945         
26946         // this is a bit insane - as the paging toolbar seems to detach the el..
26947 //        dom.parentNode.parentNode.parentNode
26948          // they get detached?
26949     }
26950     
26951     
26952     Roo.View.superclass.constructor.call(this);
26953     
26954     
26955 };
26956
26957 Roo.extend(Roo.View, Roo.util.Observable, {
26958     
26959      /**
26960      * @cfg {Roo.data.Store} store Data store to load data from.
26961      */
26962     store : false,
26963     
26964     /**
26965      * @cfg {String|Roo.Element} el The container element.
26966      */
26967     el : '',
26968     
26969     /**
26970      * @cfg {String|Roo.Template} tpl The template used by this View 
26971      */
26972     tpl : false,
26973     /**
26974      * @cfg {String} dataName the named area of the template to use as the data area
26975      *                          Works with domtemplates roo-name="name"
26976      */
26977     dataName: false,
26978     /**
26979      * @cfg {String} selectedClass The css class to add to selected nodes
26980      */
26981     selectedClass : "x-view-selected",
26982      /**
26983      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26984      */
26985     emptyText : "",
26986     
26987     /**
26988      * @cfg {String} text to display on mask (default Loading)
26989      */
26990     mask : false,
26991     /**
26992      * @cfg {Boolean} multiSelect Allow multiple selection
26993      */
26994     multiSelect : false,
26995     /**
26996      * @cfg {Boolean} singleSelect Allow single selection
26997      */
26998     singleSelect:  false,
26999     
27000     /**
27001      * @cfg {Boolean} toggleSelect - selecting 
27002      */
27003     toggleSelect : false,
27004     
27005     /**
27006      * @cfg {Boolean} tickable - selecting 
27007      */
27008     tickable : false,
27009     
27010     /**
27011      * Returns the element this view is bound to.
27012      * @return {Roo.Element}
27013      */
27014     getEl : function(){
27015         return this.wrapEl;
27016     },
27017     
27018     
27019
27020     /**
27021      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27022      */
27023     refresh : function(){
27024         //Roo.log('refresh');
27025         var t = this.tpl;
27026         
27027         // if we are using something like 'domtemplate', then
27028         // the what gets used is:
27029         // t.applySubtemplate(NAME, data, wrapping data..)
27030         // the outer template then get' applied with
27031         //     the store 'extra data'
27032         // and the body get's added to the
27033         //      roo-name="data" node?
27034         //      <span class='roo-tpl-{name}'></span> ?????
27035         
27036         
27037         
27038         this.clearSelections();
27039         this.el.update("");
27040         var html = [];
27041         var records = this.store.getRange();
27042         if(records.length < 1) {
27043             
27044             // is this valid??  = should it render a template??
27045             
27046             this.el.update(this.emptyText);
27047             return;
27048         }
27049         var el = this.el;
27050         if (this.dataName) {
27051             this.el.update(t.apply(this.store.meta)); //????
27052             el = this.el.child('.roo-tpl-' + this.dataName);
27053         }
27054         
27055         for(var i = 0, len = records.length; i < len; i++){
27056             var data = this.prepareData(records[i].data, i, records[i]);
27057             this.fireEvent("preparedata", this, data, i, records[i]);
27058             
27059             var d = Roo.apply({}, data);
27060             
27061             if(this.tickable){
27062                 Roo.apply(d, {'roo-id' : Roo.id()});
27063                 
27064                 var _this = this;
27065             
27066                 Roo.each(this.parent.item, function(item){
27067                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27068                         return;
27069                     }
27070                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27071                 });
27072             }
27073             
27074             html[html.length] = Roo.util.Format.trim(
27075                 this.dataName ?
27076                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27077                     t.apply(d)
27078             );
27079         }
27080         
27081         
27082         
27083         el.update(html.join(""));
27084         this.nodes = el.dom.childNodes;
27085         this.updateIndexes(0);
27086     },
27087     
27088
27089     /**
27090      * Function to override to reformat the data that is sent to
27091      * the template for each node.
27092      * DEPRICATED - use the preparedata event handler.
27093      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27094      * a JSON object for an UpdateManager bound view).
27095      */
27096     prepareData : function(data, index, record)
27097     {
27098         this.fireEvent("preparedata", this, data, index, record);
27099         return data;
27100     },
27101
27102     onUpdate : function(ds, record){
27103         // Roo.log('on update');   
27104         this.clearSelections();
27105         var index = this.store.indexOf(record);
27106         var n = this.nodes[index];
27107         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27108         n.parentNode.removeChild(n);
27109         this.updateIndexes(index, index);
27110     },
27111
27112     
27113     
27114 // --------- FIXME     
27115     onAdd : function(ds, records, index)
27116     {
27117         //Roo.log(['on Add', ds, records, index] );        
27118         this.clearSelections();
27119         if(this.nodes.length == 0){
27120             this.refresh();
27121             return;
27122         }
27123         var n = this.nodes[index];
27124         for(var i = 0, len = records.length; i < len; i++){
27125             var d = this.prepareData(records[i].data, i, records[i]);
27126             if(n){
27127                 this.tpl.insertBefore(n, d);
27128             }else{
27129                 
27130                 this.tpl.append(this.el, d);
27131             }
27132         }
27133         this.updateIndexes(index);
27134     },
27135
27136     onRemove : function(ds, record, index){
27137        // Roo.log('onRemove');
27138         this.clearSelections();
27139         var el = this.dataName  ?
27140             this.el.child('.roo-tpl-' + this.dataName) :
27141             this.el; 
27142         
27143         el.dom.removeChild(this.nodes[index]);
27144         this.updateIndexes(index);
27145     },
27146
27147     /**
27148      * Refresh an individual node.
27149      * @param {Number} index
27150      */
27151     refreshNode : function(index){
27152         this.onUpdate(this.store, this.store.getAt(index));
27153     },
27154
27155     updateIndexes : function(startIndex, endIndex){
27156         var ns = this.nodes;
27157         startIndex = startIndex || 0;
27158         endIndex = endIndex || ns.length - 1;
27159         for(var i = startIndex; i <= endIndex; i++){
27160             ns[i].nodeIndex = i;
27161         }
27162     },
27163
27164     /**
27165      * Changes the data store this view uses and refresh the view.
27166      * @param {Store} store
27167      */
27168     setStore : function(store, initial){
27169         if(!initial && this.store){
27170             this.store.un("datachanged", this.refresh);
27171             this.store.un("add", this.onAdd);
27172             this.store.un("remove", this.onRemove);
27173             this.store.un("update", this.onUpdate);
27174             this.store.un("clear", this.refresh);
27175             this.store.un("beforeload", this.onBeforeLoad);
27176             this.store.un("load", this.onLoad);
27177             this.store.un("loadexception", this.onLoad);
27178         }
27179         if(store){
27180           
27181             store.on("datachanged", this.refresh, this);
27182             store.on("add", this.onAdd, this);
27183             store.on("remove", this.onRemove, this);
27184             store.on("update", this.onUpdate, this);
27185             store.on("clear", this.refresh, this);
27186             store.on("beforeload", this.onBeforeLoad, this);
27187             store.on("load", this.onLoad, this);
27188             store.on("loadexception", this.onLoad, this);
27189         }
27190         
27191         if(store){
27192             this.refresh();
27193         }
27194     },
27195     /**
27196      * onbeforeLoad - masks the loading area.
27197      *
27198      */
27199     onBeforeLoad : function(store,opts)
27200     {
27201          //Roo.log('onBeforeLoad');   
27202         if (!opts.add) {
27203             this.el.update("");
27204         }
27205         this.el.mask(this.mask ? this.mask : "Loading" ); 
27206     },
27207     onLoad : function ()
27208     {
27209         this.el.unmask();
27210     },
27211     
27212
27213     /**
27214      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27215      * @param {HTMLElement} node
27216      * @return {HTMLElement} The template node
27217      */
27218     findItemFromChild : function(node){
27219         var el = this.dataName  ?
27220             this.el.child('.roo-tpl-' + this.dataName,true) :
27221             this.el.dom; 
27222         
27223         if(!node || node.parentNode == el){
27224                     return node;
27225             }
27226             var p = node.parentNode;
27227             while(p && p != el){
27228             if(p.parentNode == el){
27229                 return p;
27230             }
27231             p = p.parentNode;
27232         }
27233             return null;
27234     },
27235
27236     /** @ignore */
27237     onClick : function(e){
27238         var item = this.findItemFromChild(e.getTarget());
27239         if(item){
27240             var index = this.indexOf(item);
27241             if(this.onItemClick(item, index, e) !== false){
27242                 this.fireEvent("click", this, index, item, e);
27243             }
27244         }else{
27245             this.clearSelections();
27246         }
27247     },
27248
27249     /** @ignore */
27250     onContextMenu : function(e){
27251         var item = this.findItemFromChild(e.getTarget());
27252         if(item){
27253             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27254         }
27255     },
27256
27257     /** @ignore */
27258     onDblClick : function(e){
27259         var item = this.findItemFromChild(e.getTarget());
27260         if(item){
27261             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27262         }
27263     },
27264
27265     onItemClick : function(item, index, e)
27266     {
27267         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27268             return false;
27269         }
27270         if (this.toggleSelect) {
27271             var m = this.isSelected(item) ? 'unselect' : 'select';
27272             //Roo.log(m);
27273             var _t = this;
27274             _t[m](item, true, false);
27275             return true;
27276         }
27277         if(this.multiSelect || this.singleSelect){
27278             if(this.multiSelect && e.shiftKey && this.lastSelection){
27279                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27280             }else{
27281                 this.select(item, this.multiSelect && e.ctrlKey);
27282                 this.lastSelection = item;
27283             }
27284             
27285             if(!this.tickable){
27286                 e.preventDefault();
27287             }
27288             
27289         }
27290         return true;
27291     },
27292
27293     /**
27294      * Get the number of selected nodes.
27295      * @return {Number}
27296      */
27297     getSelectionCount : function(){
27298         return this.selections.length;
27299     },
27300
27301     /**
27302      * Get the currently selected nodes.
27303      * @return {Array} An array of HTMLElements
27304      */
27305     getSelectedNodes : function(){
27306         return this.selections;
27307     },
27308
27309     /**
27310      * Get the indexes of the selected nodes.
27311      * @return {Array}
27312      */
27313     getSelectedIndexes : function(){
27314         var indexes = [], s = this.selections;
27315         for(var i = 0, len = s.length; i < len; i++){
27316             indexes.push(s[i].nodeIndex);
27317         }
27318         return indexes;
27319     },
27320
27321     /**
27322      * Clear all selections
27323      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27324      */
27325     clearSelections : function(suppressEvent){
27326         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27327             this.cmp.elements = this.selections;
27328             this.cmp.removeClass(this.selectedClass);
27329             this.selections = [];
27330             if(!suppressEvent){
27331                 this.fireEvent("selectionchange", this, this.selections);
27332             }
27333         }
27334     },
27335
27336     /**
27337      * Returns true if the passed node is selected
27338      * @param {HTMLElement/Number} node The node or node index
27339      * @return {Boolean}
27340      */
27341     isSelected : function(node){
27342         var s = this.selections;
27343         if(s.length < 1){
27344             return false;
27345         }
27346         node = this.getNode(node);
27347         return s.indexOf(node) !== -1;
27348     },
27349
27350     /**
27351      * Selects nodes.
27352      * @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
27353      * @param {Boolean} keepExisting (optional) true to keep existing selections
27354      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27355      */
27356     select : function(nodeInfo, keepExisting, suppressEvent){
27357         if(nodeInfo instanceof Array){
27358             if(!keepExisting){
27359                 this.clearSelections(true);
27360             }
27361             for(var i = 0, len = nodeInfo.length; i < len; i++){
27362                 this.select(nodeInfo[i], true, true);
27363             }
27364             return;
27365         } 
27366         var node = this.getNode(nodeInfo);
27367         if(!node || this.isSelected(node)){
27368             return; // already selected.
27369         }
27370         if(!keepExisting){
27371             this.clearSelections(true);
27372         }
27373         
27374         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27375             Roo.fly(node).addClass(this.selectedClass);
27376             this.selections.push(node);
27377             if(!suppressEvent){
27378                 this.fireEvent("selectionchange", this, this.selections);
27379             }
27380         }
27381         
27382         
27383     },
27384       /**
27385      * Unselects nodes.
27386      * @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
27387      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27388      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27389      */
27390     unselect : function(nodeInfo, keepExisting, suppressEvent)
27391     {
27392         if(nodeInfo instanceof Array){
27393             Roo.each(this.selections, function(s) {
27394                 this.unselect(s, nodeInfo);
27395             }, this);
27396             return;
27397         }
27398         var node = this.getNode(nodeInfo);
27399         if(!node || !this.isSelected(node)){
27400             //Roo.log("not selected");
27401             return; // not selected.
27402         }
27403         // fireevent???
27404         var ns = [];
27405         Roo.each(this.selections, function(s) {
27406             if (s == node ) {
27407                 Roo.fly(node).removeClass(this.selectedClass);
27408
27409                 return;
27410             }
27411             ns.push(s);
27412         },this);
27413         
27414         this.selections= ns;
27415         this.fireEvent("selectionchange", this, this.selections);
27416     },
27417
27418     /**
27419      * Gets a template node.
27420      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27421      * @return {HTMLElement} The node or null if it wasn't found
27422      */
27423     getNode : function(nodeInfo){
27424         if(typeof nodeInfo == "string"){
27425             return document.getElementById(nodeInfo);
27426         }else if(typeof nodeInfo == "number"){
27427             return this.nodes[nodeInfo];
27428         }
27429         return nodeInfo;
27430     },
27431
27432     /**
27433      * Gets a range template nodes.
27434      * @param {Number} startIndex
27435      * @param {Number} endIndex
27436      * @return {Array} An array of nodes
27437      */
27438     getNodes : function(start, end){
27439         var ns = this.nodes;
27440         start = start || 0;
27441         end = typeof end == "undefined" ? ns.length - 1 : end;
27442         var nodes = [];
27443         if(start <= end){
27444             for(var i = start; i <= end; i++){
27445                 nodes.push(ns[i]);
27446             }
27447         } else{
27448             for(var i = start; i >= end; i--){
27449                 nodes.push(ns[i]);
27450             }
27451         }
27452         return nodes;
27453     },
27454
27455     /**
27456      * Finds the index of the passed node
27457      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27458      * @return {Number} The index of the node or -1
27459      */
27460     indexOf : function(node){
27461         node = this.getNode(node);
27462         if(typeof node.nodeIndex == "number"){
27463             return node.nodeIndex;
27464         }
27465         var ns = this.nodes;
27466         for(var i = 0, len = ns.length; i < len; i++){
27467             if(ns[i] == node){
27468                 return i;
27469             }
27470         }
27471         return -1;
27472     }
27473 });
27474 /*
27475  * Based on:
27476  * Ext JS Library 1.1.1
27477  * Copyright(c) 2006-2007, Ext JS, LLC.
27478  *
27479  * Originally Released Under LGPL - original licence link has changed is not relivant.
27480  *
27481  * Fork - LGPL
27482  * <script type="text/javascript">
27483  */
27484
27485 /**
27486  * @class Roo.JsonView
27487  * @extends Roo.View
27488  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27489 <pre><code>
27490 var view = new Roo.JsonView({
27491     container: "my-element",
27492     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27493     multiSelect: true, 
27494     jsonRoot: "data" 
27495 });
27496
27497 // listen for node click?
27498 view.on("click", function(vw, index, node, e){
27499     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27500 });
27501
27502 // direct load of JSON data
27503 view.load("foobar.php");
27504
27505 // Example from my blog list
27506 var tpl = new Roo.Template(
27507     '&lt;div class="entry"&gt;' +
27508     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27509     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27510     "&lt;/div&gt;&lt;hr /&gt;"
27511 );
27512
27513 var moreView = new Roo.JsonView({
27514     container :  "entry-list", 
27515     template : tpl,
27516     jsonRoot: "posts"
27517 });
27518 moreView.on("beforerender", this.sortEntries, this);
27519 moreView.load({
27520     url: "/blog/get-posts.php",
27521     params: "allposts=true",
27522     text: "Loading Blog Entries..."
27523 });
27524 </code></pre>
27525
27526 * Note: old code is supported with arguments : (container, template, config)
27527
27528
27529  * @constructor
27530  * Create a new JsonView
27531  * 
27532  * @param {Object} config The config object
27533  * 
27534  */
27535 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27536     
27537     
27538     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27539
27540     var um = this.el.getUpdateManager();
27541     um.setRenderer(this);
27542     um.on("update", this.onLoad, this);
27543     um.on("failure", this.onLoadException, this);
27544
27545     /**
27546      * @event beforerender
27547      * Fires before rendering of the downloaded JSON data.
27548      * @param {Roo.JsonView} this
27549      * @param {Object} data The JSON data loaded
27550      */
27551     /**
27552      * @event load
27553      * Fires when data is loaded.
27554      * @param {Roo.JsonView} this
27555      * @param {Object} data The JSON data loaded
27556      * @param {Object} response The raw Connect response object
27557      */
27558     /**
27559      * @event loadexception
27560      * Fires when loading fails.
27561      * @param {Roo.JsonView} this
27562      * @param {Object} response The raw Connect response object
27563      */
27564     this.addEvents({
27565         'beforerender' : true,
27566         'load' : true,
27567         'loadexception' : true
27568     });
27569 };
27570 Roo.extend(Roo.JsonView, Roo.View, {
27571     /**
27572      * @type {String} The root property in the loaded JSON object that contains the data
27573      */
27574     jsonRoot : "",
27575
27576     /**
27577      * Refreshes the view.
27578      */
27579     refresh : function(){
27580         this.clearSelections();
27581         this.el.update("");
27582         var html = [];
27583         var o = this.jsonData;
27584         if(o && o.length > 0){
27585             for(var i = 0, len = o.length; i < len; i++){
27586                 var data = this.prepareData(o[i], i, o);
27587                 html[html.length] = this.tpl.apply(data);
27588             }
27589         }else{
27590             html.push(this.emptyText);
27591         }
27592         this.el.update(html.join(""));
27593         this.nodes = this.el.dom.childNodes;
27594         this.updateIndexes(0);
27595     },
27596
27597     /**
27598      * 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.
27599      * @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:
27600      <pre><code>
27601      view.load({
27602          url: "your-url.php",
27603          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27604          callback: yourFunction,
27605          scope: yourObject, //(optional scope)
27606          discardUrl: false,
27607          nocache: false,
27608          text: "Loading...",
27609          timeout: 30,
27610          scripts: false
27611      });
27612      </code></pre>
27613      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27614      * 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.
27615      * @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}
27616      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27617      * @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.
27618      */
27619     load : function(){
27620         var um = this.el.getUpdateManager();
27621         um.update.apply(um, arguments);
27622     },
27623
27624     // note - render is a standard framework call...
27625     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27626     render : function(el, response){
27627         
27628         this.clearSelections();
27629         this.el.update("");
27630         var o;
27631         try{
27632             if (response != '') {
27633                 o = Roo.util.JSON.decode(response.responseText);
27634                 if(this.jsonRoot){
27635                     
27636                     o = o[this.jsonRoot];
27637                 }
27638             }
27639         } catch(e){
27640         }
27641         /**
27642          * The current JSON data or null
27643          */
27644         this.jsonData = o;
27645         this.beforeRender();
27646         this.refresh();
27647     },
27648
27649 /**
27650  * Get the number of records in the current JSON dataset
27651  * @return {Number}
27652  */
27653     getCount : function(){
27654         return this.jsonData ? this.jsonData.length : 0;
27655     },
27656
27657 /**
27658  * Returns the JSON object for the specified node(s)
27659  * @param {HTMLElement/Array} node The node or an array of nodes
27660  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27661  * you get the JSON object for the node
27662  */
27663     getNodeData : function(node){
27664         if(node instanceof Array){
27665             var data = [];
27666             for(var i = 0, len = node.length; i < len; i++){
27667                 data.push(this.getNodeData(node[i]));
27668             }
27669             return data;
27670         }
27671         return this.jsonData[this.indexOf(node)] || null;
27672     },
27673
27674     beforeRender : function(){
27675         this.snapshot = this.jsonData;
27676         if(this.sortInfo){
27677             this.sort.apply(this, this.sortInfo);
27678         }
27679         this.fireEvent("beforerender", this, this.jsonData);
27680     },
27681
27682     onLoad : function(el, o){
27683         this.fireEvent("load", this, this.jsonData, o);
27684     },
27685
27686     onLoadException : function(el, o){
27687         this.fireEvent("loadexception", this, o);
27688     },
27689
27690 /**
27691  * Filter the data by a specific property.
27692  * @param {String} property A property on your JSON objects
27693  * @param {String/RegExp} value Either string that the property values
27694  * should start with, or a RegExp to test against the property
27695  */
27696     filter : function(property, value){
27697         if(this.jsonData){
27698             var data = [];
27699             var ss = this.snapshot;
27700             if(typeof value == "string"){
27701                 var vlen = value.length;
27702                 if(vlen == 0){
27703                     this.clearFilter();
27704                     return;
27705                 }
27706                 value = value.toLowerCase();
27707                 for(var i = 0, len = ss.length; i < len; i++){
27708                     var o = ss[i];
27709                     if(o[property].substr(0, vlen).toLowerCase() == value){
27710                         data.push(o);
27711                     }
27712                 }
27713             } else if(value.exec){ // regex?
27714                 for(var i = 0, len = ss.length; i < len; i++){
27715                     var o = ss[i];
27716                     if(value.test(o[property])){
27717                         data.push(o);
27718                     }
27719                 }
27720             } else{
27721                 return;
27722             }
27723             this.jsonData = data;
27724             this.refresh();
27725         }
27726     },
27727
27728 /**
27729  * Filter by a function. The passed function will be called with each
27730  * object in the current dataset. If the function returns true the value is kept,
27731  * otherwise it is filtered.
27732  * @param {Function} fn
27733  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27734  */
27735     filterBy : function(fn, scope){
27736         if(this.jsonData){
27737             var data = [];
27738             var ss = this.snapshot;
27739             for(var i = 0, len = ss.length; i < len; i++){
27740                 var o = ss[i];
27741                 if(fn.call(scope || this, o)){
27742                     data.push(o);
27743                 }
27744             }
27745             this.jsonData = data;
27746             this.refresh();
27747         }
27748     },
27749
27750 /**
27751  * Clears the current filter.
27752  */
27753     clearFilter : function(){
27754         if(this.snapshot && this.jsonData != this.snapshot){
27755             this.jsonData = this.snapshot;
27756             this.refresh();
27757         }
27758     },
27759
27760
27761 /**
27762  * Sorts the data for this view and refreshes it.
27763  * @param {String} property A property on your JSON objects to sort on
27764  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27765  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27766  */
27767     sort : function(property, dir, sortType){
27768         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27769         if(this.jsonData){
27770             var p = property;
27771             var dsc = dir && dir.toLowerCase() == "desc";
27772             var f = function(o1, o2){
27773                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27774                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27775                 ;
27776                 if(v1 < v2){
27777                     return dsc ? +1 : -1;
27778                 } else if(v1 > v2){
27779                     return dsc ? -1 : +1;
27780                 } else{
27781                     return 0;
27782                 }
27783             };
27784             this.jsonData.sort(f);
27785             this.refresh();
27786             if(this.jsonData != this.snapshot){
27787                 this.snapshot.sort(f);
27788             }
27789         }
27790     }
27791 });/*
27792  * Based on:
27793  * Ext JS Library 1.1.1
27794  * Copyright(c) 2006-2007, Ext JS, LLC.
27795  *
27796  * Originally Released Under LGPL - original licence link has changed is not relivant.
27797  *
27798  * Fork - LGPL
27799  * <script type="text/javascript">
27800  */
27801  
27802
27803 /**
27804  * @class Roo.ColorPalette
27805  * @extends Roo.Component
27806  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27807  * Here's an example of typical usage:
27808  * <pre><code>
27809 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27810 cp.render('my-div');
27811
27812 cp.on('select', function(palette, selColor){
27813     // do something with selColor
27814 });
27815 </code></pre>
27816  * @constructor
27817  * Create a new ColorPalette
27818  * @param {Object} config The config object
27819  */
27820 Roo.ColorPalette = function(config){
27821     Roo.ColorPalette.superclass.constructor.call(this, config);
27822     this.addEvents({
27823         /**
27824              * @event select
27825              * Fires when a color is selected
27826              * @param {ColorPalette} this
27827              * @param {String} color The 6-digit color hex code (without the # symbol)
27828              */
27829         select: true
27830     });
27831
27832     if(this.handler){
27833         this.on("select", this.handler, this.scope, true);
27834     }
27835 };
27836 Roo.extend(Roo.ColorPalette, Roo.Component, {
27837     /**
27838      * @cfg {String} itemCls
27839      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27840      */
27841     itemCls : "x-color-palette",
27842     /**
27843      * @cfg {String} value
27844      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27845      * the hex codes are case-sensitive.
27846      */
27847     value : null,
27848     clickEvent:'click',
27849     // private
27850     ctype: "Roo.ColorPalette",
27851
27852     /**
27853      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27854      */
27855     allowReselect : false,
27856
27857     /**
27858      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27859      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27860      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27861      * of colors with the width setting until the box is symmetrical.</p>
27862      * <p>You can override individual colors if needed:</p>
27863      * <pre><code>
27864 var cp = new Roo.ColorPalette();
27865 cp.colors[0] = "FF0000";  // change the first box to red
27866 </code></pre>
27867
27868 Or you can provide a custom array of your own for complete control:
27869 <pre><code>
27870 var cp = new Roo.ColorPalette();
27871 cp.colors = ["000000", "993300", "333300"];
27872 </code></pre>
27873      * @type Array
27874      */
27875     colors : [
27876         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27877         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27878         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27879         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27880         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27881     ],
27882
27883     // private
27884     onRender : function(container, position){
27885         var t = new Roo.MasterTemplate(
27886             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27887         );
27888         var c = this.colors;
27889         for(var i = 0, len = c.length; i < len; i++){
27890             t.add([c[i]]);
27891         }
27892         var el = document.createElement("div");
27893         el.className = this.itemCls;
27894         t.overwrite(el);
27895         container.dom.insertBefore(el, position);
27896         this.el = Roo.get(el);
27897         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27898         if(this.clickEvent != 'click'){
27899             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27900         }
27901     },
27902
27903     // private
27904     afterRender : function(){
27905         Roo.ColorPalette.superclass.afterRender.call(this);
27906         if(this.value){
27907             var s = this.value;
27908             this.value = null;
27909             this.select(s);
27910         }
27911     },
27912
27913     // private
27914     handleClick : function(e, t){
27915         e.preventDefault();
27916         if(!this.disabled){
27917             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27918             this.select(c.toUpperCase());
27919         }
27920     },
27921
27922     /**
27923      * Selects the specified color in the palette (fires the select event)
27924      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27925      */
27926     select : function(color){
27927         color = color.replace("#", "");
27928         if(color != this.value || this.allowReselect){
27929             var el = this.el;
27930             if(this.value){
27931                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27932             }
27933             el.child("a.color-"+color).addClass("x-color-palette-sel");
27934             this.value = color;
27935             this.fireEvent("select", this, color);
27936         }
27937     }
27938 });/*
27939  * Based on:
27940  * Ext JS Library 1.1.1
27941  * Copyright(c) 2006-2007, Ext JS, LLC.
27942  *
27943  * Originally Released Under LGPL - original licence link has changed is not relivant.
27944  *
27945  * Fork - LGPL
27946  * <script type="text/javascript">
27947  */
27948  
27949 /**
27950  * @class Roo.DatePicker
27951  * @extends Roo.Component
27952  * Simple date picker class.
27953  * @constructor
27954  * Create a new DatePicker
27955  * @param {Object} config The config object
27956  */
27957 Roo.DatePicker = function(config){
27958     Roo.DatePicker.superclass.constructor.call(this, config);
27959
27960     this.value = config && config.value ?
27961                  config.value.clearTime() : new Date().clearTime();
27962
27963     this.addEvents({
27964         /**
27965              * @event select
27966              * Fires when a date is selected
27967              * @param {DatePicker} this
27968              * @param {Date} date The selected date
27969              */
27970         'select': true,
27971         /**
27972              * @event monthchange
27973              * Fires when the displayed month changes 
27974              * @param {DatePicker} this
27975              * @param {Date} date The selected month
27976              */
27977         'monthchange': true
27978     });
27979
27980     if(this.handler){
27981         this.on("select", this.handler,  this.scope || this);
27982     }
27983     // build the disabledDatesRE
27984     if(!this.disabledDatesRE && this.disabledDates){
27985         var dd = this.disabledDates;
27986         var re = "(?:";
27987         for(var i = 0; i < dd.length; i++){
27988             re += dd[i];
27989             if(i != dd.length-1) {
27990                 re += "|";
27991             }
27992         }
27993         this.disabledDatesRE = new RegExp(re + ")");
27994     }
27995 };
27996
27997 Roo.extend(Roo.DatePicker, Roo.Component, {
27998     /**
27999      * @cfg {String} todayText
28000      * The text to display on the button that selects the current date (defaults to "Today")
28001      */
28002     todayText : "Today",
28003     /**
28004      * @cfg {String} okText
28005      * The text to display on the ok button
28006      */
28007     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28008     /**
28009      * @cfg {String} cancelText
28010      * The text to display on the cancel button
28011      */
28012     cancelText : "Cancel",
28013     /**
28014      * @cfg {String} todayTip
28015      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28016      */
28017     todayTip : "{0} (Spacebar)",
28018     /**
28019      * @cfg {Date} minDate
28020      * Minimum allowable date (JavaScript date object, defaults to null)
28021      */
28022     minDate : null,
28023     /**
28024      * @cfg {Date} maxDate
28025      * Maximum allowable date (JavaScript date object, defaults to null)
28026      */
28027     maxDate : null,
28028     /**
28029      * @cfg {String} minText
28030      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28031      */
28032     minText : "This date is before the minimum date",
28033     /**
28034      * @cfg {String} maxText
28035      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28036      */
28037     maxText : "This date is after the maximum date",
28038     /**
28039      * @cfg {String} format
28040      * The default date format string which can be overriden for localization support.  The format must be
28041      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28042      */
28043     format : "m/d/y",
28044     /**
28045      * @cfg {Array} disabledDays
28046      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28047      */
28048     disabledDays : null,
28049     /**
28050      * @cfg {String} disabledDaysText
28051      * The tooltip to display when the date falls on a disabled day (defaults to "")
28052      */
28053     disabledDaysText : "",
28054     /**
28055      * @cfg {RegExp} disabledDatesRE
28056      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28057      */
28058     disabledDatesRE : null,
28059     /**
28060      * @cfg {String} disabledDatesText
28061      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28062      */
28063     disabledDatesText : "",
28064     /**
28065      * @cfg {Boolean} constrainToViewport
28066      * True to constrain the date picker to the viewport (defaults to true)
28067      */
28068     constrainToViewport : true,
28069     /**
28070      * @cfg {Array} monthNames
28071      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28072      */
28073     monthNames : Date.monthNames,
28074     /**
28075      * @cfg {Array} dayNames
28076      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28077      */
28078     dayNames : Date.dayNames,
28079     /**
28080      * @cfg {String} nextText
28081      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28082      */
28083     nextText: 'Next Month (Control+Right)',
28084     /**
28085      * @cfg {String} prevText
28086      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28087      */
28088     prevText: 'Previous Month (Control+Left)',
28089     /**
28090      * @cfg {String} monthYearText
28091      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28092      */
28093     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28094     /**
28095      * @cfg {Number} startDay
28096      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28097      */
28098     startDay : 0,
28099     /**
28100      * @cfg {Bool} showClear
28101      * Show a clear button (usefull for date form elements that can be blank.)
28102      */
28103     
28104     showClear: false,
28105     
28106     /**
28107      * Sets the value of the date field
28108      * @param {Date} value The date to set
28109      */
28110     setValue : function(value){
28111         var old = this.value;
28112         
28113         if (typeof(value) == 'string') {
28114          
28115             value = Date.parseDate(value, this.format);
28116         }
28117         if (!value) {
28118             value = new Date();
28119         }
28120         
28121         this.value = value.clearTime(true);
28122         if(this.el){
28123             this.update(this.value);
28124         }
28125     },
28126
28127     /**
28128      * Gets the current selected value of the date field
28129      * @return {Date} The selected date
28130      */
28131     getValue : function(){
28132         return this.value;
28133     },
28134
28135     // private
28136     focus : function(){
28137         if(this.el){
28138             this.update(this.activeDate);
28139         }
28140     },
28141
28142     // privateval
28143     onRender : function(container, position){
28144         
28145         var m = [
28146              '<table cellspacing="0">',
28147                 '<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>',
28148                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28149         var dn = this.dayNames;
28150         for(var i = 0; i < 7; i++){
28151             var d = this.startDay+i;
28152             if(d > 6){
28153                 d = d-7;
28154             }
28155             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28156         }
28157         m[m.length] = "</tr></thead><tbody><tr>";
28158         for(var i = 0; i < 42; i++) {
28159             if(i % 7 == 0 && i != 0){
28160                 m[m.length] = "</tr><tr>";
28161             }
28162             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28163         }
28164         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28165             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28166
28167         var el = document.createElement("div");
28168         el.className = "x-date-picker";
28169         el.innerHTML = m.join("");
28170
28171         container.dom.insertBefore(el, position);
28172
28173         this.el = Roo.get(el);
28174         this.eventEl = Roo.get(el.firstChild);
28175
28176         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28177             handler: this.showPrevMonth,
28178             scope: this,
28179             preventDefault:true,
28180             stopDefault:true
28181         });
28182
28183         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28184             handler: this.showNextMonth,
28185             scope: this,
28186             preventDefault:true,
28187             stopDefault:true
28188         });
28189
28190         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28191
28192         this.monthPicker = this.el.down('div.x-date-mp');
28193         this.monthPicker.enableDisplayMode('block');
28194         
28195         var kn = new Roo.KeyNav(this.eventEl, {
28196             "left" : function(e){
28197                 e.ctrlKey ?
28198                     this.showPrevMonth() :
28199                     this.update(this.activeDate.add("d", -1));
28200             },
28201
28202             "right" : function(e){
28203                 e.ctrlKey ?
28204                     this.showNextMonth() :
28205                     this.update(this.activeDate.add("d", 1));
28206             },
28207
28208             "up" : function(e){
28209                 e.ctrlKey ?
28210                     this.showNextYear() :
28211                     this.update(this.activeDate.add("d", -7));
28212             },
28213
28214             "down" : function(e){
28215                 e.ctrlKey ?
28216                     this.showPrevYear() :
28217                     this.update(this.activeDate.add("d", 7));
28218             },
28219
28220             "pageUp" : function(e){
28221                 this.showNextMonth();
28222             },
28223
28224             "pageDown" : function(e){
28225                 this.showPrevMonth();
28226             },
28227
28228             "enter" : function(e){
28229                 e.stopPropagation();
28230                 return true;
28231             },
28232
28233             scope : this
28234         });
28235
28236         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28237
28238         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28239
28240         this.el.unselectable();
28241         
28242         this.cells = this.el.select("table.x-date-inner tbody td");
28243         this.textNodes = this.el.query("table.x-date-inner tbody span");
28244
28245         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28246             text: "&#160;",
28247             tooltip: this.monthYearText
28248         });
28249
28250         this.mbtn.on('click', this.showMonthPicker, this);
28251         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28252
28253
28254         var today = (new Date()).dateFormat(this.format);
28255         
28256         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28257         if (this.showClear) {
28258             baseTb.add( new Roo.Toolbar.Fill());
28259         }
28260         baseTb.add({
28261             text: String.format(this.todayText, today),
28262             tooltip: String.format(this.todayTip, today),
28263             handler: this.selectToday,
28264             scope: this
28265         });
28266         
28267         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28268             
28269         //});
28270         if (this.showClear) {
28271             
28272             baseTb.add( new Roo.Toolbar.Fill());
28273             baseTb.add({
28274                 text: '&#160;',
28275                 cls: 'x-btn-icon x-btn-clear',
28276                 handler: function() {
28277                     //this.value = '';
28278                     this.fireEvent("select", this, '');
28279                 },
28280                 scope: this
28281             });
28282         }
28283         
28284         
28285         if(Roo.isIE){
28286             this.el.repaint();
28287         }
28288         this.update(this.value);
28289     },
28290
28291     createMonthPicker : function(){
28292         if(!this.monthPicker.dom.firstChild){
28293             var buf = ['<table border="0" cellspacing="0">'];
28294             for(var i = 0; i < 6; i++){
28295                 buf.push(
28296                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28297                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28298                     i == 0 ?
28299                     '<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>' :
28300                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28301                 );
28302             }
28303             buf.push(
28304                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28305                     this.okText,
28306                     '</button><button type="button" class="x-date-mp-cancel">',
28307                     this.cancelText,
28308                     '</button></td></tr>',
28309                 '</table>'
28310             );
28311             this.monthPicker.update(buf.join(''));
28312             this.monthPicker.on('click', this.onMonthClick, this);
28313             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28314
28315             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28316             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28317
28318             this.mpMonths.each(function(m, a, i){
28319                 i += 1;
28320                 if((i%2) == 0){
28321                     m.dom.xmonth = 5 + Math.round(i * .5);
28322                 }else{
28323                     m.dom.xmonth = Math.round((i-1) * .5);
28324                 }
28325             });
28326         }
28327     },
28328
28329     showMonthPicker : function(){
28330         this.createMonthPicker();
28331         var size = this.el.getSize();
28332         this.monthPicker.setSize(size);
28333         this.monthPicker.child('table').setSize(size);
28334
28335         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28336         this.updateMPMonth(this.mpSelMonth);
28337         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28338         this.updateMPYear(this.mpSelYear);
28339
28340         this.monthPicker.slideIn('t', {duration:.2});
28341     },
28342
28343     updateMPYear : function(y){
28344         this.mpyear = y;
28345         var ys = this.mpYears.elements;
28346         for(var i = 1; i <= 10; i++){
28347             var td = ys[i-1], y2;
28348             if((i%2) == 0){
28349                 y2 = y + Math.round(i * .5);
28350                 td.firstChild.innerHTML = y2;
28351                 td.xyear = y2;
28352             }else{
28353                 y2 = y - (5-Math.round(i * .5));
28354                 td.firstChild.innerHTML = y2;
28355                 td.xyear = y2;
28356             }
28357             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28358         }
28359     },
28360
28361     updateMPMonth : function(sm){
28362         this.mpMonths.each(function(m, a, i){
28363             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28364         });
28365     },
28366
28367     selectMPMonth: function(m){
28368         
28369     },
28370
28371     onMonthClick : function(e, t){
28372         e.stopEvent();
28373         var el = new Roo.Element(t), pn;
28374         if(el.is('button.x-date-mp-cancel')){
28375             this.hideMonthPicker();
28376         }
28377         else if(el.is('button.x-date-mp-ok')){
28378             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28379             this.hideMonthPicker();
28380         }
28381         else if(pn = el.up('td.x-date-mp-month', 2)){
28382             this.mpMonths.removeClass('x-date-mp-sel');
28383             pn.addClass('x-date-mp-sel');
28384             this.mpSelMonth = pn.dom.xmonth;
28385         }
28386         else if(pn = el.up('td.x-date-mp-year', 2)){
28387             this.mpYears.removeClass('x-date-mp-sel');
28388             pn.addClass('x-date-mp-sel');
28389             this.mpSelYear = pn.dom.xyear;
28390         }
28391         else if(el.is('a.x-date-mp-prev')){
28392             this.updateMPYear(this.mpyear-10);
28393         }
28394         else if(el.is('a.x-date-mp-next')){
28395             this.updateMPYear(this.mpyear+10);
28396         }
28397     },
28398
28399     onMonthDblClick : function(e, t){
28400         e.stopEvent();
28401         var el = new Roo.Element(t), pn;
28402         if(pn = el.up('td.x-date-mp-month', 2)){
28403             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28404             this.hideMonthPicker();
28405         }
28406         else if(pn = el.up('td.x-date-mp-year', 2)){
28407             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28408             this.hideMonthPicker();
28409         }
28410     },
28411
28412     hideMonthPicker : function(disableAnim){
28413         if(this.monthPicker){
28414             if(disableAnim === true){
28415                 this.monthPicker.hide();
28416             }else{
28417                 this.monthPicker.slideOut('t', {duration:.2});
28418             }
28419         }
28420     },
28421
28422     // private
28423     showPrevMonth : function(e){
28424         this.update(this.activeDate.add("mo", -1));
28425     },
28426
28427     // private
28428     showNextMonth : function(e){
28429         this.update(this.activeDate.add("mo", 1));
28430     },
28431
28432     // private
28433     showPrevYear : function(){
28434         this.update(this.activeDate.add("y", -1));
28435     },
28436
28437     // private
28438     showNextYear : function(){
28439         this.update(this.activeDate.add("y", 1));
28440     },
28441
28442     // private
28443     handleMouseWheel : function(e){
28444         var delta = e.getWheelDelta();
28445         if(delta > 0){
28446             this.showPrevMonth();
28447             e.stopEvent();
28448         } else if(delta < 0){
28449             this.showNextMonth();
28450             e.stopEvent();
28451         }
28452     },
28453
28454     // private
28455     handleDateClick : function(e, t){
28456         e.stopEvent();
28457         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28458             this.setValue(new Date(t.dateValue));
28459             this.fireEvent("select", this, this.value);
28460         }
28461     },
28462
28463     // private
28464     selectToday : function(){
28465         this.setValue(new Date().clearTime());
28466         this.fireEvent("select", this, this.value);
28467     },
28468
28469     // private
28470     update : function(date)
28471     {
28472         var vd = this.activeDate;
28473         this.activeDate = date;
28474         if(vd && this.el){
28475             var t = date.getTime();
28476             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28477                 this.cells.removeClass("x-date-selected");
28478                 this.cells.each(function(c){
28479                    if(c.dom.firstChild.dateValue == t){
28480                        c.addClass("x-date-selected");
28481                        setTimeout(function(){
28482                             try{c.dom.firstChild.focus();}catch(e){}
28483                        }, 50);
28484                        return false;
28485                    }
28486                 });
28487                 return;
28488             }
28489         }
28490         
28491         var days = date.getDaysInMonth();
28492         var firstOfMonth = date.getFirstDateOfMonth();
28493         var startingPos = firstOfMonth.getDay()-this.startDay;
28494
28495         if(startingPos <= this.startDay){
28496             startingPos += 7;
28497         }
28498
28499         var pm = date.add("mo", -1);
28500         var prevStart = pm.getDaysInMonth()-startingPos;
28501
28502         var cells = this.cells.elements;
28503         var textEls = this.textNodes;
28504         days += startingPos;
28505
28506         // convert everything to numbers so it's fast
28507         var day = 86400000;
28508         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28509         var today = new Date().clearTime().getTime();
28510         var sel = date.clearTime().getTime();
28511         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28512         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28513         var ddMatch = this.disabledDatesRE;
28514         var ddText = this.disabledDatesText;
28515         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28516         var ddaysText = this.disabledDaysText;
28517         var format = this.format;
28518
28519         var setCellClass = function(cal, cell){
28520             cell.title = "";
28521             var t = d.getTime();
28522             cell.firstChild.dateValue = t;
28523             if(t == today){
28524                 cell.className += " x-date-today";
28525                 cell.title = cal.todayText;
28526             }
28527             if(t == sel){
28528                 cell.className += " x-date-selected";
28529                 setTimeout(function(){
28530                     try{cell.firstChild.focus();}catch(e){}
28531                 }, 50);
28532             }
28533             // disabling
28534             if(t < min) {
28535                 cell.className = " x-date-disabled";
28536                 cell.title = cal.minText;
28537                 return;
28538             }
28539             if(t > max) {
28540                 cell.className = " x-date-disabled";
28541                 cell.title = cal.maxText;
28542                 return;
28543             }
28544             if(ddays){
28545                 if(ddays.indexOf(d.getDay()) != -1){
28546                     cell.title = ddaysText;
28547                     cell.className = " x-date-disabled";
28548                 }
28549             }
28550             if(ddMatch && format){
28551                 var fvalue = d.dateFormat(format);
28552                 if(ddMatch.test(fvalue)){
28553                     cell.title = ddText.replace("%0", fvalue);
28554                     cell.className = " x-date-disabled";
28555                 }
28556             }
28557         };
28558
28559         var i = 0;
28560         for(; i < startingPos; i++) {
28561             textEls[i].innerHTML = (++prevStart);
28562             d.setDate(d.getDate()+1);
28563             cells[i].className = "x-date-prevday";
28564             setCellClass(this, cells[i]);
28565         }
28566         for(; i < days; i++){
28567             intDay = i - startingPos + 1;
28568             textEls[i].innerHTML = (intDay);
28569             d.setDate(d.getDate()+1);
28570             cells[i].className = "x-date-active";
28571             setCellClass(this, cells[i]);
28572         }
28573         var extraDays = 0;
28574         for(; i < 42; i++) {
28575              textEls[i].innerHTML = (++extraDays);
28576              d.setDate(d.getDate()+1);
28577              cells[i].className = "x-date-nextday";
28578              setCellClass(this, cells[i]);
28579         }
28580
28581         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28582         this.fireEvent('monthchange', this, date);
28583         
28584         if(!this.internalRender){
28585             var main = this.el.dom.firstChild;
28586             var w = main.offsetWidth;
28587             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28588             Roo.fly(main).setWidth(w);
28589             this.internalRender = true;
28590             // opera does not respect the auto grow header center column
28591             // then, after it gets a width opera refuses to recalculate
28592             // without a second pass
28593             if(Roo.isOpera && !this.secondPass){
28594                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28595                 this.secondPass = true;
28596                 this.update.defer(10, this, [date]);
28597             }
28598         }
28599         
28600         
28601     }
28602 });        /*
28603  * Based on:
28604  * Ext JS Library 1.1.1
28605  * Copyright(c) 2006-2007, Ext JS, LLC.
28606  *
28607  * Originally Released Under LGPL - original licence link has changed is not relivant.
28608  *
28609  * Fork - LGPL
28610  * <script type="text/javascript">
28611  */
28612 /**
28613  * @class Roo.TabPanel
28614  * @extends Roo.util.Observable
28615  * A lightweight tab container.
28616  * <br><br>
28617  * Usage:
28618  * <pre><code>
28619 // basic tabs 1, built from existing content
28620 var tabs = new Roo.TabPanel("tabs1");
28621 tabs.addTab("script", "View Script");
28622 tabs.addTab("markup", "View Markup");
28623 tabs.activate("script");
28624
28625 // more advanced tabs, built from javascript
28626 var jtabs = new Roo.TabPanel("jtabs");
28627 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28628
28629 // set up the UpdateManager
28630 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28631 var updater = tab2.getUpdateManager();
28632 updater.setDefaultUrl("ajax1.htm");
28633 tab2.on('activate', updater.refresh, updater, true);
28634
28635 // Use setUrl for Ajax loading
28636 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28637 tab3.setUrl("ajax2.htm", null, true);
28638
28639 // Disabled tab
28640 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28641 tab4.disable();
28642
28643 jtabs.activate("jtabs-1");
28644  * </code></pre>
28645  * @constructor
28646  * Create a new TabPanel.
28647  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28648  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28649  */
28650 Roo.TabPanel = function(container, config){
28651     /**
28652     * The container element for this TabPanel.
28653     * @type Roo.Element
28654     */
28655     this.el = Roo.get(container, true);
28656     if(config){
28657         if(typeof config == "boolean"){
28658             this.tabPosition = config ? "bottom" : "top";
28659         }else{
28660             Roo.apply(this, config);
28661         }
28662     }
28663     if(this.tabPosition == "bottom"){
28664         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28665         this.el.addClass("x-tabs-bottom");
28666     }
28667     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28668     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28669     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28670     if(Roo.isIE){
28671         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28672     }
28673     if(this.tabPosition != "bottom"){
28674         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28675          * @type Roo.Element
28676          */
28677         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28678         this.el.addClass("x-tabs-top");
28679     }
28680     this.items = [];
28681
28682     this.bodyEl.setStyle("position", "relative");
28683
28684     this.active = null;
28685     this.activateDelegate = this.activate.createDelegate(this);
28686
28687     this.addEvents({
28688         /**
28689          * @event tabchange
28690          * Fires when the active tab changes
28691          * @param {Roo.TabPanel} this
28692          * @param {Roo.TabPanelItem} activePanel The new active tab
28693          */
28694         "tabchange": true,
28695         /**
28696          * @event beforetabchange
28697          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28698          * @param {Roo.TabPanel} this
28699          * @param {Object} e Set cancel to true on this object to cancel the tab change
28700          * @param {Roo.TabPanelItem} tab The tab being changed to
28701          */
28702         "beforetabchange" : true
28703     });
28704
28705     Roo.EventManager.onWindowResize(this.onResize, this);
28706     this.cpad = this.el.getPadding("lr");
28707     this.hiddenCount = 0;
28708
28709
28710     // toolbar on the tabbar support...
28711     if (this.toolbar) {
28712         var tcfg = this.toolbar;
28713         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28714         this.toolbar = new Roo.Toolbar(tcfg);
28715         if (Roo.isSafari) {
28716             var tbl = tcfg.container.child('table', true);
28717             tbl.setAttribute('width', '100%');
28718         }
28719         
28720     }
28721    
28722
28723
28724     Roo.TabPanel.superclass.constructor.call(this);
28725 };
28726
28727 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28728     /*
28729      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28730      */
28731     tabPosition : "top",
28732     /*
28733      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28734      */
28735     currentTabWidth : 0,
28736     /*
28737      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28738      */
28739     minTabWidth : 40,
28740     /*
28741      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28742      */
28743     maxTabWidth : 250,
28744     /*
28745      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28746      */
28747     preferredTabWidth : 175,
28748     /*
28749      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28750      */
28751     resizeTabs : false,
28752     /*
28753      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28754      */
28755     monitorResize : true,
28756     /*
28757      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28758      */
28759     toolbar : false,
28760
28761     /**
28762      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28763      * @param {String} id The id of the div to use <b>or create</b>
28764      * @param {String} text The text for the tab
28765      * @param {String} content (optional) Content to put in the TabPanelItem body
28766      * @param {Boolean} closable (optional) True to create a close icon on the tab
28767      * @return {Roo.TabPanelItem} The created TabPanelItem
28768      */
28769     addTab : function(id, text, content, closable){
28770         var item = new Roo.TabPanelItem(this, id, text, closable);
28771         this.addTabItem(item);
28772         if(content){
28773             item.setContent(content);
28774         }
28775         return item;
28776     },
28777
28778     /**
28779      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28780      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28781      * @return {Roo.TabPanelItem}
28782      */
28783     getTab : function(id){
28784         return this.items[id];
28785     },
28786
28787     /**
28788      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28789      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28790      */
28791     hideTab : function(id){
28792         var t = this.items[id];
28793         if(!t.isHidden()){
28794            t.setHidden(true);
28795            this.hiddenCount++;
28796            this.autoSizeTabs();
28797         }
28798     },
28799
28800     /**
28801      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28802      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28803      */
28804     unhideTab : function(id){
28805         var t = this.items[id];
28806         if(t.isHidden()){
28807            t.setHidden(false);
28808            this.hiddenCount--;
28809            this.autoSizeTabs();
28810         }
28811     },
28812
28813     /**
28814      * Adds an existing {@link Roo.TabPanelItem}.
28815      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28816      */
28817     addTabItem : function(item){
28818         this.items[item.id] = item;
28819         this.items.push(item);
28820         if(this.resizeTabs){
28821            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28822            this.autoSizeTabs();
28823         }else{
28824             item.autoSize();
28825         }
28826     },
28827
28828     /**
28829      * Removes a {@link Roo.TabPanelItem}.
28830      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28831      */
28832     removeTab : function(id){
28833         var items = this.items;
28834         var tab = items[id];
28835         if(!tab) { return; }
28836         var index = items.indexOf(tab);
28837         if(this.active == tab && items.length > 1){
28838             var newTab = this.getNextAvailable(index);
28839             if(newTab) {
28840                 newTab.activate();
28841             }
28842         }
28843         this.stripEl.dom.removeChild(tab.pnode.dom);
28844         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28845             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28846         }
28847         items.splice(index, 1);
28848         delete this.items[tab.id];
28849         tab.fireEvent("close", tab);
28850         tab.purgeListeners();
28851         this.autoSizeTabs();
28852     },
28853
28854     getNextAvailable : function(start){
28855         var items = this.items;
28856         var index = start;
28857         // look for a next tab that will slide over to
28858         // replace the one being removed
28859         while(index < items.length){
28860             var item = items[++index];
28861             if(item && !item.isHidden()){
28862                 return item;
28863             }
28864         }
28865         // if one isn't found select the previous tab (on the left)
28866         index = start;
28867         while(index >= 0){
28868             var item = items[--index];
28869             if(item && !item.isHidden()){
28870                 return item;
28871             }
28872         }
28873         return null;
28874     },
28875
28876     /**
28877      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28878      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28879      */
28880     disableTab : function(id){
28881         var tab = this.items[id];
28882         if(tab && this.active != tab){
28883             tab.disable();
28884         }
28885     },
28886
28887     /**
28888      * Enables a {@link Roo.TabPanelItem} that is disabled.
28889      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28890      */
28891     enableTab : function(id){
28892         var tab = this.items[id];
28893         tab.enable();
28894     },
28895
28896     /**
28897      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28898      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28899      * @return {Roo.TabPanelItem} The TabPanelItem.
28900      */
28901     activate : function(id){
28902         var tab = this.items[id];
28903         if(!tab){
28904             return null;
28905         }
28906         if(tab == this.active || tab.disabled){
28907             return tab;
28908         }
28909         var e = {};
28910         this.fireEvent("beforetabchange", this, e, tab);
28911         if(e.cancel !== true && !tab.disabled){
28912             if(this.active){
28913                 this.active.hide();
28914             }
28915             this.active = this.items[id];
28916             this.active.show();
28917             this.fireEvent("tabchange", this, this.active);
28918         }
28919         return tab;
28920     },
28921
28922     /**
28923      * Gets the active {@link Roo.TabPanelItem}.
28924      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28925      */
28926     getActiveTab : function(){
28927         return this.active;
28928     },
28929
28930     /**
28931      * Updates the tab body element to fit the height of the container element
28932      * for overflow scrolling
28933      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28934      */
28935     syncHeight : function(targetHeight){
28936         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28937         var bm = this.bodyEl.getMargins();
28938         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28939         this.bodyEl.setHeight(newHeight);
28940         return newHeight;
28941     },
28942
28943     onResize : function(){
28944         if(this.monitorResize){
28945             this.autoSizeTabs();
28946         }
28947     },
28948
28949     /**
28950      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28951      */
28952     beginUpdate : function(){
28953         this.updating = true;
28954     },
28955
28956     /**
28957      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28958      */
28959     endUpdate : function(){
28960         this.updating = false;
28961         this.autoSizeTabs();
28962     },
28963
28964     /**
28965      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28966      */
28967     autoSizeTabs : function(){
28968         var count = this.items.length;
28969         var vcount = count - this.hiddenCount;
28970         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28971             return;
28972         }
28973         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28974         var availWidth = Math.floor(w / vcount);
28975         var b = this.stripBody;
28976         if(b.getWidth() > w){
28977             var tabs = this.items;
28978             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28979             if(availWidth < this.minTabWidth){
28980                 /*if(!this.sleft){    // incomplete scrolling code
28981                     this.createScrollButtons();
28982                 }
28983                 this.showScroll();
28984                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28985             }
28986         }else{
28987             if(this.currentTabWidth < this.preferredTabWidth){
28988                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28989             }
28990         }
28991     },
28992
28993     /**
28994      * Returns the number of tabs in this TabPanel.
28995      * @return {Number}
28996      */
28997      getCount : function(){
28998          return this.items.length;
28999      },
29000
29001     /**
29002      * Resizes all the tabs to the passed width
29003      * @param {Number} The new width
29004      */
29005     setTabWidth : function(width){
29006         this.currentTabWidth = width;
29007         for(var i = 0, len = this.items.length; i < len; i++) {
29008                 if(!this.items[i].isHidden()) {
29009                 this.items[i].setWidth(width);
29010             }
29011         }
29012     },
29013
29014     /**
29015      * Destroys this TabPanel
29016      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29017      */
29018     destroy : function(removeEl){
29019         Roo.EventManager.removeResizeListener(this.onResize, this);
29020         for(var i = 0, len = this.items.length; i < len; i++){
29021             this.items[i].purgeListeners();
29022         }
29023         if(removeEl === true){
29024             this.el.update("");
29025             this.el.remove();
29026         }
29027     }
29028 });
29029
29030 /**
29031  * @class Roo.TabPanelItem
29032  * @extends Roo.util.Observable
29033  * Represents an individual item (tab plus body) in a TabPanel.
29034  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29035  * @param {String} id The id of this TabPanelItem
29036  * @param {String} text The text for the tab of this TabPanelItem
29037  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29038  */
29039 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29040     /**
29041      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29042      * @type Roo.TabPanel
29043      */
29044     this.tabPanel = tabPanel;
29045     /**
29046      * The id for this TabPanelItem
29047      * @type String
29048      */
29049     this.id = id;
29050     /** @private */
29051     this.disabled = false;
29052     /** @private */
29053     this.text = text;
29054     /** @private */
29055     this.loaded = false;
29056     this.closable = closable;
29057
29058     /**
29059      * The body element for this TabPanelItem.
29060      * @type Roo.Element
29061      */
29062     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29063     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29064     this.bodyEl.setStyle("display", "block");
29065     this.bodyEl.setStyle("zoom", "1");
29066     this.hideAction();
29067
29068     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29069     /** @private */
29070     this.el = Roo.get(els.el, true);
29071     this.inner = Roo.get(els.inner, true);
29072     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29073     this.pnode = Roo.get(els.el.parentNode, true);
29074     this.el.on("mousedown", this.onTabMouseDown, this);
29075     this.el.on("click", this.onTabClick, this);
29076     /** @private */
29077     if(closable){
29078         var c = Roo.get(els.close, true);
29079         c.dom.title = this.closeText;
29080         c.addClassOnOver("close-over");
29081         c.on("click", this.closeClick, this);
29082      }
29083
29084     this.addEvents({
29085          /**
29086          * @event activate
29087          * Fires when this tab becomes the active tab.
29088          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29089          * @param {Roo.TabPanelItem} this
29090          */
29091         "activate": true,
29092         /**
29093          * @event beforeclose
29094          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29095          * @param {Roo.TabPanelItem} this
29096          * @param {Object} e Set cancel to true on this object to cancel the close.
29097          */
29098         "beforeclose": true,
29099         /**
29100          * @event close
29101          * Fires when this tab is closed.
29102          * @param {Roo.TabPanelItem} this
29103          */
29104          "close": true,
29105         /**
29106          * @event deactivate
29107          * Fires when this tab is no longer the active tab.
29108          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29109          * @param {Roo.TabPanelItem} this
29110          */
29111          "deactivate" : true
29112     });
29113     this.hidden = false;
29114
29115     Roo.TabPanelItem.superclass.constructor.call(this);
29116 };
29117
29118 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29119     purgeListeners : function(){
29120        Roo.util.Observable.prototype.purgeListeners.call(this);
29121        this.el.removeAllListeners();
29122     },
29123     /**
29124      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29125      */
29126     show : function(){
29127         this.pnode.addClass("on");
29128         this.showAction();
29129         if(Roo.isOpera){
29130             this.tabPanel.stripWrap.repaint();
29131         }
29132         this.fireEvent("activate", this.tabPanel, this);
29133     },
29134
29135     /**
29136      * Returns true if this tab is the active tab.
29137      * @return {Boolean}
29138      */
29139     isActive : function(){
29140         return this.tabPanel.getActiveTab() == this;
29141     },
29142
29143     /**
29144      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29145      */
29146     hide : function(){
29147         this.pnode.removeClass("on");
29148         this.hideAction();
29149         this.fireEvent("deactivate", this.tabPanel, this);
29150     },
29151
29152     hideAction : function(){
29153         this.bodyEl.hide();
29154         this.bodyEl.setStyle("position", "absolute");
29155         this.bodyEl.setLeft("-20000px");
29156         this.bodyEl.setTop("-20000px");
29157     },
29158
29159     showAction : function(){
29160         this.bodyEl.setStyle("position", "relative");
29161         this.bodyEl.setTop("");
29162         this.bodyEl.setLeft("");
29163         this.bodyEl.show();
29164     },
29165
29166     /**
29167      * Set the tooltip for the tab.
29168      * @param {String} tooltip The tab's tooltip
29169      */
29170     setTooltip : function(text){
29171         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29172             this.textEl.dom.qtip = text;
29173             this.textEl.dom.removeAttribute('title');
29174         }else{
29175             this.textEl.dom.title = text;
29176         }
29177     },
29178
29179     onTabClick : function(e){
29180         e.preventDefault();
29181         this.tabPanel.activate(this.id);
29182     },
29183
29184     onTabMouseDown : function(e){
29185         e.preventDefault();
29186         this.tabPanel.activate(this.id);
29187     },
29188
29189     getWidth : function(){
29190         return this.inner.getWidth();
29191     },
29192
29193     setWidth : function(width){
29194         var iwidth = width - this.pnode.getPadding("lr");
29195         this.inner.setWidth(iwidth);
29196         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29197         this.pnode.setWidth(width);
29198     },
29199
29200     /**
29201      * Show or hide the tab
29202      * @param {Boolean} hidden True to hide or false to show.
29203      */
29204     setHidden : function(hidden){
29205         this.hidden = hidden;
29206         this.pnode.setStyle("display", hidden ? "none" : "");
29207     },
29208
29209     /**
29210      * Returns true if this tab is "hidden"
29211      * @return {Boolean}
29212      */
29213     isHidden : function(){
29214         return this.hidden;
29215     },
29216
29217     /**
29218      * Returns the text for this tab
29219      * @return {String}
29220      */
29221     getText : function(){
29222         return this.text;
29223     },
29224
29225     autoSize : function(){
29226         //this.el.beginMeasure();
29227         this.textEl.setWidth(1);
29228         /*
29229          *  #2804 [new] Tabs in Roojs
29230          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29231          */
29232         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29233         //this.el.endMeasure();
29234     },
29235
29236     /**
29237      * Sets the text for the tab (Note: this also sets the tooltip text)
29238      * @param {String} text The tab's text and tooltip
29239      */
29240     setText : function(text){
29241         this.text = text;
29242         this.textEl.update(text);
29243         this.setTooltip(text);
29244         if(!this.tabPanel.resizeTabs){
29245             this.autoSize();
29246         }
29247     },
29248     /**
29249      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29250      */
29251     activate : function(){
29252         this.tabPanel.activate(this.id);
29253     },
29254
29255     /**
29256      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29257      */
29258     disable : function(){
29259         if(this.tabPanel.active != this){
29260             this.disabled = true;
29261             this.pnode.addClass("disabled");
29262         }
29263     },
29264
29265     /**
29266      * Enables this TabPanelItem if it was previously disabled.
29267      */
29268     enable : function(){
29269         this.disabled = false;
29270         this.pnode.removeClass("disabled");
29271     },
29272
29273     /**
29274      * Sets the content for this TabPanelItem.
29275      * @param {String} content The content
29276      * @param {Boolean} loadScripts true to look for and load scripts
29277      */
29278     setContent : function(content, loadScripts){
29279         this.bodyEl.update(content, loadScripts);
29280     },
29281
29282     /**
29283      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29284      * @return {Roo.UpdateManager} The UpdateManager
29285      */
29286     getUpdateManager : function(){
29287         return this.bodyEl.getUpdateManager();
29288     },
29289
29290     /**
29291      * Set a URL to be used to load the content for this TabPanelItem.
29292      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29293      * @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)
29294      * @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)
29295      * @return {Roo.UpdateManager} The UpdateManager
29296      */
29297     setUrl : function(url, params, loadOnce){
29298         if(this.refreshDelegate){
29299             this.un('activate', this.refreshDelegate);
29300         }
29301         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29302         this.on("activate", this.refreshDelegate);
29303         return this.bodyEl.getUpdateManager();
29304     },
29305
29306     /** @private */
29307     _handleRefresh : function(url, params, loadOnce){
29308         if(!loadOnce || !this.loaded){
29309             var updater = this.bodyEl.getUpdateManager();
29310             updater.update(url, params, this._setLoaded.createDelegate(this));
29311         }
29312     },
29313
29314     /**
29315      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29316      *   Will fail silently if the setUrl method has not been called.
29317      *   This does not activate the panel, just updates its content.
29318      */
29319     refresh : function(){
29320         if(this.refreshDelegate){
29321            this.loaded = false;
29322            this.refreshDelegate();
29323         }
29324     },
29325
29326     /** @private */
29327     _setLoaded : function(){
29328         this.loaded = true;
29329     },
29330
29331     /** @private */
29332     closeClick : function(e){
29333         var o = {};
29334         e.stopEvent();
29335         this.fireEvent("beforeclose", this, o);
29336         if(o.cancel !== true){
29337             this.tabPanel.removeTab(this.id);
29338         }
29339     },
29340     /**
29341      * The text displayed in the tooltip for the close icon.
29342      * @type String
29343      */
29344     closeText : "Close this tab"
29345 });
29346
29347 /** @private */
29348 Roo.TabPanel.prototype.createStrip = function(container){
29349     var strip = document.createElement("div");
29350     strip.className = "x-tabs-wrap";
29351     container.appendChild(strip);
29352     return strip;
29353 };
29354 /** @private */
29355 Roo.TabPanel.prototype.createStripList = function(strip){
29356     // div wrapper for retard IE
29357     // returns the "tr" element.
29358     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29359         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29360         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29361     return strip.firstChild.firstChild.firstChild.firstChild;
29362 };
29363 /** @private */
29364 Roo.TabPanel.prototype.createBody = function(container){
29365     var body = document.createElement("div");
29366     Roo.id(body, "tab-body");
29367     Roo.fly(body).addClass("x-tabs-body");
29368     container.appendChild(body);
29369     return body;
29370 };
29371 /** @private */
29372 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29373     var body = Roo.getDom(id);
29374     if(!body){
29375         body = document.createElement("div");
29376         body.id = id;
29377     }
29378     Roo.fly(body).addClass("x-tabs-item-body");
29379     bodyEl.insertBefore(body, bodyEl.firstChild);
29380     return body;
29381 };
29382 /** @private */
29383 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29384     var td = document.createElement("td");
29385     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29386     //stripEl.appendChild(td);
29387     if(closable){
29388         td.className = "x-tabs-closable";
29389         if(!this.closeTpl){
29390             this.closeTpl = new Roo.Template(
29391                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29392                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29393                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29394             );
29395         }
29396         var el = this.closeTpl.overwrite(td, {"text": text});
29397         var close = el.getElementsByTagName("div")[0];
29398         var inner = el.getElementsByTagName("em")[0];
29399         return {"el": el, "close": close, "inner": inner};
29400     } else {
29401         if(!this.tabTpl){
29402             this.tabTpl = new Roo.Template(
29403                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29404                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29405             );
29406         }
29407         var el = this.tabTpl.overwrite(td, {"text": text});
29408         var inner = el.getElementsByTagName("em")[0];
29409         return {"el": el, "inner": inner};
29410     }
29411 };/*
29412  * Based on:
29413  * Ext JS Library 1.1.1
29414  * Copyright(c) 2006-2007, Ext JS, LLC.
29415  *
29416  * Originally Released Under LGPL - original licence link has changed is not relivant.
29417  *
29418  * Fork - LGPL
29419  * <script type="text/javascript">
29420  */
29421
29422 /**
29423  * @class Roo.Button
29424  * @extends Roo.util.Observable
29425  * Simple Button class
29426  * @cfg {String} text The button text
29427  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29428  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29429  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29430  * @cfg {Object} scope The scope of the handler
29431  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29432  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29433  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29434  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29435  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29436  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29437    applies if enableToggle = true)
29438  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29439  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29440   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29441  * @constructor
29442  * Create a new button
29443  * @param {Object} config The config object
29444  */
29445 Roo.Button = function(renderTo, config)
29446 {
29447     if (!config) {
29448         config = renderTo;
29449         renderTo = config.renderTo || false;
29450     }
29451     
29452     Roo.apply(this, config);
29453     this.addEvents({
29454         /**
29455              * @event click
29456              * Fires when this button is clicked
29457              * @param {Button} this
29458              * @param {EventObject} e The click event
29459              */
29460             "click" : true,
29461         /**
29462              * @event toggle
29463              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29464              * @param {Button} this
29465              * @param {Boolean} pressed
29466              */
29467             "toggle" : true,
29468         /**
29469              * @event mouseover
29470              * Fires when the mouse hovers over the button
29471              * @param {Button} this
29472              * @param {Event} e The event object
29473              */
29474         'mouseover' : true,
29475         /**
29476              * @event mouseout
29477              * Fires when the mouse exits the button
29478              * @param {Button} this
29479              * @param {Event} e The event object
29480              */
29481         'mouseout': true,
29482          /**
29483              * @event render
29484              * Fires when the button is rendered
29485              * @param {Button} this
29486              */
29487         'render': true
29488     });
29489     if(this.menu){
29490         this.menu = Roo.menu.MenuMgr.get(this.menu);
29491     }
29492     // register listeners first!!  - so render can be captured..
29493     Roo.util.Observable.call(this);
29494     if(renderTo){
29495         this.render(renderTo);
29496     }
29497     
29498   
29499 };
29500
29501 Roo.extend(Roo.Button, Roo.util.Observable, {
29502     /**
29503      * 
29504      */
29505     
29506     /**
29507      * Read-only. True if this button is hidden
29508      * @type Boolean
29509      */
29510     hidden : false,
29511     /**
29512      * Read-only. True if this button is disabled
29513      * @type Boolean
29514      */
29515     disabled : false,
29516     /**
29517      * Read-only. True if this button is pressed (only if enableToggle = true)
29518      * @type Boolean
29519      */
29520     pressed : false,
29521
29522     /**
29523      * @cfg {Number} tabIndex 
29524      * The DOM tabIndex for this button (defaults to undefined)
29525      */
29526     tabIndex : undefined,
29527
29528     /**
29529      * @cfg {Boolean} enableToggle
29530      * True to enable pressed/not pressed toggling (defaults to false)
29531      */
29532     enableToggle: false,
29533     /**
29534      * @cfg {Roo.menu.Menu} menu
29535      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29536      */
29537     menu : undefined,
29538     /**
29539      * @cfg {String} menuAlign
29540      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29541      */
29542     menuAlign : "tl-bl?",
29543
29544     /**
29545      * @cfg {String} iconCls
29546      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29547      */
29548     iconCls : undefined,
29549     /**
29550      * @cfg {String} type
29551      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29552      */
29553     type : 'button',
29554
29555     // private
29556     menuClassTarget: 'tr',
29557
29558     /**
29559      * @cfg {String} clickEvent
29560      * The type of event to map to the button's event handler (defaults to 'click')
29561      */
29562     clickEvent : 'click',
29563
29564     /**
29565      * @cfg {Boolean} handleMouseEvents
29566      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29567      */
29568     handleMouseEvents : true,
29569
29570     /**
29571      * @cfg {String} tooltipType
29572      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29573      */
29574     tooltipType : 'qtip',
29575
29576     /**
29577      * @cfg {String} cls
29578      * A CSS class to apply to the button's main element.
29579      */
29580     
29581     /**
29582      * @cfg {Roo.Template} template (Optional)
29583      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29584      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29585      * require code modifications if required elements (e.g. a button) aren't present.
29586      */
29587
29588     // private
29589     render : function(renderTo){
29590         var btn;
29591         if(this.hideParent){
29592             this.parentEl = Roo.get(renderTo);
29593         }
29594         if(!this.dhconfig){
29595             if(!this.template){
29596                 if(!Roo.Button.buttonTemplate){
29597                     // hideous table template
29598                     Roo.Button.buttonTemplate = new Roo.Template(
29599                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29600                         '<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>',
29601                         "</tr></tbody></table>");
29602                 }
29603                 this.template = Roo.Button.buttonTemplate;
29604             }
29605             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29606             var btnEl = btn.child("button:first");
29607             btnEl.on('focus', this.onFocus, this);
29608             btnEl.on('blur', this.onBlur, this);
29609             if(this.cls){
29610                 btn.addClass(this.cls);
29611             }
29612             if(this.icon){
29613                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29614             }
29615             if(this.iconCls){
29616                 btnEl.addClass(this.iconCls);
29617                 if(!this.cls){
29618                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29619                 }
29620             }
29621             if(this.tabIndex !== undefined){
29622                 btnEl.dom.tabIndex = this.tabIndex;
29623             }
29624             if(this.tooltip){
29625                 if(typeof this.tooltip == 'object'){
29626                     Roo.QuickTips.tips(Roo.apply({
29627                           target: btnEl.id
29628                     }, this.tooltip));
29629                 } else {
29630                     btnEl.dom[this.tooltipType] = this.tooltip;
29631                 }
29632             }
29633         }else{
29634             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29635         }
29636         this.el = btn;
29637         if(this.id){
29638             this.el.dom.id = this.el.id = this.id;
29639         }
29640         if(this.menu){
29641             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29642             this.menu.on("show", this.onMenuShow, this);
29643             this.menu.on("hide", this.onMenuHide, this);
29644         }
29645         btn.addClass("x-btn");
29646         if(Roo.isIE && !Roo.isIE7){
29647             this.autoWidth.defer(1, this);
29648         }else{
29649             this.autoWidth();
29650         }
29651         if(this.handleMouseEvents){
29652             btn.on("mouseover", this.onMouseOver, this);
29653             btn.on("mouseout", this.onMouseOut, this);
29654             btn.on("mousedown", this.onMouseDown, this);
29655         }
29656         btn.on(this.clickEvent, this.onClick, this);
29657         //btn.on("mouseup", this.onMouseUp, this);
29658         if(this.hidden){
29659             this.hide();
29660         }
29661         if(this.disabled){
29662             this.disable();
29663         }
29664         Roo.ButtonToggleMgr.register(this);
29665         if(this.pressed){
29666             this.el.addClass("x-btn-pressed");
29667         }
29668         if(this.repeat){
29669             var repeater = new Roo.util.ClickRepeater(btn,
29670                 typeof this.repeat == "object" ? this.repeat : {}
29671             );
29672             repeater.on("click", this.onClick,  this);
29673         }
29674         
29675         this.fireEvent('render', this);
29676         
29677     },
29678     /**
29679      * Returns the button's underlying element
29680      * @return {Roo.Element} The element
29681      */
29682     getEl : function(){
29683         return this.el;  
29684     },
29685     
29686     /**
29687      * Destroys this Button and removes any listeners.
29688      */
29689     destroy : function(){
29690         Roo.ButtonToggleMgr.unregister(this);
29691         this.el.removeAllListeners();
29692         this.purgeListeners();
29693         this.el.remove();
29694     },
29695
29696     // private
29697     autoWidth : function(){
29698         if(this.el){
29699             this.el.setWidth("auto");
29700             if(Roo.isIE7 && Roo.isStrict){
29701                 var ib = this.el.child('button');
29702                 if(ib && ib.getWidth() > 20){
29703                     ib.clip();
29704                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29705                 }
29706             }
29707             if(this.minWidth){
29708                 if(this.hidden){
29709                     this.el.beginMeasure();
29710                 }
29711                 if(this.el.getWidth() < this.minWidth){
29712                     this.el.setWidth(this.minWidth);
29713                 }
29714                 if(this.hidden){
29715                     this.el.endMeasure();
29716                 }
29717             }
29718         }
29719     },
29720
29721     /**
29722      * Assigns this button's click handler
29723      * @param {Function} handler The function to call when the button is clicked
29724      * @param {Object} scope (optional) Scope for the function passed in
29725      */
29726     setHandler : function(handler, scope){
29727         this.handler = handler;
29728         this.scope = scope;  
29729     },
29730     
29731     /**
29732      * Sets this button's text
29733      * @param {String} text The button text
29734      */
29735     setText : function(text){
29736         this.text = text;
29737         if(this.el){
29738             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29739         }
29740         this.autoWidth();
29741     },
29742     
29743     /**
29744      * Gets the text for this button
29745      * @return {String} The button text
29746      */
29747     getText : function(){
29748         return this.text;  
29749     },
29750     
29751     /**
29752      * Show this button
29753      */
29754     show: function(){
29755         this.hidden = false;
29756         if(this.el){
29757             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29758         }
29759     },
29760     
29761     /**
29762      * Hide this button
29763      */
29764     hide: function(){
29765         this.hidden = true;
29766         if(this.el){
29767             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29768         }
29769     },
29770     
29771     /**
29772      * Convenience function for boolean show/hide
29773      * @param {Boolean} visible True to show, false to hide
29774      */
29775     setVisible: function(visible){
29776         if(visible) {
29777             this.show();
29778         }else{
29779             this.hide();
29780         }
29781     },
29782     
29783     /**
29784      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29785      * @param {Boolean} state (optional) Force a particular state
29786      */
29787     toggle : function(state){
29788         state = state === undefined ? !this.pressed : state;
29789         if(state != this.pressed){
29790             if(state){
29791                 this.el.addClass("x-btn-pressed");
29792                 this.pressed = true;
29793                 this.fireEvent("toggle", this, true);
29794             }else{
29795                 this.el.removeClass("x-btn-pressed");
29796                 this.pressed = false;
29797                 this.fireEvent("toggle", this, false);
29798             }
29799             if(this.toggleHandler){
29800                 this.toggleHandler.call(this.scope || this, this, state);
29801             }
29802         }
29803     },
29804     
29805     /**
29806      * Focus the button
29807      */
29808     focus : function(){
29809         this.el.child('button:first').focus();
29810     },
29811     
29812     /**
29813      * Disable this button
29814      */
29815     disable : function(){
29816         if(this.el){
29817             this.el.addClass("x-btn-disabled");
29818         }
29819         this.disabled = true;
29820     },
29821     
29822     /**
29823      * Enable this button
29824      */
29825     enable : function(){
29826         if(this.el){
29827             this.el.removeClass("x-btn-disabled");
29828         }
29829         this.disabled = false;
29830     },
29831
29832     /**
29833      * Convenience function for boolean enable/disable
29834      * @param {Boolean} enabled True to enable, false to disable
29835      */
29836     setDisabled : function(v){
29837         this[v !== true ? "enable" : "disable"]();
29838     },
29839
29840     // private
29841     onClick : function(e)
29842     {
29843         if(e){
29844             e.preventDefault();
29845         }
29846         if(e.button != 0){
29847             return;
29848         }
29849         if(!this.disabled){
29850             if(this.enableToggle){
29851                 this.toggle();
29852             }
29853             if(this.menu && !this.menu.isVisible()){
29854                 this.menu.show(this.el, this.menuAlign);
29855             }
29856             this.fireEvent("click", this, e);
29857             if(this.handler){
29858                 this.el.removeClass("x-btn-over");
29859                 this.handler.call(this.scope || this, this, e);
29860             }
29861         }
29862     },
29863     // private
29864     onMouseOver : function(e){
29865         if(!this.disabled){
29866             this.el.addClass("x-btn-over");
29867             this.fireEvent('mouseover', this, e);
29868         }
29869     },
29870     // private
29871     onMouseOut : function(e){
29872         if(!e.within(this.el,  true)){
29873             this.el.removeClass("x-btn-over");
29874             this.fireEvent('mouseout', this, e);
29875         }
29876     },
29877     // private
29878     onFocus : function(e){
29879         if(!this.disabled){
29880             this.el.addClass("x-btn-focus");
29881         }
29882     },
29883     // private
29884     onBlur : function(e){
29885         this.el.removeClass("x-btn-focus");
29886     },
29887     // private
29888     onMouseDown : function(e){
29889         if(!this.disabled && e.button == 0){
29890             this.el.addClass("x-btn-click");
29891             Roo.get(document).on('mouseup', this.onMouseUp, this);
29892         }
29893     },
29894     // private
29895     onMouseUp : function(e){
29896         if(e.button == 0){
29897             this.el.removeClass("x-btn-click");
29898             Roo.get(document).un('mouseup', this.onMouseUp, this);
29899         }
29900     },
29901     // private
29902     onMenuShow : function(e){
29903         this.el.addClass("x-btn-menu-active");
29904     },
29905     // private
29906     onMenuHide : function(e){
29907         this.el.removeClass("x-btn-menu-active");
29908     }   
29909 });
29910
29911 // Private utility class used by Button
29912 Roo.ButtonToggleMgr = function(){
29913    var groups = {};
29914    
29915    function toggleGroup(btn, state){
29916        if(state){
29917            var g = groups[btn.toggleGroup];
29918            for(var i = 0, l = g.length; i < l; i++){
29919                if(g[i] != btn){
29920                    g[i].toggle(false);
29921                }
29922            }
29923        }
29924    }
29925    
29926    return {
29927        register : function(btn){
29928            if(!btn.toggleGroup){
29929                return;
29930            }
29931            var g = groups[btn.toggleGroup];
29932            if(!g){
29933                g = groups[btn.toggleGroup] = [];
29934            }
29935            g.push(btn);
29936            btn.on("toggle", toggleGroup);
29937        },
29938        
29939        unregister : function(btn){
29940            if(!btn.toggleGroup){
29941                return;
29942            }
29943            var g = groups[btn.toggleGroup];
29944            if(g){
29945                g.remove(btn);
29946                btn.un("toggle", toggleGroup);
29947            }
29948        }
29949    };
29950 }();/*
29951  * Based on:
29952  * Ext JS Library 1.1.1
29953  * Copyright(c) 2006-2007, Ext JS, LLC.
29954  *
29955  * Originally Released Under LGPL - original licence link has changed is not relivant.
29956  *
29957  * Fork - LGPL
29958  * <script type="text/javascript">
29959  */
29960  
29961 /**
29962  * @class Roo.SplitButton
29963  * @extends Roo.Button
29964  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29965  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29966  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29967  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29968  * @cfg {String} arrowTooltip The title attribute of the arrow
29969  * @constructor
29970  * Create a new menu button
29971  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29972  * @param {Object} config The config object
29973  */
29974 Roo.SplitButton = function(renderTo, config){
29975     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29976     /**
29977      * @event arrowclick
29978      * Fires when this button's arrow is clicked
29979      * @param {SplitButton} this
29980      * @param {EventObject} e The click event
29981      */
29982     this.addEvents({"arrowclick":true});
29983 };
29984
29985 Roo.extend(Roo.SplitButton, Roo.Button, {
29986     render : function(renderTo){
29987         // this is one sweet looking template!
29988         var tpl = new Roo.Template(
29989             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29990             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29991             '<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>',
29992             "</tbody></table></td><td>",
29993             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29994             '<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>',
29995             "</tbody></table></td></tr></table>"
29996         );
29997         var btn = tpl.append(renderTo, [this.text, this.type], true);
29998         var btnEl = btn.child("button");
29999         if(this.cls){
30000             btn.addClass(this.cls);
30001         }
30002         if(this.icon){
30003             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30004         }
30005         if(this.iconCls){
30006             btnEl.addClass(this.iconCls);
30007             if(!this.cls){
30008                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30009             }
30010         }
30011         this.el = btn;
30012         if(this.handleMouseEvents){
30013             btn.on("mouseover", this.onMouseOver, this);
30014             btn.on("mouseout", this.onMouseOut, this);
30015             btn.on("mousedown", this.onMouseDown, this);
30016             btn.on("mouseup", this.onMouseUp, this);
30017         }
30018         btn.on(this.clickEvent, this.onClick, this);
30019         if(this.tooltip){
30020             if(typeof this.tooltip == 'object'){
30021                 Roo.QuickTips.tips(Roo.apply({
30022                       target: btnEl.id
30023                 }, this.tooltip));
30024             } else {
30025                 btnEl.dom[this.tooltipType] = this.tooltip;
30026             }
30027         }
30028         if(this.arrowTooltip){
30029             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30030         }
30031         if(this.hidden){
30032             this.hide();
30033         }
30034         if(this.disabled){
30035             this.disable();
30036         }
30037         if(this.pressed){
30038             this.el.addClass("x-btn-pressed");
30039         }
30040         if(Roo.isIE && !Roo.isIE7){
30041             this.autoWidth.defer(1, this);
30042         }else{
30043             this.autoWidth();
30044         }
30045         if(this.menu){
30046             this.menu.on("show", this.onMenuShow, this);
30047             this.menu.on("hide", this.onMenuHide, this);
30048         }
30049         this.fireEvent('render', this);
30050     },
30051
30052     // private
30053     autoWidth : function(){
30054         if(this.el){
30055             var tbl = this.el.child("table:first");
30056             var tbl2 = this.el.child("table:last");
30057             this.el.setWidth("auto");
30058             tbl.setWidth("auto");
30059             if(Roo.isIE7 && Roo.isStrict){
30060                 var ib = this.el.child('button:first');
30061                 if(ib && ib.getWidth() > 20){
30062                     ib.clip();
30063                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30064                 }
30065             }
30066             if(this.minWidth){
30067                 if(this.hidden){
30068                     this.el.beginMeasure();
30069                 }
30070                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30071                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30072                 }
30073                 if(this.hidden){
30074                     this.el.endMeasure();
30075                 }
30076             }
30077             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30078         } 
30079     },
30080     /**
30081      * Sets this button's click handler
30082      * @param {Function} handler The function to call when the button is clicked
30083      * @param {Object} scope (optional) Scope for the function passed above
30084      */
30085     setHandler : function(handler, scope){
30086         this.handler = handler;
30087         this.scope = scope;  
30088     },
30089     
30090     /**
30091      * Sets this button's arrow click handler
30092      * @param {Function} handler The function to call when the arrow is clicked
30093      * @param {Object} scope (optional) Scope for the function passed above
30094      */
30095     setArrowHandler : function(handler, scope){
30096         this.arrowHandler = handler;
30097         this.scope = scope;  
30098     },
30099     
30100     /**
30101      * Focus the button
30102      */
30103     focus : function(){
30104         if(this.el){
30105             this.el.child("button:first").focus();
30106         }
30107     },
30108
30109     // private
30110     onClick : function(e){
30111         e.preventDefault();
30112         if(!this.disabled){
30113             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30114                 if(this.menu && !this.menu.isVisible()){
30115                     this.menu.show(this.el, this.menuAlign);
30116                 }
30117                 this.fireEvent("arrowclick", this, e);
30118                 if(this.arrowHandler){
30119                     this.arrowHandler.call(this.scope || this, this, e);
30120                 }
30121             }else{
30122                 this.fireEvent("click", this, e);
30123                 if(this.handler){
30124                     this.handler.call(this.scope || this, this, e);
30125                 }
30126             }
30127         }
30128     },
30129     // private
30130     onMouseDown : function(e){
30131         if(!this.disabled){
30132             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30133         }
30134     },
30135     // private
30136     onMouseUp : function(e){
30137         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30138     }   
30139 });
30140
30141
30142 // backwards compat
30143 Roo.MenuButton = Roo.SplitButton;/*
30144  * Based on:
30145  * Ext JS Library 1.1.1
30146  * Copyright(c) 2006-2007, Ext JS, LLC.
30147  *
30148  * Originally Released Under LGPL - original licence link has changed is not relivant.
30149  *
30150  * Fork - LGPL
30151  * <script type="text/javascript">
30152  */
30153
30154 /**
30155  * @class Roo.Toolbar
30156  * @children   Roo.Toolbar.Item Roo.form.Field
30157  * Basic Toolbar class.
30158  * @constructor
30159  * Creates a new Toolbar
30160  * @param {Object} container The config object
30161  */ 
30162 Roo.Toolbar = function(container, buttons, config)
30163 {
30164     /// old consturctor format still supported..
30165     if(container instanceof Array){ // omit the container for later rendering
30166         buttons = container;
30167         config = buttons;
30168         container = null;
30169     }
30170     if (typeof(container) == 'object' && container.xtype) {
30171         config = container;
30172         container = config.container;
30173         buttons = config.buttons || []; // not really - use items!!
30174     }
30175     var xitems = [];
30176     if (config && config.items) {
30177         xitems = config.items;
30178         delete config.items;
30179     }
30180     Roo.apply(this, config);
30181     this.buttons = buttons;
30182     
30183     if(container){
30184         this.render(container);
30185     }
30186     this.xitems = xitems;
30187     Roo.each(xitems, function(b) {
30188         this.add(b);
30189     }, this);
30190     
30191 };
30192
30193 Roo.Toolbar.prototype = {
30194     /**
30195      * @cfg {Array} items
30196      * array of button configs or elements to add (will be converted to a MixedCollection)
30197      */
30198     items: false,
30199     /**
30200      * @cfg {String/HTMLElement/Element} container
30201      * The id or element that will contain the toolbar
30202      */
30203     // private
30204     render : function(ct){
30205         this.el = Roo.get(ct);
30206         if(this.cls){
30207             this.el.addClass(this.cls);
30208         }
30209         // using a table allows for vertical alignment
30210         // 100% width is needed by Safari...
30211         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30212         this.tr = this.el.child("tr", true);
30213         var autoId = 0;
30214         this.items = new Roo.util.MixedCollection(false, function(o){
30215             return o.id || ("item" + (++autoId));
30216         });
30217         if(this.buttons){
30218             this.add.apply(this, this.buttons);
30219             delete this.buttons;
30220         }
30221     },
30222
30223     /**
30224      * Adds element(s) to the toolbar -- this function takes a variable number of 
30225      * arguments of mixed type and adds them to the toolbar.
30226      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30227      * <ul>
30228      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30229      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30230      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30231      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30232      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30233      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30234      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30235      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30236      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30237      * </ul>
30238      * @param {Mixed} arg2
30239      * @param {Mixed} etc.
30240      */
30241     add : function(){
30242         var a = arguments, l = a.length;
30243         for(var i = 0; i < l; i++){
30244             this._add(a[i]);
30245         }
30246     },
30247     // private..
30248     _add : function(el) {
30249         
30250         if (el.xtype) {
30251             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30252         }
30253         
30254         if (el.applyTo){ // some kind of form field
30255             return this.addField(el);
30256         } 
30257         if (el.render){ // some kind of Toolbar.Item
30258             return this.addItem(el);
30259         }
30260         if (typeof el == "string"){ // string
30261             if(el == "separator" || el == "-"){
30262                 return this.addSeparator();
30263             }
30264             if (el == " "){
30265                 return this.addSpacer();
30266             }
30267             if(el == "->"){
30268                 return this.addFill();
30269             }
30270             return this.addText(el);
30271             
30272         }
30273         if(el.tagName){ // element
30274             return this.addElement(el);
30275         }
30276         if(typeof el == "object"){ // must be button config?
30277             return this.addButton(el);
30278         }
30279         // and now what?!?!
30280         return false;
30281         
30282     },
30283     
30284     /**
30285      * Add an Xtype element
30286      * @param {Object} xtype Xtype Object
30287      * @return {Object} created Object
30288      */
30289     addxtype : function(e){
30290         return this.add(e);  
30291     },
30292     
30293     /**
30294      * Returns the Element for this toolbar.
30295      * @return {Roo.Element}
30296      */
30297     getEl : function(){
30298         return this.el;  
30299     },
30300     
30301     /**
30302      * Adds a separator
30303      * @return {Roo.Toolbar.Item} The separator item
30304      */
30305     addSeparator : function(){
30306         return this.addItem(new Roo.Toolbar.Separator());
30307     },
30308
30309     /**
30310      * Adds a spacer element
30311      * @return {Roo.Toolbar.Spacer} The spacer item
30312      */
30313     addSpacer : function(){
30314         return this.addItem(new Roo.Toolbar.Spacer());
30315     },
30316
30317     /**
30318      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30319      * @return {Roo.Toolbar.Fill} The fill item
30320      */
30321     addFill : function(){
30322         return this.addItem(new Roo.Toolbar.Fill());
30323     },
30324
30325     /**
30326      * Adds any standard HTML element to the toolbar
30327      * @param {String/HTMLElement/Element} el The element or id of the element to add
30328      * @return {Roo.Toolbar.Item} The element's item
30329      */
30330     addElement : function(el){
30331         return this.addItem(new Roo.Toolbar.Item(el));
30332     },
30333     /**
30334      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30335      * @type Roo.util.MixedCollection  
30336      */
30337     items : false,
30338      
30339     /**
30340      * Adds any Toolbar.Item or subclass
30341      * @param {Roo.Toolbar.Item} item
30342      * @return {Roo.Toolbar.Item} The item
30343      */
30344     addItem : function(item){
30345         var td = this.nextBlock();
30346         item.render(td);
30347         this.items.add(item);
30348         return item;
30349     },
30350     
30351     /**
30352      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30353      * @param {Object/Array} config A button config or array of configs
30354      * @return {Roo.Toolbar.Button/Array}
30355      */
30356     addButton : function(config){
30357         if(config instanceof Array){
30358             var buttons = [];
30359             for(var i = 0, len = config.length; i < len; i++) {
30360                 buttons.push(this.addButton(config[i]));
30361             }
30362             return buttons;
30363         }
30364         var b = config;
30365         if(!(config instanceof Roo.Toolbar.Button)){
30366             b = config.split ?
30367                 new Roo.Toolbar.SplitButton(config) :
30368                 new Roo.Toolbar.Button(config);
30369         }
30370         var td = this.nextBlock();
30371         b.render(td);
30372         this.items.add(b);
30373         return b;
30374     },
30375     
30376     /**
30377      * Adds text to the toolbar
30378      * @param {String} text The text to add
30379      * @return {Roo.Toolbar.Item} The element's item
30380      */
30381     addText : function(text){
30382         return this.addItem(new Roo.Toolbar.TextItem(text));
30383     },
30384     
30385     /**
30386      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30387      * @param {Number} index The index where the item is to be inserted
30388      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30389      * @return {Roo.Toolbar.Button/Item}
30390      */
30391     insertButton : function(index, item){
30392         if(item instanceof Array){
30393             var buttons = [];
30394             for(var i = 0, len = item.length; i < len; i++) {
30395                buttons.push(this.insertButton(index + i, item[i]));
30396             }
30397             return buttons;
30398         }
30399         if (!(item instanceof Roo.Toolbar.Button)){
30400            item = new Roo.Toolbar.Button(item);
30401         }
30402         var td = document.createElement("td");
30403         this.tr.insertBefore(td, this.tr.childNodes[index]);
30404         item.render(td);
30405         this.items.insert(index, item);
30406         return item;
30407     },
30408     
30409     /**
30410      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30411      * @param {Object} config
30412      * @return {Roo.Toolbar.Item} The element's item
30413      */
30414     addDom : function(config, returnEl){
30415         var td = this.nextBlock();
30416         Roo.DomHelper.overwrite(td, config);
30417         var ti = new Roo.Toolbar.Item(td.firstChild);
30418         ti.render(td);
30419         this.items.add(ti);
30420         return ti;
30421     },
30422
30423     /**
30424      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30425      * @type Roo.util.MixedCollection  
30426      */
30427     fields : false,
30428     
30429     /**
30430      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30431      * Note: the field should not have been rendered yet. For a field that has already been
30432      * rendered, use {@link #addElement}.
30433      * @param {Roo.form.Field} field
30434      * @return {Roo.ToolbarItem}
30435      */
30436      
30437       
30438     addField : function(field) {
30439         if (!this.fields) {
30440             var autoId = 0;
30441             this.fields = new Roo.util.MixedCollection(false, function(o){
30442                 return o.id || ("item" + (++autoId));
30443             });
30444
30445         }
30446         
30447         var td = this.nextBlock();
30448         field.render(td);
30449         var ti = new Roo.Toolbar.Item(td.firstChild);
30450         ti.render(td);
30451         this.items.add(ti);
30452         this.fields.add(field);
30453         return ti;
30454     },
30455     /**
30456      * Hide the toolbar
30457      * @method hide
30458      */
30459      
30460       
30461     hide : function()
30462     {
30463         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30464         this.el.child('div').hide();
30465     },
30466     /**
30467      * Show the toolbar
30468      * @method show
30469      */
30470     show : function()
30471     {
30472         this.el.child('div').show();
30473     },
30474       
30475     // private
30476     nextBlock : function(){
30477         var td = document.createElement("td");
30478         this.tr.appendChild(td);
30479         return td;
30480     },
30481
30482     // private
30483     destroy : function(){
30484         if(this.items){ // rendered?
30485             Roo.destroy.apply(Roo, this.items.items);
30486         }
30487         if(this.fields){ // rendered?
30488             Roo.destroy.apply(Roo, this.fields.items);
30489         }
30490         Roo.Element.uncache(this.el, this.tr);
30491     }
30492 };
30493
30494 /**
30495  * @class Roo.Toolbar.Item
30496  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30497  * @constructor
30498  * Creates a new Item
30499  * @param {HTMLElement} el 
30500  */
30501 Roo.Toolbar.Item = function(el){
30502     var cfg = {};
30503     if (typeof (el.xtype) != 'undefined') {
30504         cfg = el;
30505         el = cfg.el;
30506     }
30507     
30508     this.el = Roo.getDom(el);
30509     this.id = Roo.id(this.el);
30510     this.hidden = false;
30511     
30512     this.addEvents({
30513          /**
30514              * @event render
30515              * Fires when the button is rendered
30516              * @param {Button} this
30517              */
30518         'render': true
30519     });
30520     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30521 };
30522 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30523 //Roo.Toolbar.Item.prototype = {
30524     
30525     /**
30526      * Get this item's HTML Element
30527      * @return {HTMLElement}
30528      */
30529     getEl : function(){
30530        return this.el;  
30531     },
30532
30533     // private
30534     render : function(td){
30535         
30536          this.td = td;
30537         td.appendChild(this.el);
30538         
30539         this.fireEvent('render', this);
30540     },
30541     
30542     /**
30543      * Removes and destroys this item.
30544      */
30545     destroy : function(){
30546         this.td.parentNode.removeChild(this.td);
30547     },
30548     
30549     /**
30550      * Shows this item.
30551      */
30552     show: function(){
30553         this.hidden = false;
30554         this.td.style.display = "";
30555     },
30556     
30557     /**
30558      * Hides this item.
30559      */
30560     hide: function(){
30561         this.hidden = true;
30562         this.td.style.display = "none";
30563     },
30564     
30565     /**
30566      * Convenience function for boolean show/hide.
30567      * @param {Boolean} visible true to show/false to hide
30568      */
30569     setVisible: function(visible){
30570         if(visible) {
30571             this.show();
30572         }else{
30573             this.hide();
30574         }
30575     },
30576     
30577     /**
30578      * Try to focus this item.
30579      */
30580     focus : function(){
30581         Roo.fly(this.el).focus();
30582     },
30583     
30584     /**
30585      * Disables this item.
30586      */
30587     disable : function(){
30588         Roo.fly(this.td).addClass("x-item-disabled");
30589         this.disabled = true;
30590         this.el.disabled = true;
30591     },
30592     
30593     /**
30594      * Enables this item.
30595      */
30596     enable : function(){
30597         Roo.fly(this.td).removeClass("x-item-disabled");
30598         this.disabled = false;
30599         this.el.disabled = false;
30600     }
30601 });
30602
30603
30604 /**
30605  * @class Roo.Toolbar.Separator
30606  * @extends Roo.Toolbar.Item
30607  * A simple toolbar separator class
30608  * @constructor
30609  * Creates a new Separator
30610  */
30611 Roo.Toolbar.Separator = function(cfg){
30612     
30613     var s = document.createElement("span");
30614     s.className = "ytb-sep";
30615     if (cfg) {
30616         cfg.el = s;
30617     }
30618     
30619     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30620 };
30621 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30622     enable:Roo.emptyFn,
30623     disable:Roo.emptyFn,
30624     focus:Roo.emptyFn
30625 });
30626
30627 /**
30628  * @class Roo.Toolbar.Spacer
30629  * @extends Roo.Toolbar.Item
30630  * A simple element that adds extra horizontal space to a toolbar.
30631  * @constructor
30632  * Creates a new Spacer
30633  */
30634 Roo.Toolbar.Spacer = function(cfg){
30635     var s = document.createElement("div");
30636     s.className = "ytb-spacer";
30637     if (cfg) {
30638         cfg.el = s;
30639     }
30640     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30641 };
30642 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30643     enable:Roo.emptyFn,
30644     disable:Roo.emptyFn,
30645     focus:Roo.emptyFn
30646 });
30647
30648 /**
30649  * @class Roo.Toolbar.Fill
30650  * @extends Roo.Toolbar.Spacer
30651  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30652  * @constructor
30653  * Creates a new Spacer
30654  */
30655 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30656     // private
30657     render : function(td){
30658         td.style.width = '100%';
30659         Roo.Toolbar.Fill.superclass.render.call(this, td);
30660     }
30661 });
30662
30663 /**
30664  * @class Roo.Toolbar.TextItem
30665  * @extends Roo.Toolbar.Item
30666  * A simple class that renders text directly into a toolbar.
30667  * @constructor
30668  * Creates a new TextItem
30669  * @cfg {string} text 
30670  */
30671 Roo.Toolbar.TextItem = function(cfg){
30672     var  text = cfg || "";
30673     if (typeof(cfg) == 'object') {
30674         text = cfg.text || "";
30675     }  else {
30676         cfg = null;
30677     }
30678     var s = document.createElement("span");
30679     s.className = "ytb-text";
30680     s.innerHTML = text;
30681     if (cfg) {
30682         cfg.el  = s;
30683     }
30684     
30685     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30686 };
30687 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30688     
30689      
30690     enable:Roo.emptyFn,
30691     disable:Roo.emptyFn,
30692     focus:Roo.emptyFn
30693 });
30694
30695 /**
30696  * @class Roo.Toolbar.Button
30697  * @extends Roo.Button
30698  * A button that renders into a toolbar.
30699  * @constructor
30700  * Creates a new Button
30701  * @param {Object} config A standard {@link Roo.Button} config object
30702  */
30703 Roo.Toolbar.Button = function(config){
30704     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30705 };
30706 Roo.extend(Roo.Toolbar.Button, Roo.Button,
30707 {
30708     
30709     
30710     render : function(td){
30711         this.td = td;
30712         Roo.Toolbar.Button.superclass.render.call(this, td);
30713     },
30714     
30715     /**
30716      * Removes and destroys this button
30717      */
30718     destroy : function(){
30719         Roo.Toolbar.Button.superclass.destroy.call(this);
30720         this.td.parentNode.removeChild(this.td);
30721     },
30722     
30723     /**
30724      * Shows this button
30725      */
30726     show: function(){
30727         this.hidden = false;
30728         this.td.style.display = "";
30729     },
30730     
30731     /**
30732      * Hides this button
30733      */
30734     hide: function(){
30735         this.hidden = true;
30736         this.td.style.display = "none";
30737     },
30738
30739     /**
30740      * Disables this item
30741      */
30742     disable : function(){
30743         Roo.fly(this.td).addClass("x-item-disabled");
30744         this.disabled = true;
30745     },
30746
30747     /**
30748      * Enables this item
30749      */
30750     enable : function(){
30751         Roo.fly(this.td).removeClass("x-item-disabled");
30752         this.disabled = false;
30753     }
30754 });
30755 // backwards compat
30756 Roo.ToolbarButton = Roo.Toolbar.Button;
30757
30758 /**
30759  * @class Roo.Toolbar.SplitButton
30760  * @extends Roo.SplitButton
30761  * A menu button that renders into a toolbar.
30762  * @constructor
30763  * Creates a new SplitButton
30764  * @param {Object} config A standard {@link Roo.SplitButton} config object
30765  */
30766 Roo.Toolbar.SplitButton = function(config){
30767     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30768 };
30769 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30770     render : function(td){
30771         this.td = td;
30772         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30773     },
30774     
30775     /**
30776      * Removes and destroys this button
30777      */
30778     destroy : function(){
30779         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30780         this.td.parentNode.removeChild(this.td);
30781     },
30782     
30783     /**
30784      * Shows this button
30785      */
30786     show: function(){
30787         this.hidden = false;
30788         this.td.style.display = "";
30789     },
30790     
30791     /**
30792      * Hides this button
30793      */
30794     hide: function(){
30795         this.hidden = true;
30796         this.td.style.display = "none";
30797     }
30798 });
30799
30800 // backwards compat
30801 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30802  * Based on:
30803  * Ext JS Library 1.1.1
30804  * Copyright(c) 2006-2007, Ext JS, LLC.
30805  *
30806  * Originally Released Under LGPL - original licence link has changed is not relivant.
30807  *
30808  * Fork - LGPL
30809  * <script type="text/javascript">
30810  */
30811  
30812 /**
30813  * @class Roo.PagingToolbar
30814  * @extends Roo.Toolbar
30815  * @children   Roo.Toolbar.Item Roo.form.Field
30816  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30817  * @constructor
30818  * Create a new PagingToolbar
30819  * @param {Object} config The config object
30820  */
30821 Roo.PagingToolbar = function(el, ds, config)
30822 {
30823     // old args format still supported... - xtype is prefered..
30824     if (typeof(el) == 'object' && el.xtype) {
30825         // created from xtype...
30826         config = el;
30827         ds = el.dataSource;
30828         el = config.container;
30829     }
30830     var items = [];
30831     if (config.items) {
30832         items = config.items;
30833         config.items = [];
30834     }
30835     
30836     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30837     this.ds = ds;
30838     this.cursor = 0;
30839     this.renderButtons(this.el);
30840     this.bind(ds);
30841     
30842     // supprot items array.
30843    
30844     Roo.each(items, function(e) {
30845         this.add(Roo.factory(e));
30846     },this);
30847     
30848 };
30849
30850 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30851     /**
30852      * @cfg {Roo.data.Store} dataSource
30853      * The underlying data store providing the paged data
30854      */
30855     /**
30856      * @cfg {String/HTMLElement/Element} container
30857      * container The id or element that will contain the toolbar
30858      */
30859     /**
30860      * @cfg {Boolean} displayInfo
30861      * True to display the displayMsg (defaults to false)
30862      */
30863     /**
30864      * @cfg {Number} pageSize
30865      * The number of records to display per page (defaults to 20)
30866      */
30867     pageSize: 20,
30868     /**
30869      * @cfg {String} displayMsg
30870      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30871      */
30872     displayMsg : 'Displaying {0} - {1} of {2}',
30873     /**
30874      * @cfg {String} emptyMsg
30875      * The message to display when no records are found (defaults to "No data to display")
30876      */
30877     emptyMsg : 'No data to display',
30878     /**
30879      * Customizable piece of the default paging text (defaults to "Page")
30880      * @type String
30881      */
30882     beforePageText : "Page",
30883     /**
30884      * Customizable piece of the default paging text (defaults to "of %0")
30885      * @type String
30886      */
30887     afterPageText : "of {0}",
30888     /**
30889      * Customizable piece of the default paging text (defaults to "First Page")
30890      * @type String
30891      */
30892     firstText : "First Page",
30893     /**
30894      * Customizable piece of the default paging text (defaults to "Previous Page")
30895      * @type String
30896      */
30897     prevText : "Previous Page",
30898     /**
30899      * Customizable piece of the default paging text (defaults to "Next Page")
30900      * @type String
30901      */
30902     nextText : "Next Page",
30903     /**
30904      * Customizable piece of the default paging text (defaults to "Last Page")
30905      * @type String
30906      */
30907     lastText : "Last Page",
30908     /**
30909      * Customizable piece of the default paging text (defaults to "Refresh")
30910      * @type String
30911      */
30912     refreshText : "Refresh",
30913
30914     // private
30915     renderButtons : function(el){
30916         Roo.PagingToolbar.superclass.render.call(this, el);
30917         this.first = this.addButton({
30918             tooltip: this.firstText,
30919             cls: "x-btn-icon x-grid-page-first",
30920             disabled: true,
30921             handler: this.onClick.createDelegate(this, ["first"])
30922         });
30923         this.prev = this.addButton({
30924             tooltip: this.prevText,
30925             cls: "x-btn-icon x-grid-page-prev",
30926             disabled: true,
30927             handler: this.onClick.createDelegate(this, ["prev"])
30928         });
30929         //this.addSeparator();
30930         this.add(this.beforePageText);
30931         this.field = Roo.get(this.addDom({
30932            tag: "input",
30933            type: "text",
30934            size: "3",
30935            value: "1",
30936            cls: "x-grid-page-number"
30937         }).el);
30938         this.field.on("keydown", this.onPagingKeydown, this);
30939         this.field.on("focus", function(){this.dom.select();});
30940         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30941         this.field.setHeight(18);
30942         //this.addSeparator();
30943         this.next = this.addButton({
30944             tooltip: this.nextText,
30945             cls: "x-btn-icon x-grid-page-next",
30946             disabled: true,
30947             handler: this.onClick.createDelegate(this, ["next"])
30948         });
30949         this.last = this.addButton({
30950             tooltip: this.lastText,
30951             cls: "x-btn-icon x-grid-page-last",
30952             disabled: true,
30953             handler: this.onClick.createDelegate(this, ["last"])
30954         });
30955         //this.addSeparator();
30956         this.loading = this.addButton({
30957             tooltip: this.refreshText,
30958             cls: "x-btn-icon x-grid-loading",
30959             handler: this.onClick.createDelegate(this, ["refresh"])
30960         });
30961
30962         if(this.displayInfo){
30963             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30964         }
30965     },
30966
30967     // private
30968     updateInfo : function(){
30969         if(this.displayEl){
30970             var count = this.ds.getCount();
30971             var msg = count == 0 ?
30972                 this.emptyMsg :
30973                 String.format(
30974                     this.displayMsg,
30975                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30976                 );
30977             this.displayEl.update(msg);
30978         }
30979     },
30980
30981     // private
30982     onLoad : function(ds, r, o){
30983        this.cursor = o.params ? o.params.start : 0;
30984        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30985
30986        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30987        this.field.dom.value = ap;
30988        this.first.setDisabled(ap == 1);
30989        this.prev.setDisabled(ap == 1);
30990        this.next.setDisabled(ap == ps);
30991        this.last.setDisabled(ap == ps);
30992        this.loading.enable();
30993        this.updateInfo();
30994     },
30995
30996     // private
30997     getPageData : function(){
30998         var total = this.ds.getTotalCount();
30999         return {
31000             total : total,
31001             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31002             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31003         };
31004     },
31005
31006     // private
31007     onLoadError : function(){
31008         this.loading.enable();
31009     },
31010
31011     // private
31012     onPagingKeydown : function(e){
31013         var k = e.getKey();
31014         var d = this.getPageData();
31015         if(k == e.RETURN){
31016             var v = this.field.dom.value, pageNum;
31017             if(!v || isNaN(pageNum = parseInt(v, 10))){
31018                 this.field.dom.value = d.activePage;
31019                 return;
31020             }
31021             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31022             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31023             e.stopEvent();
31024         }
31025         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))
31026         {
31027           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31028           this.field.dom.value = pageNum;
31029           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31030           e.stopEvent();
31031         }
31032         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31033         {
31034           var v = this.field.dom.value, pageNum; 
31035           var increment = (e.shiftKey) ? 10 : 1;
31036           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31037             increment *= -1;
31038           }
31039           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31040             this.field.dom.value = d.activePage;
31041             return;
31042           }
31043           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31044           {
31045             this.field.dom.value = parseInt(v, 10) + increment;
31046             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31047             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31048           }
31049           e.stopEvent();
31050         }
31051     },
31052
31053     // private
31054     beforeLoad : function(){
31055         if(this.loading){
31056             this.loading.disable();
31057         }
31058     },
31059
31060     // private
31061     onClick : function(which){
31062         var ds = this.ds;
31063         switch(which){
31064             case "first":
31065                 ds.load({params:{start: 0, limit: this.pageSize}});
31066             break;
31067             case "prev":
31068                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31069             break;
31070             case "next":
31071                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31072             break;
31073             case "last":
31074                 var total = ds.getTotalCount();
31075                 var extra = total % this.pageSize;
31076                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31077                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31078             break;
31079             case "refresh":
31080                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31081             break;
31082         }
31083     },
31084
31085     /**
31086      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31087      * @param {Roo.data.Store} store The data store to unbind
31088      */
31089     unbind : function(ds){
31090         ds.un("beforeload", this.beforeLoad, this);
31091         ds.un("load", this.onLoad, this);
31092         ds.un("loadexception", this.onLoadError, this);
31093         ds.un("remove", this.updateInfo, this);
31094         ds.un("add", this.updateInfo, this);
31095         this.ds = undefined;
31096     },
31097
31098     /**
31099      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31100      * @param {Roo.data.Store} store The data store to bind
31101      */
31102     bind : function(ds){
31103         ds.on("beforeload", this.beforeLoad, this);
31104         ds.on("load", this.onLoad, this);
31105         ds.on("loadexception", this.onLoadError, this);
31106         ds.on("remove", this.updateInfo, this);
31107         ds.on("add", this.updateInfo, this);
31108         this.ds = ds;
31109     }
31110 });/*
31111  * Based on:
31112  * Ext JS Library 1.1.1
31113  * Copyright(c) 2006-2007, Ext JS, LLC.
31114  *
31115  * Originally Released Under LGPL - original licence link has changed is not relivant.
31116  *
31117  * Fork - LGPL
31118  * <script type="text/javascript">
31119  */
31120
31121 /**
31122  * @class Roo.Resizable
31123  * @extends Roo.util.Observable
31124  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31125  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31126  * 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
31127  * the element will be wrapped for you automatically.</p>
31128  * <p>Here is the list of valid resize handles:</p>
31129  * <pre>
31130 Value   Description
31131 ------  -------------------
31132  'n'     north
31133  's'     south
31134  'e'     east
31135  'w'     west
31136  'nw'    northwest
31137  'sw'    southwest
31138  'se'    southeast
31139  'ne'    northeast
31140  'hd'    horizontal drag
31141  'all'   all
31142 </pre>
31143  * <p>Here's an example showing the creation of a typical Resizable:</p>
31144  * <pre><code>
31145 var resizer = new Roo.Resizable("element-id", {
31146     handles: 'all',
31147     minWidth: 200,
31148     minHeight: 100,
31149     maxWidth: 500,
31150     maxHeight: 400,
31151     pinned: true
31152 });
31153 resizer.on("resize", myHandler);
31154 </code></pre>
31155  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31156  * resizer.east.setDisplayed(false);</p>
31157  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31158  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31159  * resize operation's new size (defaults to [0, 0])
31160  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31161  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31162  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31163  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31164  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31165  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31166  * @cfg {Number} width The width of the element in pixels (defaults to null)
31167  * @cfg {Number} height The height of the element in pixels (defaults to null)
31168  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31169  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31170  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31171  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31172  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31173  * in favor of the handles config option (defaults to false)
31174  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31175  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31176  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31177  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31178  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31179  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31180  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31181  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31182  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31183  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31184  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31185  * @constructor
31186  * Create a new resizable component
31187  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31188  * @param {Object} config configuration options
31189   */
31190 Roo.Resizable = function(el, config)
31191 {
31192     this.el = Roo.get(el);
31193
31194     if(config && config.wrap){
31195         config.resizeChild = this.el;
31196         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31197         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31198         this.el.setStyle("overflow", "hidden");
31199         this.el.setPositioning(config.resizeChild.getPositioning());
31200         config.resizeChild.clearPositioning();
31201         if(!config.width || !config.height){
31202             var csize = config.resizeChild.getSize();
31203             this.el.setSize(csize.width, csize.height);
31204         }
31205         if(config.pinned && !config.adjustments){
31206             config.adjustments = "auto";
31207         }
31208     }
31209
31210     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31211     this.proxy.unselectable();
31212     this.proxy.enableDisplayMode('block');
31213
31214     Roo.apply(this, config);
31215
31216     if(this.pinned){
31217         this.disableTrackOver = true;
31218         this.el.addClass("x-resizable-pinned");
31219     }
31220     // if the element isn't positioned, make it relative
31221     var position = this.el.getStyle("position");
31222     if(position != "absolute" && position != "fixed"){
31223         this.el.setStyle("position", "relative");
31224     }
31225     if(!this.handles){ // no handles passed, must be legacy style
31226         this.handles = 's,e,se';
31227         if(this.multiDirectional){
31228             this.handles += ',n,w';
31229         }
31230     }
31231     if(this.handles == "all"){
31232         this.handles = "n s e w ne nw se sw";
31233     }
31234     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31235     var ps = Roo.Resizable.positions;
31236     for(var i = 0, len = hs.length; i < len; i++){
31237         if(hs[i] && ps[hs[i]]){
31238             var pos = ps[hs[i]];
31239             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31240         }
31241     }
31242     // legacy
31243     this.corner = this.southeast;
31244     
31245     // updateBox = the box can move..
31246     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31247         this.updateBox = true;
31248     }
31249
31250     this.activeHandle = null;
31251
31252     if(this.resizeChild){
31253         if(typeof this.resizeChild == "boolean"){
31254             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31255         }else{
31256             this.resizeChild = Roo.get(this.resizeChild, true);
31257         }
31258     }
31259     
31260     if(this.adjustments == "auto"){
31261         var rc = this.resizeChild;
31262         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31263         if(rc && (hw || hn)){
31264             rc.position("relative");
31265             rc.setLeft(hw ? hw.el.getWidth() : 0);
31266             rc.setTop(hn ? hn.el.getHeight() : 0);
31267         }
31268         this.adjustments = [
31269             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31270             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31271         ];
31272     }
31273
31274     if(this.draggable){
31275         this.dd = this.dynamic ?
31276             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31277         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31278     }
31279
31280     // public events
31281     this.addEvents({
31282         /**
31283          * @event beforeresize
31284          * Fired before resize is allowed. Set enabled to false to cancel resize.
31285          * @param {Roo.Resizable} this
31286          * @param {Roo.EventObject} e The mousedown event
31287          */
31288         "beforeresize" : true,
31289         /**
31290          * @event resizing
31291          * Fired a resizing.
31292          * @param {Roo.Resizable} this
31293          * @param {Number} x The new x position
31294          * @param {Number} y The new y position
31295          * @param {Number} w The new w width
31296          * @param {Number} h The new h hight
31297          * @param {Roo.EventObject} e The mouseup event
31298          */
31299         "resizing" : true,
31300         /**
31301          * @event resize
31302          * Fired after a resize.
31303          * @param {Roo.Resizable} this
31304          * @param {Number} width The new width
31305          * @param {Number} height The new height
31306          * @param {Roo.EventObject} e The mouseup event
31307          */
31308         "resize" : true
31309     });
31310
31311     if(this.width !== null && this.height !== null){
31312         this.resizeTo(this.width, this.height);
31313     }else{
31314         this.updateChildSize();
31315     }
31316     if(Roo.isIE){
31317         this.el.dom.style.zoom = 1;
31318     }
31319     Roo.Resizable.superclass.constructor.call(this);
31320 };
31321
31322 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31323         resizeChild : false,
31324         adjustments : [0, 0],
31325         minWidth : 5,
31326         minHeight : 5,
31327         maxWidth : 10000,
31328         maxHeight : 10000,
31329         enabled : true,
31330         animate : false,
31331         duration : .35,
31332         dynamic : false,
31333         handles : false,
31334         multiDirectional : false,
31335         disableTrackOver : false,
31336         easing : 'easeOutStrong',
31337         widthIncrement : 0,
31338         heightIncrement : 0,
31339         pinned : false,
31340         width : null,
31341         height : null,
31342         preserveRatio : false,
31343         transparent: false,
31344         minX: 0,
31345         minY: 0,
31346         draggable: false,
31347
31348         /**
31349          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31350          */
31351         constrainTo: undefined,
31352         /**
31353          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31354          */
31355         resizeRegion: undefined,
31356
31357
31358     /**
31359      * Perform a manual resize
31360      * @param {Number} width
31361      * @param {Number} height
31362      */
31363     resizeTo : function(width, height){
31364         this.el.setSize(width, height);
31365         this.updateChildSize();
31366         this.fireEvent("resize", this, width, height, null);
31367     },
31368
31369     // private
31370     startSizing : function(e, handle){
31371         this.fireEvent("beforeresize", this, e);
31372         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31373
31374             if(!this.overlay){
31375                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31376                 this.overlay.unselectable();
31377                 this.overlay.enableDisplayMode("block");
31378                 this.overlay.on("mousemove", this.onMouseMove, this);
31379                 this.overlay.on("mouseup", this.onMouseUp, this);
31380             }
31381             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31382
31383             this.resizing = true;
31384             this.startBox = this.el.getBox();
31385             this.startPoint = e.getXY();
31386             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31387                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31388
31389             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31390             this.overlay.show();
31391
31392             if(this.constrainTo) {
31393                 var ct = Roo.get(this.constrainTo);
31394                 this.resizeRegion = ct.getRegion().adjust(
31395                     ct.getFrameWidth('t'),
31396                     ct.getFrameWidth('l'),
31397                     -ct.getFrameWidth('b'),
31398                     -ct.getFrameWidth('r')
31399                 );
31400             }
31401
31402             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31403             this.proxy.show();
31404             this.proxy.setBox(this.startBox);
31405             if(!this.dynamic){
31406                 this.proxy.setStyle('visibility', 'visible');
31407             }
31408         }
31409     },
31410
31411     // private
31412     onMouseDown : function(handle, e){
31413         if(this.enabled){
31414             e.stopEvent();
31415             this.activeHandle = handle;
31416             this.startSizing(e, handle);
31417         }
31418     },
31419
31420     // private
31421     onMouseUp : function(e){
31422         var size = this.resizeElement();
31423         this.resizing = false;
31424         this.handleOut();
31425         this.overlay.hide();
31426         this.proxy.hide();
31427         this.fireEvent("resize", this, size.width, size.height, e);
31428     },
31429
31430     // private
31431     updateChildSize : function(){
31432         
31433         if(this.resizeChild){
31434             var el = this.el;
31435             var child = this.resizeChild;
31436             var adj = this.adjustments;
31437             if(el.dom.offsetWidth){
31438                 var b = el.getSize(true);
31439                 child.setSize(b.width+adj[0], b.height+adj[1]);
31440             }
31441             // Second call here for IE
31442             // The first call enables instant resizing and
31443             // the second call corrects scroll bars if they
31444             // exist
31445             if(Roo.isIE){
31446                 setTimeout(function(){
31447                     if(el.dom.offsetWidth){
31448                         var b = el.getSize(true);
31449                         child.setSize(b.width+adj[0], b.height+adj[1]);
31450                     }
31451                 }, 10);
31452             }
31453         }
31454     },
31455
31456     // private
31457     snap : function(value, inc, min){
31458         if(!inc || !value) {
31459             return value;
31460         }
31461         var newValue = value;
31462         var m = value % inc;
31463         if(m > 0){
31464             if(m > (inc/2)){
31465                 newValue = value + (inc-m);
31466             }else{
31467                 newValue = value - m;
31468             }
31469         }
31470         return Math.max(min, newValue);
31471     },
31472
31473     // private
31474     resizeElement : function(){
31475         var box = this.proxy.getBox();
31476         if(this.updateBox){
31477             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31478         }else{
31479             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31480         }
31481         this.updateChildSize();
31482         if(!this.dynamic){
31483             this.proxy.hide();
31484         }
31485         return box;
31486     },
31487
31488     // private
31489     constrain : function(v, diff, m, mx){
31490         if(v - diff < m){
31491             diff = v - m;
31492         }else if(v - diff > mx){
31493             diff = mx - v;
31494         }
31495         return diff;
31496     },
31497
31498     // private
31499     onMouseMove : function(e){
31500         
31501         if(this.enabled){
31502             try{// try catch so if something goes wrong the user doesn't get hung
31503
31504             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31505                 return;
31506             }
31507
31508             //var curXY = this.startPoint;
31509             var curSize = this.curSize || this.startBox;
31510             var x = this.startBox.x, y = this.startBox.y;
31511             var ox = x, oy = y;
31512             var w = curSize.width, h = curSize.height;
31513             var ow = w, oh = h;
31514             var mw = this.minWidth, mh = this.minHeight;
31515             var mxw = this.maxWidth, mxh = this.maxHeight;
31516             var wi = this.widthIncrement;
31517             var hi = this.heightIncrement;
31518
31519             var eventXY = e.getXY();
31520             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31521             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31522
31523             var pos = this.activeHandle.position;
31524
31525             switch(pos){
31526                 case "east":
31527                     w += diffX;
31528                     w = Math.min(Math.max(mw, w), mxw);
31529                     break;
31530              
31531                 case "south":
31532                     h += diffY;
31533                     h = Math.min(Math.max(mh, h), mxh);
31534                     break;
31535                 case "southeast":
31536                     w += diffX;
31537                     h += diffY;
31538                     w = Math.min(Math.max(mw, w), mxw);
31539                     h = Math.min(Math.max(mh, h), mxh);
31540                     break;
31541                 case "north":
31542                     diffY = this.constrain(h, diffY, mh, mxh);
31543                     y += diffY;
31544                     h -= diffY;
31545                     break;
31546                 case "hdrag":
31547                     
31548                     if (wi) {
31549                         var adiffX = Math.abs(diffX);
31550                         var sub = (adiffX % wi); // how much 
31551                         if (sub > (wi/2)) { // far enough to snap
31552                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31553                         } else {
31554                             // remove difference.. 
31555                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31556                         }
31557                     }
31558                     x += diffX;
31559                     x = Math.max(this.minX, x);
31560                     break;
31561                 case "west":
31562                     diffX = this.constrain(w, diffX, mw, mxw);
31563                     x += diffX;
31564                     w -= diffX;
31565                     break;
31566                 case "northeast":
31567                     w += diffX;
31568                     w = Math.min(Math.max(mw, w), mxw);
31569                     diffY = this.constrain(h, diffY, mh, mxh);
31570                     y += diffY;
31571                     h -= diffY;
31572                     break;
31573                 case "northwest":
31574                     diffX = this.constrain(w, diffX, mw, mxw);
31575                     diffY = this.constrain(h, diffY, mh, mxh);
31576                     y += diffY;
31577                     h -= diffY;
31578                     x += diffX;
31579                     w -= diffX;
31580                     break;
31581                case "southwest":
31582                     diffX = this.constrain(w, diffX, mw, mxw);
31583                     h += diffY;
31584                     h = Math.min(Math.max(mh, h), mxh);
31585                     x += diffX;
31586                     w -= diffX;
31587                     break;
31588             }
31589
31590             var sw = this.snap(w, wi, mw);
31591             var sh = this.snap(h, hi, mh);
31592             if(sw != w || sh != h){
31593                 switch(pos){
31594                     case "northeast":
31595                         y -= sh - h;
31596                     break;
31597                     case "north":
31598                         y -= sh - h;
31599                         break;
31600                     case "southwest":
31601                         x -= sw - w;
31602                     break;
31603                     case "west":
31604                         x -= sw - w;
31605                         break;
31606                     case "northwest":
31607                         x -= sw - w;
31608                         y -= sh - h;
31609                     break;
31610                 }
31611                 w = sw;
31612                 h = sh;
31613             }
31614
31615             if(this.preserveRatio){
31616                 switch(pos){
31617                     case "southeast":
31618                     case "east":
31619                         h = oh * (w/ow);
31620                         h = Math.min(Math.max(mh, h), mxh);
31621                         w = ow * (h/oh);
31622                        break;
31623                     case "south":
31624                         w = ow * (h/oh);
31625                         w = Math.min(Math.max(mw, w), mxw);
31626                         h = oh * (w/ow);
31627                         break;
31628                     case "northeast":
31629                         w = ow * (h/oh);
31630                         w = Math.min(Math.max(mw, w), mxw);
31631                         h = oh * (w/ow);
31632                     break;
31633                     case "north":
31634                         var tw = w;
31635                         w = ow * (h/oh);
31636                         w = Math.min(Math.max(mw, w), mxw);
31637                         h = oh * (w/ow);
31638                         x += (tw - w) / 2;
31639                         break;
31640                     case "southwest":
31641                         h = oh * (w/ow);
31642                         h = Math.min(Math.max(mh, h), mxh);
31643                         var tw = w;
31644                         w = ow * (h/oh);
31645                         x += tw - w;
31646                         break;
31647                     case "west":
31648                         var th = h;
31649                         h = oh * (w/ow);
31650                         h = Math.min(Math.max(mh, h), mxh);
31651                         y += (th - h) / 2;
31652                         var tw = w;
31653                         w = ow * (h/oh);
31654                         x += tw - w;
31655                        break;
31656                     case "northwest":
31657                         var tw = w;
31658                         var th = h;
31659                         h = oh * (w/ow);
31660                         h = Math.min(Math.max(mh, h), mxh);
31661                         w = ow * (h/oh);
31662                         y += th - h;
31663                         x += tw - w;
31664                        break;
31665
31666                 }
31667             }
31668             if (pos == 'hdrag') {
31669                 w = ow;
31670             }
31671             this.proxy.setBounds(x, y, w, h);
31672             if(this.dynamic){
31673                 this.resizeElement();
31674             }
31675             }catch(e){}
31676         }
31677         this.fireEvent("resizing", this, x, y, w, h, e);
31678     },
31679
31680     // private
31681     handleOver : function(){
31682         if(this.enabled){
31683             this.el.addClass("x-resizable-over");
31684         }
31685     },
31686
31687     // private
31688     handleOut : function(){
31689         if(!this.resizing){
31690             this.el.removeClass("x-resizable-over");
31691         }
31692     },
31693
31694     /**
31695      * Returns the element this component is bound to.
31696      * @return {Roo.Element}
31697      */
31698     getEl : function(){
31699         return this.el;
31700     },
31701
31702     /**
31703      * Returns the resizeChild element (or null).
31704      * @return {Roo.Element}
31705      */
31706     getResizeChild : function(){
31707         return this.resizeChild;
31708     },
31709     groupHandler : function()
31710     {
31711         
31712     },
31713     /**
31714      * Destroys this resizable. If the element was wrapped and
31715      * removeEl is not true then the element remains.
31716      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31717      */
31718     destroy : function(removeEl){
31719         this.proxy.remove();
31720         if(this.overlay){
31721             this.overlay.removeAllListeners();
31722             this.overlay.remove();
31723         }
31724         var ps = Roo.Resizable.positions;
31725         for(var k in ps){
31726             if(typeof ps[k] != "function" && this[ps[k]]){
31727                 var h = this[ps[k]];
31728                 h.el.removeAllListeners();
31729                 h.el.remove();
31730             }
31731         }
31732         if(removeEl){
31733             this.el.update("");
31734             this.el.remove();
31735         }
31736     }
31737 });
31738
31739 // private
31740 // hash to map config positions to true positions
31741 Roo.Resizable.positions = {
31742     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31743     hd: "hdrag"
31744 };
31745
31746 // private
31747 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31748     if(!this.tpl){
31749         // only initialize the template if resizable is used
31750         var tpl = Roo.DomHelper.createTemplate(
31751             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31752         );
31753         tpl.compile();
31754         Roo.Resizable.Handle.prototype.tpl = tpl;
31755     }
31756     this.position = pos;
31757     this.rz = rz;
31758     // show north drag fro topdra
31759     var handlepos = pos == 'hdrag' ? 'north' : pos;
31760     
31761     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31762     if (pos == 'hdrag') {
31763         this.el.setStyle('cursor', 'pointer');
31764     }
31765     this.el.unselectable();
31766     if(transparent){
31767         this.el.setOpacity(0);
31768     }
31769     this.el.on("mousedown", this.onMouseDown, this);
31770     if(!disableTrackOver){
31771         this.el.on("mouseover", this.onMouseOver, this);
31772         this.el.on("mouseout", this.onMouseOut, this);
31773     }
31774 };
31775
31776 // private
31777 Roo.Resizable.Handle.prototype = {
31778     afterResize : function(rz){
31779         Roo.log('after?');
31780         // do nothing
31781     },
31782     // private
31783     onMouseDown : function(e){
31784         this.rz.onMouseDown(this, e);
31785     },
31786     // private
31787     onMouseOver : function(e){
31788         this.rz.handleOver(this, e);
31789     },
31790     // private
31791     onMouseOut : function(e){
31792         this.rz.handleOut(this, e);
31793     }
31794 };/*
31795  * Based on:
31796  * Ext JS Library 1.1.1
31797  * Copyright(c) 2006-2007, Ext JS, LLC.
31798  *
31799  * Originally Released Under LGPL - original licence link has changed is not relivant.
31800  *
31801  * Fork - LGPL
31802  * <script type="text/javascript">
31803  */
31804
31805 /**
31806  * @class Roo.Editor
31807  * @extends Roo.Component
31808  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31809  * @constructor
31810  * Create a new Editor
31811  * @param {Roo.form.Field} field The Field object (or descendant)
31812  * @param {Object} config The config object
31813  */
31814 Roo.Editor = function(field, config){
31815     Roo.Editor.superclass.constructor.call(this, config);
31816     this.field = field;
31817     this.addEvents({
31818         /**
31819              * @event beforestartedit
31820              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31821              * false from the handler of this event.
31822              * @param {Editor} this
31823              * @param {Roo.Element} boundEl The underlying element bound to this editor
31824              * @param {Mixed} value The field value being set
31825              */
31826         "beforestartedit" : true,
31827         /**
31828              * @event startedit
31829              * Fires when this editor is displayed
31830              * @param {Roo.Element} boundEl The underlying element bound to this editor
31831              * @param {Mixed} value The starting field value
31832              */
31833         "startedit" : true,
31834         /**
31835              * @event beforecomplete
31836              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31837              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31838              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31839              * event will not fire since no edit actually occurred.
31840              * @param {Editor} this
31841              * @param {Mixed} value The current field value
31842              * @param {Mixed} startValue The original field value
31843              */
31844         "beforecomplete" : true,
31845         /**
31846              * @event complete
31847              * Fires after editing is complete and any changed value has been written to the underlying field.
31848              * @param {Editor} this
31849              * @param {Mixed} value The current field value
31850              * @param {Mixed} startValue The original field value
31851              */
31852         "complete" : true,
31853         /**
31854          * @event specialkey
31855          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31856          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31857          * @param {Roo.form.Field} this
31858          * @param {Roo.EventObject} e The event object
31859          */
31860         "specialkey" : true
31861     });
31862 };
31863
31864 Roo.extend(Roo.Editor, Roo.Component, {
31865     /**
31866      * @cfg {Boolean/String} autosize
31867      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31868      * or "height" to adopt the height only (defaults to false)
31869      */
31870     /**
31871      * @cfg {Boolean} revertInvalid
31872      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31873      * validation fails (defaults to true)
31874      */
31875     /**
31876      * @cfg {Boolean} ignoreNoChange
31877      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31878      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31879      * will never be ignored.
31880      */
31881     /**
31882      * @cfg {Boolean} hideEl
31883      * False to keep the bound element visible while the editor is displayed (defaults to true)
31884      */
31885     /**
31886      * @cfg {Mixed} value
31887      * The data value of the underlying field (defaults to "")
31888      */
31889     value : "",
31890     /**
31891      * @cfg {String} alignment
31892      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31893      */
31894     alignment: "c-c?",
31895     /**
31896      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31897      * for bottom-right shadow (defaults to "frame")
31898      */
31899     shadow : "frame",
31900     /**
31901      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31902      */
31903     constrain : false,
31904     /**
31905      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31906      */
31907     completeOnEnter : false,
31908     /**
31909      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31910      */
31911     cancelOnEsc : false,
31912     /**
31913      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31914      */
31915     updateEl : false,
31916
31917     // private
31918     onRender : function(ct, position){
31919         this.el = new Roo.Layer({
31920             shadow: this.shadow,
31921             cls: "x-editor",
31922             parentEl : ct,
31923             shim : this.shim,
31924             shadowOffset:4,
31925             id: this.id,
31926             constrain: this.constrain
31927         });
31928         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31929         if(this.field.msgTarget != 'title'){
31930             this.field.msgTarget = 'qtip';
31931         }
31932         this.field.render(this.el);
31933         if(Roo.isGecko){
31934             this.field.el.dom.setAttribute('autocomplete', 'off');
31935         }
31936         this.field.on("specialkey", this.onSpecialKey, this);
31937         if(this.swallowKeys){
31938             this.field.el.swallowEvent(['keydown','keypress']);
31939         }
31940         this.field.show();
31941         this.field.on("blur", this.onBlur, this);
31942         if(this.field.grow){
31943             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31944         }
31945     },
31946
31947     onSpecialKey : function(field, e)
31948     {
31949         //Roo.log('editor onSpecialKey');
31950         if(this.completeOnEnter && e.getKey() == e.ENTER){
31951             e.stopEvent();
31952             this.completeEdit();
31953             return;
31954         }
31955         // do not fire special key otherwise it might hide close the editor...
31956         if(e.getKey() == e.ENTER){    
31957             return;
31958         }
31959         if(this.cancelOnEsc && e.getKey() == e.ESC){
31960             this.cancelEdit();
31961             return;
31962         } 
31963         this.fireEvent('specialkey', field, e);
31964     
31965     },
31966
31967     /**
31968      * Starts the editing process and shows the editor.
31969      * @param {String/HTMLElement/Element} el The element to edit
31970      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31971       * to the innerHTML of el.
31972      */
31973     startEdit : function(el, value){
31974         if(this.editing){
31975             this.completeEdit();
31976         }
31977         this.boundEl = Roo.get(el);
31978         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31979         if(!this.rendered){
31980             this.render(this.parentEl || document.body);
31981         }
31982         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31983             return;
31984         }
31985         this.startValue = v;
31986         this.field.setValue(v);
31987         if(this.autoSize){
31988             var sz = this.boundEl.getSize();
31989             switch(this.autoSize){
31990                 case "width":
31991                 this.setSize(sz.width,  "");
31992                 break;
31993                 case "height":
31994                 this.setSize("",  sz.height);
31995                 break;
31996                 default:
31997                 this.setSize(sz.width,  sz.height);
31998             }
31999         }
32000         this.el.alignTo(this.boundEl, this.alignment);
32001         this.editing = true;
32002         if(Roo.QuickTips){
32003             Roo.QuickTips.disable();
32004         }
32005         this.show();
32006     },
32007
32008     /**
32009      * Sets the height and width of this editor.
32010      * @param {Number} width The new width
32011      * @param {Number} height The new height
32012      */
32013     setSize : function(w, h){
32014         this.field.setSize(w, h);
32015         if(this.el){
32016             this.el.sync();
32017         }
32018     },
32019
32020     /**
32021      * Realigns the editor to the bound field based on the current alignment config value.
32022      */
32023     realign : function(){
32024         this.el.alignTo(this.boundEl, this.alignment);
32025     },
32026
32027     /**
32028      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32029      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32030      */
32031     completeEdit : function(remainVisible){
32032         if(!this.editing){
32033             return;
32034         }
32035         var v = this.getValue();
32036         if(this.revertInvalid !== false && !this.field.isValid()){
32037             v = this.startValue;
32038             this.cancelEdit(true);
32039         }
32040         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32041             this.editing = false;
32042             this.hide();
32043             return;
32044         }
32045         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32046             this.editing = false;
32047             if(this.updateEl && this.boundEl){
32048                 this.boundEl.update(v);
32049             }
32050             if(remainVisible !== true){
32051                 this.hide();
32052             }
32053             this.fireEvent("complete", this, v, this.startValue);
32054         }
32055     },
32056
32057     // private
32058     onShow : function(){
32059         this.el.show();
32060         if(this.hideEl !== false){
32061             this.boundEl.hide();
32062         }
32063         this.field.show();
32064         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32065             this.fixIEFocus = true;
32066             this.deferredFocus.defer(50, this);
32067         }else{
32068             this.field.focus();
32069         }
32070         this.fireEvent("startedit", this.boundEl, this.startValue);
32071     },
32072
32073     deferredFocus : function(){
32074         if(this.editing){
32075             this.field.focus();
32076         }
32077     },
32078
32079     /**
32080      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32081      * reverted to the original starting value.
32082      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32083      * cancel (defaults to false)
32084      */
32085     cancelEdit : function(remainVisible){
32086         if(this.editing){
32087             this.setValue(this.startValue);
32088             if(remainVisible !== true){
32089                 this.hide();
32090             }
32091         }
32092     },
32093
32094     // private
32095     onBlur : function(){
32096         if(this.allowBlur !== true && this.editing){
32097             this.completeEdit();
32098         }
32099     },
32100
32101     // private
32102     onHide : function(){
32103         if(this.editing){
32104             this.completeEdit();
32105             return;
32106         }
32107         this.field.blur();
32108         if(this.field.collapse){
32109             this.field.collapse();
32110         }
32111         this.el.hide();
32112         if(this.hideEl !== false){
32113             this.boundEl.show();
32114         }
32115         if(Roo.QuickTips){
32116             Roo.QuickTips.enable();
32117         }
32118     },
32119
32120     /**
32121      * Sets the data value of the editor
32122      * @param {Mixed} value Any valid value supported by the underlying field
32123      */
32124     setValue : function(v){
32125         this.field.setValue(v);
32126     },
32127
32128     /**
32129      * Gets the data value of the editor
32130      * @return {Mixed} The data value
32131      */
32132     getValue : function(){
32133         return this.field.getValue();
32134     }
32135 });/*
32136  * Based on:
32137  * Ext JS Library 1.1.1
32138  * Copyright(c) 2006-2007, Ext JS, LLC.
32139  *
32140  * Originally Released Under LGPL - original licence link has changed is not relivant.
32141  *
32142  * Fork - LGPL
32143  * <script type="text/javascript">
32144  */
32145  
32146 /**
32147  * @class Roo.BasicDialog
32148  * @extends Roo.util.Observable
32149  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32150  * <pre><code>
32151 var dlg = new Roo.BasicDialog("my-dlg", {
32152     height: 200,
32153     width: 300,
32154     minHeight: 100,
32155     minWidth: 150,
32156     modal: true,
32157     proxyDrag: true,
32158     shadow: true
32159 });
32160 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32161 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32162 dlg.addButton('Cancel', dlg.hide, dlg);
32163 dlg.show();
32164 </code></pre>
32165   <b>A Dialog should always be a direct child of the body element.</b>
32166  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32167  * @cfg {String} title Default text to display in the title bar (defaults to null)
32168  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32169  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32170  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32171  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32172  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32173  * (defaults to null with no animation)
32174  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32175  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32176  * property for valid values (defaults to 'all')
32177  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32178  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32179  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32180  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32181  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32182  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32183  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32184  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32185  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32186  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32187  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32188  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32189  * draggable = true (defaults to false)
32190  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32191  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32192  * shadow (defaults to false)
32193  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32194  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32195  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32196  * @cfg {Array} buttons Array of buttons
32197  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32198  * @constructor
32199  * Create a new BasicDialog.
32200  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32201  * @param {Object} config Configuration options
32202  */
32203 Roo.BasicDialog = function(el, config){
32204     this.el = Roo.get(el);
32205     var dh = Roo.DomHelper;
32206     if(!this.el && config && config.autoCreate){
32207         if(typeof config.autoCreate == "object"){
32208             if(!config.autoCreate.id){
32209                 config.autoCreate.id = el;
32210             }
32211             this.el = dh.append(document.body,
32212                         config.autoCreate, true);
32213         }else{
32214             this.el = dh.append(document.body,
32215                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32216         }
32217     }
32218     el = this.el;
32219     el.setDisplayed(true);
32220     el.hide = this.hideAction;
32221     this.id = el.id;
32222     el.addClass("x-dlg");
32223
32224     Roo.apply(this, config);
32225
32226     this.proxy = el.createProxy("x-dlg-proxy");
32227     this.proxy.hide = this.hideAction;
32228     this.proxy.setOpacity(.5);
32229     this.proxy.hide();
32230
32231     if(config.width){
32232         el.setWidth(config.width);
32233     }
32234     if(config.height){
32235         el.setHeight(config.height);
32236     }
32237     this.size = el.getSize();
32238     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32239         this.xy = [config.x,config.y];
32240     }else{
32241         this.xy = el.getCenterXY(true);
32242     }
32243     /** The header element @type Roo.Element */
32244     this.header = el.child("> .x-dlg-hd");
32245     /** The body element @type Roo.Element */
32246     this.body = el.child("> .x-dlg-bd");
32247     /** The footer element @type Roo.Element */
32248     this.footer = el.child("> .x-dlg-ft");
32249
32250     if(!this.header){
32251         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32252     }
32253     if(!this.body){
32254         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32255     }
32256
32257     this.header.unselectable();
32258     if(this.title){
32259         this.header.update(this.title);
32260     }
32261     // this element allows the dialog to be focused for keyboard event
32262     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32263     this.focusEl.swallowEvent("click", true);
32264
32265     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32266
32267     // wrap the body and footer for special rendering
32268     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32269     if(this.footer){
32270         this.bwrap.dom.appendChild(this.footer.dom);
32271     }
32272
32273     this.bg = this.el.createChild({
32274         tag: "div", cls:"x-dlg-bg",
32275         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32276     });
32277     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32278
32279
32280     if(this.autoScroll !== false && !this.autoTabs){
32281         this.body.setStyle("overflow", "auto");
32282     }
32283
32284     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32285
32286     if(this.closable !== false){
32287         this.el.addClass("x-dlg-closable");
32288         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32289         this.close.on("click", this.closeClick, this);
32290         this.close.addClassOnOver("x-dlg-close-over");
32291     }
32292     if(this.collapsible !== false){
32293         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32294         this.collapseBtn.on("click", this.collapseClick, this);
32295         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32296         this.header.on("dblclick", this.collapseClick, this);
32297     }
32298     if(this.resizable !== false){
32299         this.el.addClass("x-dlg-resizable");
32300         this.resizer = new Roo.Resizable(el, {
32301             minWidth: this.minWidth || 80,
32302             minHeight:this.minHeight || 80,
32303             handles: this.resizeHandles || "all",
32304             pinned: true
32305         });
32306         this.resizer.on("beforeresize", this.beforeResize, this);
32307         this.resizer.on("resize", this.onResize, this);
32308     }
32309     if(this.draggable !== false){
32310         el.addClass("x-dlg-draggable");
32311         if (!this.proxyDrag) {
32312             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32313         }
32314         else {
32315             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32316         }
32317         dd.setHandleElId(this.header.id);
32318         dd.endDrag = this.endMove.createDelegate(this);
32319         dd.startDrag = this.startMove.createDelegate(this);
32320         dd.onDrag = this.onDrag.createDelegate(this);
32321         dd.scroll = false;
32322         this.dd = dd;
32323     }
32324     if(this.modal){
32325         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32326         this.mask.enableDisplayMode("block");
32327         this.mask.hide();
32328         this.el.addClass("x-dlg-modal");
32329     }
32330     if(this.shadow){
32331         this.shadow = new Roo.Shadow({
32332             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32333             offset : this.shadowOffset
32334         });
32335     }else{
32336         this.shadowOffset = 0;
32337     }
32338     if(Roo.useShims && this.shim !== false){
32339         this.shim = this.el.createShim();
32340         this.shim.hide = this.hideAction;
32341         this.shim.hide();
32342     }else{
32343         this.shim = false;
32344     }
32345     if(this.autoTabs){
32346         this.initTabs();
32347     }
32348     if (this.buttons) { 
32349         var bts= this.buttons;
32350         this.buttons = [];
32351         Roo.each(bts, function(b) {
32352             this.addButton(b);
32353         }, this);
32354     }
32355     
32356     
32357     this.addEvents({
32358         /**
32359          * @event keydown
32360          * Fires when a key is pressed
32361          * @param {Roo.BasicDialog} this
32362          * @param {Roo.EventObject} e
32363          */
32364         "keydown" : true,
32365         /**
32366          * @event move
32367          * Fires when this dialog is moved by the user.
32368          * @param {Roo.BasicDialog} this
32369          * @param {Number} x The new page X
32370          * @param {Number} y The new page Y
32371          */
32372         "move" : true,
32373         /**
32374          * @event resize
32375          * Fires when this dialog is resized by the user.
32376          * @param {Roo.BasicDialog} this
32377          * @param {Number} width The new width
32378          * @param {Number} height The new height
32379          */
32380         "resize" : true,
32381         /**
32382          * @event beforehide
32383          * Fires before this dialog is hidden.
32384          * @param {Roo.BasicDialog} this
32385          */
32386         "beforehide" : true,
32387         /**
32388          * @event hide
32389          * Fires when this dialog is hidden.
32390          * @param {Roo.BasicDialog} this
32391          */
32392         "hide" : true,
32393         /**
32394          * @event beforeshow
32395          * Fires before this dialog is shown.
32396          * @param {Roo.BasicDialog} this
32397          */
32398         "beforeshow" : true,
32399         /**
32400          * @event show
32401          * Fires when this dialog is shown.
32402          * @param {Roo.BasicDialog} this
32403          */
32404         "show" : true
32405     });
32406     el.on("keydown", this.onKeyDown, this);
32407     el.on("mousedown", this.toFront, this);
32408     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32409     this.el.hide();
32410     Roo.DialogManager.register(this);
32411     Roo.BasicDialog.superclass.constructor.call(this);
32412 };
32413
32414 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32415     shadowOffset: Roo.isIE ? 6 : 5,
32416     minHeight: 80,
32417     minWidth: 200,
32418     minButtonWidth: 75,
32419     defaultButton: null,
32420     buttonAlign: "right",
32421     tabTag: 'div',
32422     firstShow: true,
32423
32424     /**
32425      * Sets the dialog title text
32426      * @param {String} text The title text to display
32427      * @return {Roo.BasicDialog} this
32428      */
32429     setTitle : function(text){
32430         this.header.update(text);
32431         return this;
32432     },
32433
32434     // private
32435     closeClick : function(){
32436         this.hide();
32437     },
32438
32439     // private
32440     collapseClick : function(){
32441         this[this.collapsed ? "expand" : "collapse"]();
32442     },
32443
32444     /**
32445      * Collapses the dialog to its minimized state (only the title bar is visible).
32446      * Equivalent to the user clicking the collapse dialog button.
32447      */
32448     collapse : function(){
32449         if(!this.collapsed){
32450             this.collapsed = true;
32451             this.el.addClass("x-dlg-collapsed");
32452             this.restoreHeight = this.el.getHeight();
32453             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32454         }
32455     },
32456
32457     /**
32458      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32459      * clicking the expand dialog button.
32460      */
32461     expand : function(){
32462         if(this.collapsed){
32463             this.collapsed = false;
32464             this.el.removeClass("x-dlg-collapsed");
32465             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32466         }
32467     },
32468
32469     /**
32470      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32471      * @return {Roo.TabPanel} The tabs component
32472      */
32473     initTabs : function(){
32474         var tabs = this.getTabs();
32475         while(tabs.getTab(0)){
32476             tabs.removeTab(0);
32477         }
32478         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32479             var dom = el.dom;
32480             tabs.addTab(Roo.id(dom), dom.title);
32481             dom.title = "";
32482         });
32483         tabs.activate(0);
32484         return tabs;
32485     },
32486
32487     // private
32488     beforeResize : function(){
32489         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32490     },
32491
32492     // private
32493     onResize : function(){
32494         this.refreshSize();
32495         this.syncBodyHeight();
32496         this.adjustAssets();
32497         this.focus();
32498         this.fireEvent("resize", this, this.size.width, this.size.height);
32499     },
32500
32501     // private
32502     onKeyDown : function(e){
32503         if(this.isVisible()){
32504             this.fireEvent("keydown", this, e);
32505         }
32506     },
32507
32508     /**
32509      * Resizes the dialog.
32510      * @param {Number} width
32511      * @param {Number} height
32512      * @return {Roo.BasicDialog} this
32513      */
32514     resizeTo : function(width, height){
32515         this.el.setSize(width, height);
32516         this.size = {width: width, height: height};
32517         this.syncBodyHeight();
32518         if(this.fixedcenter){
32519             this.center();
32520         }
32521         if(this.isVisible()){
32522             this.constrainXY();
32523             this.adjustAssets();
32524         }
32525         this.fireEvent("resize", this, width, height);
32526         return this;
32527     },
32528
32529
32530     /**
32531      * Resizes the dialog to fit the specified content size.
32532      * @param {Number} width
32533      * @param {Number} height
32534      * @return {Roo.BasicDialog} this
32535      */
32536     setContentSize : function(w, h){
32537         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32538         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32539         //if(!this.el.isBorderBox()){
32540             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32541             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32542         //}
32543         if(this.tabs){
32544             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32545             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32546         }
32547         this.resizeTo(w, h);
32548         return this;
32549     },
32550
32551     /**
32552      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32553      * executed in response to a particular key being pressed while the dialog is active.
32554      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32555      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32556      * @param {Function} fn The function to call
32557      * @param {Object} scope (optional) The scope of the function
32558      * @return {Roo.BasicDialog} this
32559      */
32560     addKeyListener : function(key, fn, scope){
32561         var keyCode, shift, ctrl, alt;
32562         if(typeof key == "object" && !(key instanceof Array)){
32563             keyCode = key["key"];
32564             shift = key["shift"];
32565             ctrl = key["ctrl"];
32566             alt = key["alt"];
32567         }else{
32568             keyCode = key;
32569         }
32570         var handler = function(dlg, e){
32571             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32572                 var k = e.getKey();
32573                 if(keyCode instanceof Array){
32574                     for(var i = 0, len = keyCode.length; i < len; i++){
32575                         if(keyCode[i] == k){
32576                           fn.call(scope || window, dlg, k, e);
32577                           return;
32578                         }
32579                     }
32580                 }else{
32581                     if(k == keyCode){
32582                         fn.call(scope || window, dlg, k, e);
32583                     }
32584                 }
32585             }
32586         };
32587         this.on("keydown", handler);
32588         return this;
32589     },
32590
32591     /**
32592      * Returns the TabPanel component (creates it if it doesn't exist).
32593      * Note: If you wish to simply check for the existence of tabs without creating them,
32594      * check for a null 'tabs' property.
32595      * @return {Roo.TabPanel} The tabs component
32596      */
32597     getTabs : function(){
32598         if(!this.tabs){
32599             this.el.addClass("x-dlg-auto-tabs");
32600             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32601             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32602         }
32603         return this.tabs;
32604     },
32605
32606     /**
32607      * Adds a button to the footer section of the dialog.
32608      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32609      * object or a valid Roo.DomHelper element config
32610      * @param {Function} handler The function called when the button is clicked
32611      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32612      * @return {Roo.Button} The new button
32613      */
32614     addButton : function(config, handler, scope){
32615         var dh = Roo.DomHelper;
32616         if(!this.footer){
32617             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32618         }
32619         if(!this.btnContainer){
32620             var tb = this.footer.createChild({
32621
32622                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32623                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32624             }, null, true);
32625             this.btnContainer = tb.firstChild.firstChild.firstChild;
32626         }
32627         var bconfig = {
32628             handler: handler,
32629             scope: scope,
32630             minWidth: this.minButtonWidth,
32631             hideParent:true
32632         };
32633         if(typeof config == "string"){
32634             bconfig.text = config;
32635         }else{
32636             if(config.tag){
32637                 bconfig.dhconfig = config;
32638             }else{
32639                 Roo.apply(bconfig, config);
32640             }
32641         }
32642         var fc = false;
32643         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32644             bconfig.position = Math.max(0, bconfig.position);
32645             fc = this.btnContainer.childNodes[bconfig.position];
32646         }
32647          
32648         var btn = new Roo.Button(
32649             fc ? 
32650                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32651                 : this.btnContainer.appendChild(document.createElement("td")),
32652             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32653             bconfig
32654         );
32655         this.syncBodyHeight();
32656         if(!this.buttons){
32657             /**
32658              * Array of all the buttons that have been added to this dialog via addButton
32659              * @type Array
32660              */
32661             this.buttons = [];
32662         }
32663         this.buttons.push(btn);
32664         return btn;
32665     },
32666
32667     /**
32668      * Sets the default button to be focused when the dialog is displayed.
32669      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32670      * @return {Roo.BasicDialog} this
32671      */
32672     setDefaultButton : function(btn){
32673         this.defaultButton = btn;
32674         return this;
32675     },
32676
32677     // private
32678     getHeaderFooterHeight : function(safe){
32679         var height = 0;
32680         if(this.header){
32681            height += this.header.getHeight();
32682         }
32683         if(this.footer){
32684            var fm = this.footer.getMargins();
32685             height += (this.footer.getHeight()+fm.top+fm.bottom);
32686         }
32687         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32688         height += this.centerBg.getPadding("tb");
32689         return height;
32690     },
32691
32692     // private
32693     syncBodyHeight : function()
32694     {
32695         var bd = this.body, // the text
32696             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32697             bw = this.bwrap;
32698         var height = this.size.height - this.getHeaderFooterHeight(false);
32699         bd.setHeight(height-bd.getMargins("tb"));
32700         var hh = this.header.getHeight();
32701         var h = this.size.height-hh;
32702         cb.setHeight(h);
32703         
32704         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32705         bw.setHeight(h-cb.getPadding("tb"));
32706         
32707         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32708         bd.setWidth(bw.getWidth(true));
32709         if(this.tabs){
32710             this.tabs.syncHeight();
32711             if(Roo.isIE){
32712                 this.tabs.el.repaint();
32713             }
32714         }
32715     },
32716
32717     /**
32718      * Restores the previous state of the dialog if Roo.state is configured.
32719      * @return {Roo.BasicDialog} this
32720      */
32721     restoreState : function(){
32722         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32723         if(box && box.width){
32724             this.xy = [box.x, box.y];
32725             this.resizeTo(box.width, box.height);
32726         }
32727         return this;
32728     },
32729
32730     // private
32731     beforeShow : function(){
32732         this.expand();
32733         if(this.fixedcenter){
32734             this.xy = this.el.getCenterXY(true);
32735         }
32736         if(this.modal){
32737             Roo.get(document.body).addClass("x-body-masked");
32738             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32739             this.mask.show();
32740         }
32741         this.constrainXY();
32742     },
32743
32744     // private
32745     animShow : function(){
32746         var b = Roo.get(this.animateTarget).getBox();
32747         this.proxy.setSize(b.width, b.height);
32748         this.proxy.setLocation(b.x, b.y);
32749         this.proxy.show();
32750         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32751                     true, .35, this.showEl.createDelegate(this));
32752     },
32753
32754     /**
32755      * Shows the dialog.
32756      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32757      * @return {Roo.BasicDialog} this
32758      */
32759     show : function(animateTarget){
32760         if (this.fireEvent("beforeshow", this) === false){
32761             return;
32762         }
32763         if(this.syncHeightBeforeShow){
32764             this.syncBodyHeight();
32765         }else if(this.firstShow){
32766             this.firstShow = false;
32767             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32768         }
32769         this.animateTarget = animateTarget || this.animateTarget;
32770         if(!this.el.isVisible()){
32771             this.beforeShow();
32772             if(this.animateTarget && Roo.get(this.animateTarget)){
32773                 this.animShow();
32774             }else{
32775                 this.showEl();
32776             }
32777         }
32778         return this;
32779     },
32780
32781     // private
32782     showEl : function(){
32783         this.proxy.hide();
32784         this.el.setXY(this.xy);
32785         this.el.show();
32786         this.adjustAssets(true);
32787         this.toFront();
32788         this.focus();
32789         // IE peekaboo bug - fix found by Dave Fenwick
32790         if(Roo.isIE){
32791             this.el.repaint();
32792         }
32793         this.fireEvent("show", this);
32794     },
32795
32796     /**
32797      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32798      * dialog itself will receive focus.
32799      */
32800     focus : function(){
32801         if(this.defaultButton){
32802             this.defaultButton.focus();
32803         }else{
32804             this.focusEl.focus();
32805         }
32806     },
32807
32808     // private
32809     constrainXY : function(){
32810         if(this.constraintoviewport !== false){
32811             if(!this.viewSize){
32812                 if(this.container){
32813                     var s = this.container.getSize();
32814                     this.viewSize = [s.width, s.height];
32815                 }else{
32816                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32817                 }
32818             }
32819             var s = Roo.get(this.container||document).getScroll();
32820
32821             var x = this.xy[0], y = this.xy[1];
32822             var w = this.size.width, h = this.size.height;
32823             var vw = this.viewSize[0], vh = this.viewSize[1];
32824             // only move it if it needs it
32825             var moved = false;
32826             // first validate right/bottom
32827             if(x + w > vw+s.left){
32828                 x = vw - w;
32829                 moved = true;
32830             }
32831             if(y + h > vh+s.top){
32832                 y = vh - h;
32833                 moved = true;
32834             }
32835             // then make sure top/left isn't negative
32836             if(x < s.left){
32837                 x = s.left;
32838                 moved = true;
32839             }
32840             if(y < s.top){
32841                 y = s.top;
32842                 moved = true;
32843             }
32844             if(moved){
32845                 // cache xy
32846                 this.xy = [x, y];
32847                 if(this.isVisible()){
32848                     this.el.setLocation(x, y);
32849                     this.adjustAssets();
32850                 }
32851             }
32852         }
32853     },
32854
32855     // private
32856     onDrag : function(){
32857         if(!this.proxyDrag){
32858             this.xy = this.el.getXY();
32859             this.adjustAssets();
32860         }
32861     },
32862
32863     // private
32864     adjustAssets : function(doShow){
32865         var x = this.xy[0], y = this.xy[1];
32866         var w = this.size.width, h = this.size.height;
32867         if(doShow === true){
32868             if(this.shadow){
32869                 this.shadow.show(this.el);
32870             }
32871             if(this.shim){
32872                 this.shim.show();
32873             }
32874         }
32875         if(this.shadow && this.shadow.isVisible()){
32876             this.shadow.show(this.el);
32877         }
32878         if(this.shim && this.shim.isVisible()){
32879             this.shim.setBounds(x, y, w, h);
32880         }
32881     },
32882
32883     // private
32884     adjustViewport : function(w, h){
32885         if(!w || !h){
32886             w = Roo.lib.Dom.getViewWidth();
32887             h = Roo.lib.Dom.getViewHeight();
32888         }
32889         // cache the size
32890         this.viewSize = [w, h];
32891         if(this.modal && this.mask.isVisible()){
32892             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32893             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32894         }
32895         if(this.isVisible()){
32896             this.constrainXY();
32897         }
32898     },
32899
32900     /**
32901      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32902      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32903      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32904      */
32905     destroy : function(removeEl){
32906         if(this.isVisible()){
32907             this.animateTarget = null;
32908             this.hide();
32909         }
32910         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32911         if(this.tabs){
32912             this.tabs.destroy(removeEl);
32913         }
32914         Roo.destroy(
32915              this.shim,
32916              this.proxy,
32917              this.resizer,
32918              this.close,
32919              this.mask
32920         );
32921         if(this.dd){
32922             this.dd.unreg();
32923         }
32924         if(this.buttons){
32925            for(var i = 0, len = this.buttons.length; i < len; i++){
32926                this.buttons[i].destroy();
32927            }
32928         }
32929         this.el.removeAllListeners();
32930         if(removeEl === true){
32931             this.el.update("");
32932             this.el.remove();
32933         }
32934         Roo.DialogManager.unregister(this);
32935     },
32936
32937     // private
32938     startMove : function(){
32939         if(this.proxyDrag){
32940             this.proxy.show();
32941         }
32942         if(this.constraintoviewport !== false){
32943             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32944         }
32945     },
32946
32947     // private
32948     endMove : function(){
32949         if(!this.proxyDrag){
32950             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32951         }else{
32952             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32953             this.proxy.hide();
32954         }
32955         this.refreshSize();
32956         this.adjustAssets();
32957         this.focus();
32958         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32959     },
32960
32961     /**
32962      * Brings this dialog to the front of any other visible dialogs
32963      * @return {Roo.BasicDialog} this
32964      */
32965     toFront : function(){
32966         Roo.DialogManager.bringToFront(this);
32967         return this;
32968     },
32969
32970     /**
32971      * Sends this dialog to the back (under) of any other visible dialogs
32972      * @return {Roo.BasicDialog} this
32973      */
32974     toBack : function(){
32975         Roo.DialogManager.sendToBack(this);
32976         return this;
32977     },
32978
32979     /**
32980      * Centers this dialog in the viewport
32981      * @return {Roo.BasicDialog} this
32982      */
32983     center : function(){
32984         var xy = this.el.getCenterXY(true);
32985         this.moveTo(xy[0], xy[1]);
32986         return this;
32987     },
32988
32989     /**
32990      * Moves the dialog's top-left corner to the specified point
32991      * @param {Number} x
32992      * @param {Number} y
32993      * @return {Roo.BasicDialog} this
32994      */
32995     moveTo : function(x, y){
32996         this.xy = [x,y];
32997         if(this.isVisible()){
32998             this.el.setXY(this.xy);
32999             this.adjustAssets();
33000         }
33001         return this;
33002     },
33003
33004     /**
33005      * Aligns the dialog to the specified element
33006      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33007      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33008      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33009      * @return {Roo.BasicDialog} this
33010      */
33011     alignTo : function(element, position, offsets){
33012         this.xy = this.el.getAlignToXY(element, position, offsets);
33013         if(this.isVisible()){
33014             this.el.setXY(this.xy);
33015             this.adjustAssets();
33016         }
33017         return this;
33018     },
33019
33020     /**
33021      * Anchors an element to another element and realigns it when the window is resized.
33022      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33023      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33024      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33025      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33026      * is a number, it is used as the buffer delay (defaults to 50ms).
33027      * @return {Roo.BasicDialog} this
33028      */
33029     anchorTo : function(el, alignment, offsets, monitorScroll){
33030         var action = function(){
33031             this.alignTo(el, alignment, offsets);
33032         };
33033         Roo.EventManager.onWindowResize(action, this);
33034         var tm = typeof monitorScroll;
33035         if(tm != 'undefined'){
33036             Roo.EventManager.on(window, 'scroll', action, this,
33037                 {buffer: tm == 'number' ? monitorScroll : 50});
33038         }
33039         action.call(this);
33040         return this;
33041     },
33042
33043     /**
33044      * Returns true if the dialog is visible
33045      * @return {Boolean}
33046      */
33047     isVisible : function(){
33048         return this.el.isVisible();
33049     },
33050
33051     // private
33052     animHide : function(callback){
33053         var b = Roo.get(this.animateTarget).getBox();
33054         this.proxy.show();
33055         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33056         this.el.hide();
33057         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33058                     this.hideEl.createDelegate(this, [callback]));
33059     },
33060
33061     /**
33062      * Hides the dialog.
33063      * @param {Function} callback (optional) Function to call when the dialog is hidden
33064      * @return {Roo.BasicDialog} this
33065      */
33066     hide : function(callback){
33067         if (this.fireEvent("beforehide", this) === false){
33068             return;
33069         }
33070         if(this.shadow){
33071             this.shadow.hide();
33072         }
33073         if(this.shim) {
33074           this.shim.hide();
33075         }
33076         // sometimes animateTarget seems to get set.. causing problems...
33077         // this just double checks..
33078         if(this.animateTarget && Roo.get(this.animateTarget)) {
33079            this.animHide(callback);
33080         }else{
33081             this.el.hide();
33082             this.hideEl(callback);
33083         }
33084         return this;
33085     },
33086
33087     // private
33088     hideEl : function(callback){
33089         this.proxy.hide();
33090         if(this.modal){
33091             this.mask.hide();
33092             Roo.get(document.body).removeClass("x-body-masked");
33093         }
33094         this.fireEvent("hide", this);
33095         if(typeof callback == "function"){
33096             callback();
33097         }
33098     },
33099
33100     // private
33101     hideAction : function(){
33102         this.setLeft("-10000px");
33103         this.setTop("-10000px");
33104         this.setStyle("visibility", "hidden");
33105     },
33106
33107     // private
33108     refreshSize : function(){
33109         this.size = this.el.getSize();
33110         this.xy = this.el.getXY();
33111         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33112     },
33113
33114     // private
33115     // z-index is managed by the DialogManager and may be overwritten at any time
33116     setZIndex : function(index){
33117         if(this.modal){
33118             this.mask.setStyle("z-index", index);
33119         }
33120         if(this.shim){
33121             this.shim.setStyle("z-index", ++index);
33122         }
33123         if(this.shadow){
33124             this.shadow.setZIndex(++index);
33125         }
33126         this.el.setStyle("z-index", ++index);
33127         if(this.proxy){
33128             this.proxy.setStyle("z-index", ++index);
33129         }
33130         if(this.resizer){
33131             this.resizer.proxy.setStyle("z-index", ++index);
33132         }
33133
33134         this.lastZIndex = index;
33135     },
33136
33137     /**
33138      * Returns the element for this dialog
33139      * @return {Roo.Element} The underlying dialog Element
33140      */
33141     getEl : function(){
33142         return this.el;
33143     }
33144 });
33145
33146 /**
33147  * @class Roo.DialogManager
33148  * Provides global access to BasicDialogs that have been created and
33149  * support for z-indexing (layering) multiple open dialogs.
33150  */
33151 Roo.DialogManager = function(){
33152     var list = {};
33153     var accessList = [];
33154     var front = null;
33155
33156     // private
33157     var sortDialogs = function(d1, d2){
33158         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33159     };
33160
33161     // private
33162     var orderDialogs = function(){
33163         accessList.sort(sortDialogs);
33164         var seed = Roo.DialogManager.zseed;
33165         for(var i = 0, len = accessList.length; i < len; i++){
33166             var dlg = accessList[i];
33167             if(dlg){
33168                 dlg.setZIndex(seed + (i*10));
33169             }
33170         }
33171     };
33172
33173     return {
33174         /**
33175          * The starting z-index for BasicDialogs (defaults to 9000)
33176          * @type Number The z-index value
33177          */
33178         zseed : 9000,
33179
33180         // private
33181         register : function(dlg){
33182             list[dlg.id] = dlg;
33183             accessList.push(dlg);
33184         },
33185
33186         // private
33187         unregister : function(dlg){
33188             delete list[dlg.id];
33189             var i=0;
33190             var len=0;
33191             if(!accessList.indexOf){
33192                 for(  i = 0, len = accessList.length; i < len; i++){
33193                     if(accessList[i] == dlg){
33194                         accessList.splice(i, 1);
33195                         return;
33196                     }
33197                 }
33198             }else{
33199                  i = accessList.indexOf(dlg);
33200                 if(i != -1){
33201                     accessList.splice(i, 1);
33202                 }
33203             }
33204         },
33205
33206         /**
33207          * Gets a registered dialog by id
33208          * @param {String/Object} id The id of the dialog or a dialog
33209          * @return {Roo.BasicDialog} this
33210          */
33211         get : function(id){
33212             return typeof id == "object" ? id : list[id];
33213         },
33214
33215         /**
33216          * Brings the specified dialog to the front
33217          * @param {String/Object} dlg The id of the dialog or a dialog
33218          * @return {Roo.BasicDialog} this
33219          */
33220         bringToFront : function(dlg){
33221             dlg = this.get(dlg);
33222             if(dlg != front){
33223                 front = dlg;
33224                 dlg._lastAccess = new Date().getTime();
33225                 orderDialogs();
33226             }
33227             return dlg;
33228         },
33229
33230         /**
33231          * Sends the specified dialog to the back
33232          * @param {String/Object} dlg The id of the dialog or a dialog
33233          * @return {Roo.BasicDialog} this
33234          */
33235         sendToBack : function(dlg){
33236             dlg = this.get(dlg);
33237             dlg._lastAccess = -(new Date().getTime());
33238             orderDialogs();
33239             return dlg;
33240         },
33241
33242         /**
33243          * Hides all dialogs
33244          */
33245         hideAll : function(){
33246             for(var id in list){
33247                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33248                     list[id].hide();
33249                 }
33250             }
33251         }
33252     };
33253 }();
33254
33255 /**
33256  * @class Roo.LayoutDialog
33257  * @extends Roo.BasicDialog
33258  * @children Roo.ContentPanel
33259  * Dialog which provides adjustments for working with a layout in a Dialog.
33260  * Add your necessary layout config options to the dialog's config.<br>
33261  * Example usage (including a nested layout):
33262  * <pre><code>
33263 if(!dialog){
33264     dialog = new Roo.LayoutDialog("download-dlg", {
33265         modal: true,
33266         width:600,
33267         height:450,
33268         shadow:true,
33269         minWidth:500,
33270         minHeight:350,
33271         autoTabs:true,
33272         proxyDrag:true,
33273         // layout config merges with the dialog config
33274         center:{
33275             tabPosition: "top",
33276             alwaysShowTabs: true
33277         }
33278     });
33279     dialog.addKeyListener(27, dialog.hide, dialog);
33280     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33281     dialog.addButton("Build It!", this.getDownload, this);
33282
33283     // we can even add nested layouts
33284     var innerLayout = new Roo.BorderLayout("dl-inner", {
33285         east: {
33286             initialSize: 200,
33287             autoScroll:true,
33288             split:true
33289         },
33290         center: {
33291             autoScroll:true
33292         }
33293     });
33294     innerLayout.beginUpdate();
33295     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33296     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33297     innerLayout.endUpdate(true);
33298
33299     var layout = dialog.getLayout();
33300     layout.beginUpdate();
33301     layout.add("center", new Roo.ContentPanel("standard-panel",
33302                         {title: "Download the Source", fitToFrame:true}));
33303     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33304                {title: "Build your own roo.js"}));
33305     layout.getRegion("center").showPanel(sp);
33306     layout.endUpdate();
33307 }
33308 </code></pre>
33309     * @constructor
33310     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33311     * @param {Object} config configuration options
33312   */
33313 Roo.LayoutDialog = function(el, cfg){
33314     
33315     var config=  cfg;
33316     if (typeof(cfg) == 'undefined') {
33317         config = Roo.apply({}, el);
33318         // not sure why we use documentElement here.. - it should always be body.
33319         // IE7 borks horribly if we use documentElement.
33320         // webkit also does not like documentElement - it creates a body element...
33321         el = Roo.get( document.body || document.documentElement ).createChild();
33322         //config.autoCreate = true;
33323     }
33324     
33325     
33326     config.autoTabs = false;
33327     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33328     this.body.setStyle({overflow:"hidden", position:"relative"});
33329     this.layout = new Roo.BorderLayout(this.body.dom, config);
33330     this.layout.monitorWindowResize = false;
33331     this.el.addClass("x-dlg-auto-layout");
33332     // fix case when center region overwrites center function
33333     this.center = Roo.BasicDialog.prototype.center;
33334     this.on("show", this.layout.layout, this.layout, true);
33335     if (config.items) {
33336         var xitems = config.items;
33337         delete config.items;
33338         Roo.each(xitems, this.addxtype, this);
33339     }
33340     
33341     
33342 };
33343 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33344     
33345     
33346     /**
33347      * @cfg {Roo.LayoutRegion} east  
33348      */
33349     /**
33350      * @cfg {Roo.LayoutRegion} west
33351      */
33352     /**
33353      * @cfg {Roo.LayoutRegion} south
33354      */
33355     /**
33356      * @cfg {Roo.LayoutRegion} north
33357      */
33358     /**
33359      * @cfg {Roo.LayoutRegion} center
33360      */
33361     /**
33362      * @cfg {Roo.Button} buttons[]  Bottom buttons..
33363      */
33364     
33365     
33366     /**
33367      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33368      * @deprecated
33369      */
33370     endUpdate : function(){
33371         this.layout.endUpdate();
33372     },
33373
33374     /**
33375      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33376      *  @deprecated
33377      */
33378     beginUpdate : function(){
33379         this.layout.beginUpdate();
33380     },
33381
33382     /**
33383      * Get the BorderLayout for this dialog
33384      * @return {Roo.BorderLayout}
33385      */
33386     getLayout : function(){
33387         return this.layout;
33388     },
33389
33390     showEl : function(){
33391         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33392         if(Roo.isIE7){
33393             this.layout.layout();
33394         }
33395     },
33396
33397     // private
33398     // Use the syncHeightBeforeShow config option to control this automatically
33399     syncBodyHeight : function(){
33400         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33401         if(this.layout){this.layout.layout();}
33402     },
33403     
33404       /**
33405      * Add an xtype element (actually adds to the layout.)
33406      * @return {Object} xdata xtype object data.
33407      */
33408     
33409     addxtype : function(c) {
33410         return this.layout.addxtype(c);
33411     }
33412 });/*
33413  * Based on:
33414  * Ext JS Library 1.1.1
33415  * Copyright(c) 2006-2007, Ext JS, LLC.
33416  *
33417  * Originally Released Under LGPL - original licence link has changed is not relivant.
33418  *
33419  * Fork - LGPL
33420  * <script type="text/javascript">
33421  */
33422  
33423 /**
33424  * @class Roo.MessageBox
33425  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33426  * Example usage:
33427  *<pre><code>
33428 // Basic alert:
33429 Roo.Msg.alert('Status', 'Changes saved successfully.');
33430
33431 // Prompt for user data:
33432 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33433     if (btn == 'ok'){
33434         // process text value...
33435     }
33436 });
33437
33438 // Show a dialog using config options:
33439 Roo.Msg.show({
33440    title:'Save Changes?',
33441    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33442    buttons: Roo.Msg.YESNOCANCEL,
33443    fn: processResult,
33444    animEl: 'elId'
33445 });
33446 </code></pre>
33447  * @singleton
33448  */
33449 Roo.MessageBox = function(){
33450     var dlg, opt, mask, waitTimer;
33451     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33452     var buttons, activeTextEl, bwidth;
33453
33454     // private
33455     var handleButton = function(button){
33456         dlg.hide();
33457         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33458     };
33459
33460     // private
33461     var handleHide = function(){
33462         if(opt && opt.cls){
33463             dlg.el.removeClass(opt.cls);
33464         }
33465         if(waitTimer){
33466             Roo.TaskMgr.stop(waitTimer);
33467             waitTimer = null;
33468         }
33469     };
33470
33471     // private
33472     var updateButtons = function(b){
33473         var width = 0;
33474         if(!b){
33475             buttons["ok"].hide();
33476             buttons["cancel"].hide();
33477             buttons["yes"].hide();
33478             buttons["no"].hide();
33479             dlg.footer.dom.style.display = 'none';
33480             return width;
33481         }
33482         dlg.footer.dom.style.display = '';
33483         for(var k in buttons){
33484             if(typeof buttons[k] != "function"){
33485                 if(b[k]){
33486                     buttons[k].show();
33487                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33488                     width += buttons[k].el.getWidth()+15;
33489                 }else{
33490                     buttons[k].hide();
33491                 }
33492             }
33493         }
33494         return width;
33495     };
33496
33497     // private
33498     var handleEsc = function(d, k, e){
33499         if(opt && opt.closable !== false){
33500             dlg.hide();
33501         }
33502         if(e){
33503             e.stopEvent();
33504         }
33505     };
33506
33507     return {
33508         /**
33509          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33510          * @return {Roo.BasicDialog} The BasicDialog element
33511          */
33512         getDialog : function(){
33513            if(!dlg){
33514                 dlg = new Roo.BasicDialog("x-msg-box", {
33515                     autoCreate : true,
33516                     shadow: true,
33517                     draggable: true,
33518                     resizable:false,
33519                     constraintoviewport:false,
33520                     fixedcenter:true,
33521                     collapsible : false,
33522                     shim:true,
33523                     modal: true,
33524                     width:400, height:100,
33525                     buttonAlign:"center",
33526                     closeClick : function(){
33527                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33528                             handleButton("no");
33529                         }else{
33530                             handleButton("cancel");
33531                         }
33532                     }
33533                 });
33534                 dlg.on("hide", handleHide);
33535                 mask = dlg.mask;
33536                 dlg.addKeyListener(27, handleEsc);
33537                 buttons = {};
33538                 var bt = this.buttonText;
33539                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33540                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33541                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33542                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33543                 bodyEl = dlg.body.createChild({
33544
33545                     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>'
33546                 });
33547                 msgEl = bodyEl.dom.firstChild;
33548                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33549                 textboxEl.enableDisplayMode();
33550                 textboxEl.addKeyListener([10,13], function(){
33551                     if(dlg.isVisible() && opt && opt.buttons){
33552                         if(opt.buttons.ok){
33553                             handleButton("ok");
33554                         }else if(opt.buttons.yes){
33555                             handleButton("yes");
33556                         }
33557                     }
33558                 });
33559                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33560                 textareaEl.enableDisplayMode();
33561                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33562                 progressEl.enableDisplayMode();
33563                 var pf = progressEl.dom.firstChild;
33564                 if (pf) {
33565                     pp = Roo.get(pf.firstChild);
33566                     pp.setHeight(pf.offsetHeight);
33567                 }
33568                 
33569             }
33570             return dlg;
33571         },
33572
33573         /**
33574          * Updates the message box body text
33575          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33576          * the XHTML-compliant non-breaking space character '&amp;#160;')
33577          * @return {Roo.MessageBox} This message box
33578          */
33579         updateText : function(text){
33580             if(!dlg.isVisible() && !opt.width){
33581                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33582             }
33583             msgEl.innerHTML = text || '&#160;';
33584       
33585             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33586             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33587             var w = Math.max(
33588                     Math.min(opt.width || cw , this.maxWidth), 
33589                     Math.max(opt.minWidth || this.minWidth, bwidth)
33590             );
33591             if(opt.prompt){
33592                 activeTextEl.setWidth(w);
33593             }
33594             if(dlg.isVisible()){
33595                 dlg.fixedcenter = false;
33596             }
33597             // to big, make it scroll. = But as usual stupid IE does not support
33598             // !important..
33599             
33600             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33601                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33602                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33603             } else {
33604                 bodyEl.dom.style.height = '';
33605                 bodyEl.dom.style.overflowY = '';
33606             }
33607             if (cw > w) {
33608                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33609             } else {
33610                 bodyEl.dom.style.overflowX = '';
33611             }
33612             
33613             dlg.setContentSize(w, bodyEl.getHeight());
33614             if(dlg.isVisible()){
33615                 dlg.fixedcenter = true;
33616             }
33617             return this;
33618         },
33619
33620         /**
33621          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33622          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33623          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33624          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33625          * @return {Roo.MessageBox} This message box
33626          */
33627         updateProgress : function(value, text){
33628             if(text){
33629                 this.updateText(text);
33630             }
33631             if (pp) { // weird bug on my firefox - for some reason this is not defined
33632                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33633             }
33634             return this;
33635         },        
33636
33637         /**
33638          * Returns true if the message box is currently displayed
33639          * @return {Boolean} True if the message box is visible, else false
33640          */
33641         isVisible : function(){
33642             return dlg && dlg.isVisible();  
33643         },
33644
33645         /**
33646          * Hides the message box if it is displayed
33647          */
33648         hide : function(){
33649             if(this.isVisible()){
33650                 dlg.hide();
33651             }  
33652         },
33653
33654         /**
33655          * Displays a new message box, or reinitializes an existing message box, based on the config options
33656          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33657          * The following config object properties are supported:
33658          * <pre>
33659 Property    Type             Description
33660 ----------  ---------------  ------------------------------------------------------------------------------------
33661 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33662                                    closes (defaults to undefined)
33663 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33664                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33665 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33666                                    progress and wait dialogs will ignore this property and always hide the
33667                                    close button as they can only be closed programmatically.
33668 cls               String           A custom CSS class to apply to the message box element
33669 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33670                                    displayed (defaults to 75)
33671 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33672                                    function will be btn (the name of the button that was clicked, if applicable,
33673                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33674                                    Progress and wait dialogs will ignore this option since they do not respond to
33675                                    user actions and can only be closed programmatically, so any required function
33676                                    should be called by the same code after it closes the dialog.
33677 icon              String           A CSS class that provides a background image to be used as an icon for
33678                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33679 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33680 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33681 modal             Boolean          False to allow user interaction with the page while the message box is
33682                                    displayed (defaults to true)
33683 msg               String           A string that will replace the existing message box body text (defaults
33684                                    to the XHTML-compliant non-breaking space character '&#160;')
33685 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33686 progress          Boolean          True to display a progress bar (defaults to false)
33687 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33688 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33689 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33690 title             String           The title text
33691 value             String           The string value to set into the active textbox element if displayed
33692 wait              Boolean          True to display a progress bar (defaults to false)
33693 width             Number           The width of the dialog in pixels
33694 </pre>
33695          *
33696          * Example usage:
33697          * <pre><code>
33698 Roo.Msg.show({
33699    title: 'Address',
33700    msg: 'Please enter your address:',
33701    width: 300,
33702    buttons: Roo.MessageBox.OKCANCEL,
33703    multiline: true,
33704    fn: saveAddress,
33705    animEl: 'addAddressBtn'
33706 });
33707 </code></pre>
33708          * @param {Object} config Configuration options
33709          * @return {Roo.MessageBox} This message box
33710          */
33711         show : function(options)
33712         {
33713             
33714             // this causes nightmares if you show one dialog after another
33715             // especially on callbacks..
33716              
33717             if(this.isVisible()){
33718                 
33719                 this.hide();
33720                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33721                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33722                 Roo.log("New Dialog Message:" +  options.msg )
33723                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33724                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33725                 
33726             }
33727             var d = this.getDialog();
33728             opt = options;
33729             d.setTitle(opt.title || "&#160;");
33730             d.close.setDisplayed(opt.closable !== false);
33731             activeTextEl = textboxEl;
33732             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33733             if(opt.prompt){
33734                 if(opt.multiline){
33735                     textboxEl.hide();
33736                     textareaEl.show();
33737                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33738                         opt.multiline : this.defaultTextHeight);
33739                     activeTextEl = textareaEl;
33740                 }else{
33741                     textboxEl.show();
33742                     textareaEl.hide();
33743                 }
33744             }else{
33745                 textboxEl.hide();
33746                 textareaEl.hide();
33747             }
33748             progressEl.setDisplayed(opt.progress === true);
33749             this.updateProgress(0);
33750             activeTextEl.dom.value = opt.value || "";
33751             if(opt.prompt){
33752                 dlg.setDefaultButton(activeTextEl);
33753             }else{
33754                 var bs = opt.buttons;
33755                 var db = null;
33756                 if(bs && bs.ok){
33757                     db = buttons["ok"];
33758                 }else if(bs && bs.yes){
33759                     db = buttons["yes"];
33760                 }
33761                 dlg.setDefaultButton(db);
33762             }
33763             bwidth = updateButtons(opt.buttons);
33764             this.updateText(opt.msg);
33765             if(opt.cls){
33766                 d.el.addClass(opt.cls);
33767             }
33768             d.proxyDrag = opt.proxyDrag === true;
33769             d.modal = opt.modal !== false;
33770             d.mask = opt.modal !== false ? mask : false;
33771             if(!d.isVisible()){
33772                 // force it to the end of the z-index stack so it gets a cursor in FF
33773                 document.body.appendChild(dlg.el.dom);
33774                 d.animateTarget = null;
33775                 d.show(options.animEl);
33776             }
33777             return this;
33778         },
33779
33780         /**
33781          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33782          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33783          * and closing the message box when the process is complete.
33784          * @param {String} title The title bar text
33785          * @param {String} msg The message box body text
33786          * @return {Roo.MessageBox} This message box
33787          */
33788         progress : function(title, msg){
33789             this.show({
33790                 title : title,
33791                 msg : msg,
33792                 buttons: false,
33793                 progress:true,
33794                 closable:false,
33795                 minWidth: this.minProgressWidth,
33796                 modal : true
33797             });
33798             return this;
33799         },
33800
33801         /**
33802          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33803          * If a callback function is passed it will be called after the user clicks the button, and the
33804          * id of the button that was clicked will be passed as the only parameter to the callback
33805          * (could also be the top-right close button).
33806          * @param {String} title The title bar text
33807          * @param {String} msg The message box body text
33808          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33809          * @param {Object} scope (optional) The scope of the callback function
33810          * @return {Roo.MessageBox} This message box
33811          */
33812         alert : function(title, msg, fn, scope){
33813             this.show({
33814                 title : title,
33815                 msg : msg,
33816                 buttons: this.OK,
33817                 fn: fn,
33818                 scope : scope,
33819                 modal : true
33820             });
33821             return this;
33822         },
33823
33824         /**
33825          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33826          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33827          * You are responsible for closing the message box when the process is complete.
33828          * @param {String} msg The message box body text
33829          * @param {String} title (optional) The title bar text
33830          * @return {Roo.MessageBox} This message box
33831          */
33832         wait : function(msg, title){
33833             this.show({
33834                 title : title,
33835                 msg : msg,
33836                 buttons: false,
33837                 closable:false,
33838                 progress:true,
33839                 modal:true,
33840                 width:300,
33841                 wait:true
33842             });
33843             waitTimer = Roo.TaskMgr.start({
33844                 run: function(i){
33845                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33846                 },
33847                 interval: 1000
33848             });
33849             return this;
33850         },
33851
33852         /**
33853          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33854          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33855          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33856          * @param {String} title The title bar text
33857          * @param {String} msg The message box body text
33858          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33859          * @param {Object} scope (optional) The scope of the callback function
33860          * @return {Roo.MessageBox} This message box
33861          */
33862         confirm : function(title, msg, fn, scope){
33863             this.show({
33864                 title : title,
33865                 msg : msg,
33866                 buttons: this.YESNO,
33867                 fn: fn,
33868                 scope : scope,
33869                 modal : true
33870             });
33871             return this;
33872         },
33873
33874         /**
33875          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33876          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33877          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33878          * (could also be the top-right close button) and the text that was entered will be passed as the two
33879          * parameters to the callback.
33880          * @param {String} title The title bar text
33881          * @param {String} msg The message box body text
33882          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33883          * @param {Object} scope (optional) The scope of the callback function
33884          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33885          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33886          * @return {Roo.MessageBox} This message box
33887          */
33888         prompt : function(title, msg, fn, scope, multiline){
33889             this.show({
33890                 title : title,
33891                 msg : msg,
33892                 buttons: this.OKCANCEL,
33893                 fn: fn,
33894                 minWidth:250,
33895                 scope : scope,
33896                 prompt:true,
33897                 multiline: multiline,
33898                 modal : true
33899             });
33900             return this;
33901         },
33902
33903         /**
33904          * Button config that displays a single OK button
33905          * @type Object
33906          */
33907         OK : {ok:true},
33908         /**
33909          * Button config that displays Yes and No buttons
33910          * @type Object
33911          */
33912         YESNO : {yes:true, no:true},
33913         /**
33914          * Button config that displays OK and Cancel buttons
33915          * @type Object
33916          */
33917         OKCANCEL : {ok:true, cancel:true},
33918         /**
33919          * Button config that displays Yes, No and Cancel buttons
33920          * @type Object
33921          */
33922         YESNOCANCEL : {yes:true, no:true, cancel:true},
33923
33924         /**
33925          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33926          * @type Number
33927          */
33928         defaultTextHeight : 75,
33929         /**
33930          * The maximum width in pixels of the message box (defaults to 600)
33931          * @type Number
33932          */
33933         maxWidth : 600,
33934         /**
33935          * The minimum width in pixels of the message box (defaults to 100)
33936          * @type Number
33937          */
33938         minWidth : 100,
33939         /**
33940          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33941          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33942          * @type Number
33943          */
33944         minProgressWidth : 250,
33945         /**
33946          * An object containing the default button text strings that can be overriden for localized language support.
33947          * Supported properties are: ok, cancel, yes and no.
33948          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33949          * @type Object
33950          */
33951         buttonText : {
33952             ok : "OK",
33953             cancel : "Cancel",
33954             yes : "Yes",
33955             no : "No"
33956         }
33957     };
33958 }();
33959
33960 /**
33961  * Shorthand for {@link Roo.MessageBox}
33962  */
33963 Roo.Msg = Roo.MessageBox;/*
33964  * Based on:
33965  * Ext JS Library 1.1.1
33966  * Copyright(c) 2006-2007, Ext JS, LLC.
33967  *
33968  * Originally Released Under LGPL - original licence link has changed is not relivant.
33969  *
33970  * Fork - LGPL
33971  * <script type="text/javascript">
33972  */
33973 /**
33974  * @class Roo.QuickTips
33975  * Provides attractive and customizable tooltips for any element.
33976  * @singleton
33977  */
33978 Roo.QuickTips = function(){
33979     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33980     var ce, bd, xy, dd;
33981     var visible = false, disabled = true, inited = false;
33982     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33983     
33984     var onOver = function(e){
33985         if(disabled){
33986             return;
33987         }
33988         var t = e.getTarget();
33989         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33990             return;
33991         }
33992         if(ce && t == ce.el){
33993             clearTimeout(hideProc);
33994             return;
33995         }
33996         if(t && tagEls[t.id]){
33997             tagEls[t.id].el = t;
33998             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33999             return;
34000         }
34001         var ttp, et = Roo.fly(t);
34002         var ns = cfg.namespace;
34003         if(tm.interceptTitles && t.title){
34004             ttp = t.title;
34005             t.qtip = ttp;
34006             t.removeAttribute("title");
34007             e.preventDefault();
34008         }else{
34009             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34010         }
34011         if(ttp){
34012             showProc = show.defer(tm.showDelay, tm, [{
34013                 el: t, 
34014                 text: ttp.replace(/\\n/g,'<br/>'),
34015                 width: et.getAttributeNS(ns, cfg.width),
34016                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34017                 title: et.getAttributeNS(ns, cfg.title),
34018                     cls: et.getAttributeNS(ns, cfg.cls)
34019             }]);
34020         }
34021     };
34022     
34023     var onOut = function(e){
34024         clearTimeout(showProc);
34025         var t = e.getTarget();
34026         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34027             hideProc = setTimeout(hide, tm.hideDelay);
34028         }
34029     };
34030     
34031     var onMove = function(e){
34032         if(disabled){
34033             return;
34034         }
34035         xy = e.getXY();
34036         xy[1] += 18;
34037         if(tm.trackMouse && ce){
34038             el.setXY(xy);
34039         }
34040     };
34041     
34042     var onDown = function(e){
34043         clearTimeout(showProc);
34044         clearTimeout(hideProc);
34045         if(!e.within(el)){
34046             if(tm.hideOnClick){
34047                 hide();
34048                 tm.disable();
34049                 tm.enable.defer(100, tm);
34050             }
34051         }
34052     };
34053     
34054     var getPad = function(){
34055         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34056     };
34057
34058     var show = function(o){
34059         if(disabled){
34060             return;
34061         }
34062         clearTimeout(dismissProc);
34063         ce = o;
34064         if(removeCls){ // in case manually hidden
34065             el.removeClass(removeCls);
34066             removeCls = null;
34067         }
34068         if(ce.cls){
34069             el.addClass(ce.cls);
34070             removeCls = ce.cls;
34071         }
34072         if(ce.title){
34073             tipTitle.update(ce.title);
34074             tipTitle.show();
34075         }else{
34076             tipTitle.update('');
34077             tipTitle.hide();
34078         }
34079         el.dom.style.width  = tm.maxWidth+'px';
34080         //tipBody.dom.style.width = '';
34081         tipBodyText.update(o.text);
34082         var p = getPad(), w = ce.width;
34083         if(!w){
34084             var td = tipBodyText.dom;
34085             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34086             if(aw > tm.maxWidth){
34087                 w = tm.maxWidth;
34088             }else if(aw < tm.minWidth){
34089                 w = tm.minWidth;
34090             }else{
34091                 w = aw;
34092             }
34093         }
34094         //tipBody.setWidth(w);
34095         el.setWidth(parseInt(w, 10) + p);
34096         if(ce.autoHide === false){
34097             close.setDisplayed(true);
34098             if(dd){
34099                 dd.unlock();
34100             }
34101         }else{
34102             close.setDisplayed(false);
34103             if(dd){
34104                 dd.lock();
34105             }
34106         }
34107         if(xy){
34108             el.avoidY = xy[1]-18;
34109             el.setXY(xy);
34110         }
34111         if(tm.animate){
34112             el.setOpacity(.1);
34113             el.setStyle("visibility", "visible");
34114             el.fadeIn({callback: afterShow});
34115         }else{
34116             afterShow();
34117         }
34118     };
34119     
34120     var afterShow = function(){
34121         if(ce){
34122             el.show();
34123             esc.enable();
34124             if(tm.autoDismiss && ce.autoHide !== false){
34125                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34126             }
34127         }
34128     };
34129     
34130     var hide = function(noanim){
34131         clearTimeout(dismissProc);
34132         clearTimeout(hideProc);
34133         ce = null;
34134         if(el.isVisible()){
34135             esc.disable();
34136             if(noanim !== true && tm.animate){
34137                 el.fadeOut({callback: afterHide});
34138             }else{
34139                 afterHide();
34140             } 
34141         }
34142     };
34143     
34144     var afterHide = function(){
34145         el.hide();
34146         if(removeCls){
34147             el.removeClass(removeCls);
34148             removeCls = null;
34149         }
34150     };
34151     
34152     return {
34153         /**
34154         * @cfg {Number} minWidth
34155         * The minimum width of the quick tip (defaults to 40)
34156         */
34157        minWidth : 40,
34158         /**
34159         * @cfg {Number} maxWidth
34160         * The maximum width of the quick tip (defaults to 300)
34161         */
34162        maxWidth : 300,
34163         /**
34164         * @cfg {Boolean} interceptTitles
34165         * True to automatically use the element's DOM title value if available (defaults to false)
34166         */
34167        interceptTitles : false,
34168         /**
34169         * @cfg {Boolean} trackMouse
34170         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34171         */
34172        trackMouse : false,
34173         /**
34174         * @cfg {Boolean} hideOnClick
34175         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34176         */
34177        hideOnClick : true,
34178         /**
34179         * @cfg {Number} showDelay
34180         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34181         */
34182        showDelay : 500,
34183         /**
34184         * @cfg {Number} hideDelay
34185         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34186         */
34187        hideDelay : 200,
34188         /**
34189         * @cfg {Boolean} autoHide
34190         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34191         * Used in conjunction with hideDelay.
34192         */
34193        autoHide : true,
34194         /**
34195         * @cfg {Boolean}
34196         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34197         * (defaults to true).  Used in conjunction with autoDismissDelay.
34198         */
34199        autoDismiss : true,
34200         /**
34201         * @cfg {Number}
34202         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34203         */
34204        autoDismissDelay : 5000,
34205        /**
34206         * @cfg {Boolean} animate
34207         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34208         */
34209        animate : false,
34210
34211        /**
34212         * @cfg {String} title
34213         * Title text to display (defaults to '').  This can be any valid HTML markup.
34214         */
34215         title: '',
34216        /**
34217         * @cfg {String} text
34218         * Body text to display (defaults to '').  This can be any valid HTML markup.
34219         */
34220         text : '',
34221        /**
34222         * @cfg {String} cls
34223         * A CSS class to apply to the base quick tip element (defaults to '').
34224         */
34225         cls : '',
34226        /**
34227         * @cfg {Number} width
34228         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34229         * minWidth or maxWidth.
34230         */
34231         width : null,
34232
34233     /**
34234      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34235      * or display QuickTips in a page.
34236      */
34237        init : function(){
34238           tm = Roo.QuickTips;
34239           cfg = tm.tagConfig;
34240           if(!inited){
34241               if(!Roo.isReady){ // allow calling of init() before onReady
34242                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34243                   return;
34244               }
34245               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34246               el.fxDefaults = {stopFx: true};
34247               // maximum custom styling
34248               //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>');
34249               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>');              
34250               tipTitle = el.child('h3');
34251               tipTitle.enableDisplayMode("block");
34252               tipBody = el.child('div.x-tip-bd');
34253               tipBodyText = el.child('div.x-tip-bd-inner');
34254               //bdLeft = el.child('div.x-tip-bd-left');
34255               //bdRight = el.child('div.x-tip-bd-right');
34256               close = el.child('div.x-tip-close');
34257               close.enableDisplayMode("block");
34258               close.on("click", hide);
34259               var d = Roo.get(document);
34260               d.on("mousedown", onDown);
34261               d.on("mouseover", onOver);
34262               d.on("mouseout", onOut);
34263               d.on("mousemove", onMove);
34264               esc = d.addKeyListener(27, hide);
34265               esc.disable();
34266               if(Roo.dd.DD){
34267                   dd = el.initDD("default", null, {
34268                       onDrag : function(){
34269                           el.sync();  
34270                       }
34271                   });
34272                   dd.setHandleElId(tipTitle.id);
34273                   dd.lock();
34274               }
34275               inited = true;
34276           }
34277           this.enable(); 
34278        },
34279
34280     /**
34281      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34282      * are supported:
34283      * <pre>
34284 Property    Type                   Description
34285 ----------  ---------------------  ------------------------------------------------------------------------
34286 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34287      * </ul>
34288      * @param {Object} config The config object
34289      */
34290        register : function(config){
34291            var cs = config instanceof Array ? config : arguments;
34292            for(var i = 0, len = cs.length; i < len; i++) {
34293                var c = cs[i];
34294                var target = c.target;
34295                if(target){
34296                    if(target instanceof Array){
34297                        for(var j = 0, jlen = target.length; j < jlen; j++){
34298                            tagEls[target[j]] = c;
34299                        }
34300                    }else{
34301                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34302                    }
34303                }
34304            }
34305        },
34306
34307     /**
34308      * Removes this quick tip from its element and destroys it.
34309      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34310      */
34311        unregister : function(el){
34312            delete tagEls[Roo.id(el)];
34313        },
34314
34315     /**
34316      * Enable this quick tip.
34317      */
34318        enable : function(){
34319            if(inited && disabled){
34320                locks.pop();
34321                if(locks.length < 1){
34322                    disabled = false;
34323                }
34324            }
34325        },
34326
34327     /**
34328      * Disable this quick tip.
34329      */
34330        disable : function(){
34331           disabled = true;
34332           clearTimeout(showProc);
34333           clearTimeout(hideProc);
34334           clearTimeout(dismissProc);
34335           if(ce){
34336               hide(true);
34337           }
34338           locks.push(1);
34339        },
34340
34341     /**
34342      * Returns true if the quick tip is enabled, else false.
34343      */
34344        isEnabled : function(){
34345             return !disabled;
34346        },
34347
34348         // private
34349        tagConfig : {
34350            namespace : "roo", // was ext?? this may break..
34351            alt_namespace : "ext",
34352            attribute : "qtip",
34353            width : "width",
34354            target : "target",
34355            title : "qtitle",
34356            hide : "hide",
34357            cls : "qclass"
34358        }
34359    };
34360 }();
34361
34362 // backwards compat
34363 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373  
34374
34375 /**
34376  * @class Roo.tree.TreePanel
34377  * @extends Roo.data.Tree
34378
34379  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34380  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34381  * @cfg {Boolean} enableDD true to enable drag and drop
34382  * @cfg {Boolean} enableDrag true to enable just drag
34383  * @cfg {Boolean} enableDrop true to enable just drop
34384  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34385  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34386  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34387  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34388  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34389  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34390  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34391  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34392  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34393  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34394  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34395  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34396  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34397  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34398  * @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>
34399  * @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>
34400  * 
34401  * @constructor
34402  * @param {String/HTMLElement/Element} el The container element
34403  * @param {Object} config
34404  */
34405 Roo.tree.TreePanel = function(el, config){
34406     var root = false;
34407     var loader = false;
34408     if (config.root) {
34409         root = config.root;
34410         delete config.root;
34411     }
34412     if (config.loader) {
34413         loader = config.loader;
34414         delete config.loader;
34415     }
34416     
34417     Roo.apply(this, config);
34418     Roo.tree.TreePanel.superclass.constructor.call(this);
34419     this.el = Roo.get(el);
34420     this.el.addClass('x-tree');
34421     //console.log(root);
34422     if (root) {
34423         this.setRootNode( Roo.factory(root, Roo.tree));
34424     }
34425     if (loader) {
34426         this.loader = Roo.factory(loader, Roo.tree);
34427     }
34428    /**
34429     * Read-only. The id of the container element becomes this TreePanel's id.
34430     */
34431     this.id = this.el.id;
34432     this.addEvents({
34433         /**
34434         * @event beforeload
34435         * Fires before a node is loaded, return false to cancel
34436         * @param {Node} node The node being loaded
34437         */
34438         "beforeload" : true,
34439         /**
34440         * @event load
34441         * Fires when a node is loaded
34442         * @param {Node} node The node that was loaded
34443         */
34444         "load" : true,
34445         /**
34446         * @event textchange
34447         * Fires when the text for a node is changed
34448         * @param {Node} node The node
34449         * @param {String} text The new text
34450         * @param {String} oldText The old text
34451         */
34452         "textchange" : true,
34453         /**
34454         * @event beforeexpand
34455         * Fires before a node is expanded, return false to cancel.
34456         * @param {Node} node The node
34457         * @param {Boolean} deep
34458         * @param {Boolean} anim
34459         */
34460         "beforeexpand" : true,
34461         /**
34462         * @event beforecollapse
34463         * Fires before a node is collapsed, return false to cancel.
34464         * @param {Node} node The node
34465         * @param {Boolean} deep
34466         * @param {Boolean} anim
34467         */
34468         "beforecollapse" : true,
34469         /**
34470         * @event expand
34471         * Fires when a node is expanded
34472         * @param {Node} node The node
34473         */
34474         "expand" : true,
34475         /**
34476         * @event disabledchange
34477         * Fires when the disabled status of a node changes
34478         * @param {Node} node The node
34479         * @param {Boolean} disabled
34480         */
34481         "disabledchange" : true,
34482         /**
34483         * @event collapse
34484         * Fires when a node is collapsed
34485         * @param {Node} node The node
34486         */
34487         "collapse" : true,
34488         /**
34489         * @event beforeclick
34490         * Fires before click processing on a node. Return false to cancel the default action.
34491         * @param {Node} node The node
34492         * @param {Roo.EventObject} e The event object
34493         */
34494         "beforeclick":true,
34495         /**
34496         * @event checkchange
34497         * Fires when a node with a checkbox's checked property changes
34498         * @param {Node} this This node
34499         * @param {Boolean} checked
34500         */
34501         "checkchange":true,
34502         /**
34503         * @event click
34504         * Fires when a node is clicked
34505         * @param {Node} node The node
34506         * @param {Roo.EventObject} e The event object
34507         */
34508         "click":true,
34509         /**
34510         * @event dblclick
34511         * Fires when a node is double clicked
34512         * @param {Node} node The node
34513         * @param {Roo.EventObject} e The event object
34514         */
34515         "dblclick":true,
34516         /**
34517         * @event contextmenu
34518         * Fires when a node is right clicked
34519         * @param {Node} node The node
34520         * @param {Roo.EventObject} e The event object
34521         */
34522         "contextmenu":true,
34523         /**
34524         * @event beforechildrenrendered
34525         * Fires right before the child nodes for a node are rendered
34526         * @param {Node} node The node
34527         */
34528         "beforechildrenrendered":true,
34529         /**
34530         * @event startdrag
34531         * Fires when a node starts being dragged
34532         * @param {Roo.tree.TreePanel} this
34533         * @param {Roo.tree.TreeNode} node
34534         * @param {event} e The raw browser event
34535         */ 
34536        "startdrag" : true,
34537        /**
34538         * @event enddrag
34539         * Fires when a drag operation is complete
34540         * @param {Roo.tree.TreePanel} this
34541         * @param {Roo.tree.TreeNode} node
34542         * @param {event} e The raw browser event
34543         */
34544        "enddrag" : true,
34545        /**
34546         * @event dragdrop
34547         * Fires when a dragged node is dropped on a valid DD target
34548         * @param {Roo.tree.TreePanel} this
34549         * @param {Roo.tree.TreeNode} node
34550         * @param {DD} dd The dd it was dropped on
34551         * @param {event} e The raw browser event
34552         */
34553        "dragdrop" : true,
34554        /**
34555         * @event beforenodedrop
34556         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34557         * passed to handlers has the following properties:<br />
34558         * <ul style="padding:5px;padding-left:16px;">
34559         * <li>tree - The TreePanel</li>
34560         * <li>target - The node being targeted for the drop</li>
34561         * <li>data - The drag data from the drag source</li>
34562         * <li>point - The point of the drop - append, above or below</li>
34563         * <li>source - The drag source</li>
34564         * <li>rawEvent - Raw mouse event</li>
34565         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34566         * to be inserted by setting them on this object.</li>
34567         * <li>cancel - Set this to true to cancel the drop.</li>
34568         * </ul>
34569         * @param {Object} dropEvent
34570         */
34571        "beforenodedrop" : true,
34572        /**
34573         * @event nodedrop
34574         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34575         * passed to handlers has the following properties:<br />
34576         * <ul style="padding:5px;padding-left:16px;">
34577         * <li>tree - The TreePanel</li>
34578         * <li>target - The node being targeted for the drop</li>
34579         * <li>data - The drag data from the drag source</li>
34580         * <li>point - The point of the drop - append, above or below</li>
34581         * <li>source - The drag source</li>
34582         * <li>rawEvent - Raw mouse event</li>
34583         * <li>dropNode - Dropped node(s).</li>
34584         * </ul>
34585         * @param {Object} dropEvent
34586         */
34587        "nodedrop" : true,
34588         /**
34589         * @event nodedragover
34590         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34591         * passed to handlers has the following properties:<br />
34592         * <ul style="padding:5px;padding-left:16px;">
34593         * <li>tree - The TreePanel</li>
34594         * <li>target - The node being targeted for the drop</li>
34595         * <li>data - The drag data from the drag source</li>
34596         * <li>point - The point of the drop - append, above or below</li>
34597         * <li>source - The drag source</li>
34598         * <li>rawEvent - Raw mouse event</li>
34599         * <li>dropNode - Drop node(s) provided by the source.</li>
34600         * <li>cancel - Set this to true to signal drop not allowed.</li>
34601         * </ul>
34602         * @param {Object} dragOverEvent
34603         */
34604        "nodedragover" : true,
34605        /**
34606         * @event appendnode
34607         * Fires when append node to the tree
34608         * @param {Roo.tree.TreePanel} this
34609         * @param {Roo.tree.TreeNode} node
34610         * @param {Number} index The index of the newly appended node
34611         */
34612        "appendnode" : true
34613         
34614     });
34615     if(this.singleExpand){
34616        this.on("beforeexpand", this.restrictExpand, this);
34617     }
34618     if (this.editor) {
34619         this.editor.tree = this;
34620         this.editor = Roo.factory(this.editor, Roo.tree);
34621     }
34622     
34623     if (this.selModel) {
34624         this.selModel = Roo.factory(this.selModel, Roo.tree);
34625     }
34626    
34627 };
34628 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34629     rootVisible : true,
34630     animate: Roo.enableFx,
34631     lines : true,
34632     enableDD : false,
34633     hlDrop : Roo.enableFx,
34634   
34635     renderer: false,
34636     
34637     rendererTip: false,
34638     // private
34639     restrictExpand : function(node){
34640         var p = node.parentNode;
34641         if(p){
34642             if(p.expandedChild && p.expandedChild.parentNode == p){
34643                 p.expandedChild.collapse();
34644             }
34645             p.expandedChild = node;
34646         }
34647     },
34648
34649     // private override
34650     setRootNode : function(node){
34651         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34652         if(!this.rootVisible){
34653             node.ui = new Roo.tree.RootTreeNodeUI(node);
34654         }
34655         return node;
34656     },
34657
34658     /**
34659      * Returns the container element for this TreePanel
34660      */
34661     getEl : function(){
34662         return this.el;
34663     },
34664
34665     /**
34666      * Returns the default TreeLoader for this TreePanel
34667      */
34668     getLoader : function(){
34669         return this.loader;
34670     },
34671
34672     /**
34673      * Expand all nodes
34674      */
34675     expandAll : function(){
34676         this.root.expand(true);
34677     },
34678
34679     /**
34680      * Collapse all nodes
34681      */
34682     collapseAll : function(){
34683         this.root.collapse(true);
34684     },
34685
34686     /**
34687      * Returns the selection model used by this TreePanel
34688      */
34689     getSelectionModel : function(){
34690         if(!this.selModel){
34691             this.selModel = new Roo.tree.DefaultSelectionModel();
34692         }
34693         return this.selModel;
34694     },
34695
34696     /**
34697      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34698      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34699      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34700      * @return {Array}
34701      */
34702     getChecked : function(a, startNode){
34703         startNode = startNode || this.root;
34704         var r = [];
34705         var f = function(){
34706             if(this.attributes.checked){
34707                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34708             }
34709         }
34710         startNode.cascade(f);
34711         return r;
34712     },
34713
34714     /**
34715      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34716      * @param {String} path
34717      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34718      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34719      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34720      */
34721     expandPath : function(path, attr, callback){
34722         attr = attr || "id";
34723         var keys = path.split(this.pathSeparator);
34724         var curNode = this.root;
34725         if(curNode.attributes[attr] != keys[1]){ // invalid root
34726             if(callback){
34727                 callback(false, null);
34728             }
34729             return;
34730         }
34731         var index = 1;
34732         var f = function(){
34733             if(++index == keys.length){
34734                 if(callback){
34735                     callback(true, curNode);
34736                 }
34737                 return;
34738             }
34739             var c = curNode.findChild(attr, keys[index]);
34740             if(!c){
34741                 if(callback){
34742                     callback(false, curNode);
34743                 }
34744                 return;
34745             }
34746             curNode = c;
34747             c.expand(false, false, f);
34748         };
34749         curNode.expand(false, false, f);
34750     },
34751
34752     /**
34753      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34754      * @param {String} path
34755      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34756      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34757      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34758      */
34759     selectPath : function(path, attr, callback){
34760         attr = attr || "id";
34761         var keys = path.split(this.pathSeparator);
34762         var v = keys.pop();
34763         if(keys.length > 0){
34764             var f = function(success, node){
34765                 if(success && node){
34766                     var n = node.findChild(attr, v);
34767                     if(n){
34768                         n.select();
34769                         if(callback){
34770                             callback(true, n);
34771                         }
34772                     }else if(callback){
34773                         callback(false, n);
34774                     }
34775                 }else{
34776                     if(callback){
34777                         callback(false, n);
34778                     }
34779                 }
34780             };
34781             this.expandPath(keys.join(this.pathSeparator), attr, f);
34782         }else{
34783             this.root.select();
34784             if(callback){
34785                 callback(true, this.root);
34786             }
34787         }
34788     },
34789
34790     getTreeEl : function(){
34791         return this.el;
34792     },
34793
34794     /**
34795      * Trigger rendering of this TreePanel
34796      */
34797     render : function(){
34798         if (this.innerCt) {
34799             return this; // stop it rendering more than once!!
34800         }
34801         
34802         this.innerCt = this.el.createChild({tag:"ul",
34803                cls:"x-tree-root-ct " +
34804                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34805
34806         if(this.containerScroll){
34807             Roo.dd.ScrollManager.register(this.el);
34808         }
34809         if((this.enableDD || this.enableDrop) && !this.dropZone){
34810            /**
34811             * The dropZone used by this tree if drop is enabled
34812             * @type Roo.tree.TreeDropZone
34813             */
34814              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34815                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34816            });
34817         }
34818         if((this.enableDD || this.enableDrag) && !this.dragZone){
34819            /**
34820             * The dragZone used by this tree if drag is enabled
34821             * @type Roo.tree.TreeDragZone
34822             */
34823             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34824                ddGroup: this.ddGroup || "TreeDD",
34825                scroll: this.ddScroll
34826            });
34827         }
34828         this.getSelectionModel().init(this);
34829         if (!this.root) {
34830             Roo.log("ROOT not set in tree");
34831             return this;
34832         }
34833         this.root.render();
34834         if(!this.rootVisible){
34835             this.root.renderChildren();
34836         }
34837         return this;
34838     }
34839 });/*
34840  * Based on:
34841  * Ext JS Library 1.1.1
34842  * Copyright(c) 2006-2007, Ext JS, LLC.
34843  *
34844  * Originally Released Under LGPL - original licence link has changed is not relivant.
34845  *
34846  * Fork - LGPL
34847  * <script type="text/javascript">
34848  */
34849  
34850
34851 /**
34852  * @class Roo.tree.DefaultSelectionModel
34853  * @extends Roo.util.Observable
34854  * The default single selection for a TreePanel.
34855  * @param {Object} cfg Configuration
34856  */
34857 Roo.tree.DefaultSelectionModel = function(cfg){
34858    this.selNode = null;
34859    
34860    
34861    
34862    this.addEvents({
34863        /**
34864         * @event selectionchange
34865         * Fires when the selected node changes
34866         * @param {DefaultSelectionModel} this
34867         * @param {TreeNode} node the new selection
34868         */
34869        "selectionchange" : true,
34870
34871        /**
34872         * @event beforeselect
34873         * Fires before the selected node changes, return false to cancel the change
34874         * @param {DefaultSelectionModel} this
34875         * @param {TreeNode} node the new selection
34876         * @param {TreeNode} node the old selection
34877         */
34878        "beforeselect" : true
34879    });
34880    
34881     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34882 };
34883
34884 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34885     init : function(tree){
34886         this.tree = tree;
34887         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34888         tree.on("click", this.onNodeClick, this);
34889     },
34890     
34891     onNodeClick : function(node, e){
34892         if (e.ctrlKey && this.selNode == node)  {
34893             this.unselect(node);
34894             return;
34895         }
34896         this.select(node);
34897     },
34898     
34899     /**
34900      * Select a node.
34901      * @param {TreeNode} node The node to select
34902      * @return {TreeNode} The selected node
34903      */
34904     select : function(node){
34905         var last = this.selNode;
34906         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34907             if(last){
34908                 last.ui.onSelectedChange(false);
34909             }
34910             this.selNode = node;
34911             node.ui.onSelectedChange(true);
34912             this.fireEvent("selectionchange", this, node, last);
34913         }
34914         return node;
34915     },
34916     
34917     /**
34918      * Deselect a node.
34919      * @param {TreeNode} node The node to unselect
34920      */
34921     unselect : function(node){
34922         if(this.selNode == node){
34923             this.clearSelections();
34924         }    
34925     },
34926     
34927     /**
34928      * Clear all selections
34929      */
34930     clearSelections : function(){
34931         var n = this.selNode;
34932         if(n){
34933             n.ui.onSelectedChange(false);
34934             this.selNode = null;
34935             this.fireEvent("selectionchange", this, null);
34936         }
34937         return n;
34938     },
34939     
34940     /**
34941      * Get the selected node
34942      * @return {TreeNode} The selected node
34943      */
34944     getSelectedNode : function(){
34945         return this.selNode;    
34946     },
34947     
34948     /**
34949      * Returns true if the node is selected
34950      * @param {TreeNode} node The node to check
34951      * @return {Boolean}
34952      */
34953     isSelected : function(node){
34954         return this.selNode == node;  
34955     },
34956
34957     /**
34958      * Selects the node above the selected node in the tree, intelligently walking the nodes
34959      * @return TreeNode The new selection
34960      */
34961     selectPrevious : function(){
34962         var s = this.selNode || this.lastSelNode;
34963         if(!s){
34964             return null;
34965         }
34966         var ps = s.previousSibling;
34967         if(ps){
34968             if(!ps.isExpanded() || ps.childNodes.length < 1){
34969                 return this.select(ps);
34970             } else{
34971                 var lc = ps.lastChild;
34972                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34973                     lc = lc.lastChild;
34974                 }
34975                 return this.select(lc);
34976             }
34977         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34978             return this.select(s.parentNode);
34979         }
34980         return null;
34981     },
34982
34983     /**
34984      * Selects the node above the selected node in the tree, intelligently walking the nodes
34985      * @return TreeNode The new selection
34986      */
34987     selectNext : function(){
34988         var s = this.selNode || this.lastSelNode;
34989         if(!s){
34990             return null;
34991         }
34992         if(s.firstChild && s.isExpanded()){
34993              return this.select(s.firstChild);
34994          }else if(s.nextSibling){
34995              return this.select(s.nextSibling);
34996          }else if(s.parentNode){
34997             var newS = null;
34998             s.parentNode.bubble(function(){
34999                 if(this.nextSibling){
35000                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35001                     return false;
35002                 }
35003             });
35004             return newS;
35005          }
35006         return null;
35007     },
35008
35009     onKeyDown : function(e){
35010         var s = this.selNode || this.lastSelNode;
35011         // undesirable, but required
35012         var sm = this;
35013         if(!s){
35014             return;
35015         }
35016         var k = e.getKey();
35017         switch(k){
35018              case e.DOWN:
35019                  e.stopEvent();
35020                  this.selectNext();
35021              break;
35022              case e.UP:
35023                  e.stopEvent();
35024                  this.selectPrevious();
35025              break;
35026              case e.RIGHT:
35027                  e.preventDefault();
35028                  if(s.hasChildNodes()){
35029                      if(!s.isExpanded()){
35030                          s.expand();
35031                      }else if(s.firstChild){
35032                          this.select(s.firstChild, e);
35033                      }
35034                  }
35035              break;
35036              case e.LEFT:
35037                  e.preventDefault();
35038                  if(s.hasChildNodes() && s.isExpanded()){
35039                      s.collapse();
35040                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35041                      this.select(s.parentNode, e);
35042                  }
35043              break;
35044         };
35045     }
35046 });
35047
35048 /**
35049  * @class Roo.tree.MultiSelectionModel
35050  * @extends Roo.util.Observable
35051  * Multi selection for a TreePanel.
35052  * @param {Object} cfg Configuration
35053  */
35054 Roo.tree.MultiSelectionModel = function(){
35055    this.selNodes = [];
35056    this.selMap = {};
35057    this.addEvents({
35058        /**
35059         * @event selectionchange
35060         * Fires when the selected nodes change
35061         * @param {MultiSelectionModel} this
35062         * @param {Array} nodes Array of the selected nodes
35063         */
35064        "selectionchange" : true
35065    });
35066    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35067    
35068 };
35069
35070 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35071     init : function(tree){
35072         this.tree = tree;
35073         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35074         tree.on("click", this.onNodeClick, this);
35075     },
35076     
35077     onNodeClick : function(node, e){
35078         this.select(node, e, e.ctrlKey);
35079     },
35080     
35081     /**
35082      * Select a node.
35083      * @param {TreeNode} node The node to select
35084      * @param {EventObject} e (optional) An event associated with the selection
35085      * @param {Boolean} keepExisting True to retain existing selections
35086      * @return {TreeNode} The selected node
35087      */
35088     select : function(node, e, keepExisting){
35089         if(keepExisting !== true){
35090             this.clearSelections(true);
35091         }
35092         if(this.isSelected(node)){
35093             this.lastSelNode = node;
35094             return node;
35095         }
35096         this.selNodes.push(node);
35097         this.selMap[node.id] = node;
35098         this.lastSelNode = node;
35099         node.ui.onSelectedChange(true);
35100         this.fireEvent("selectionchange", this, this.selNodes);
35101         return node;
35102     },
35103     
35104     /**
35105      * Deselect a node.
35106      * @param {TreeNode} node The node to unselect
35107      */
35108     unselect : function(node){
35109         if(this.selMap[node.id]){
35110             node.ui.onSelectedChange(false);
35111             var sn = this.selNodes;
35112             var index = -1;
35113             if(sn.indexOf){
35114                 index = sn.indexOf(node);
35115             }else{
35116                 for(var i = 0, len = sn.length; i < len; i++){
35117                     if(sn[i] == node){
35118                         index = i;
35119                         break;
35120                     }
35121                 }
35122             }
35123             if(index != -1){
35124                 this.selNodes.splice(index, 1);
35125             }
35126             delete this.selMap[node.id];
35127             this.fireEvent("selectionchange", this, this.selNodes);
35128         }
35129     },
35130     
35131     /**
35132      * Clear all selections
35133      */
35134     clearSelections : function(suppressEvent){
35135         var sn = this.selNodes;
35136         if(sn.length > 0){
35137             for(var i = 0, len = sn.length; i < len; i++){
35138                 sn[i].ui.onSelectedChange(false);
35139             }
35140             this.selNodes = [];
35141             this.selMap = {};
35142             if(suppressEvent !== true){
35143                 this.fireEvent("selectionchange", this, this.selNodes);
35144             }
35145         }
35146     },
35147     
35148     /**
35149      * Returns true if the node is selected
35150      * @param {TreeNode} node The node to check
35151      * @return {Boolean}
35152      */
35153     isSelected : function(node){
35154         return this.selMap[node.id] ? true : false;  
35155     },
35156     
35157     /**
35158      * Returns an array of the selected nodes
35159      * @return {Array}
35160      */
35161     getSelectedNodes : function(){
35162         return this.selNodes;    
35163     },
35164
35165     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35166
35167     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35168
35169     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35170 });/*
35171  * Based on:
35172  * Ext JS Library 1.1.1
35173  * Copyright(c) 2006-2007, Ext JS, LLC.
35174  *
35175  * Originally Released Under LGPL - original licence link has changed is not relivant.
35176  *
35177  * Fork - LGPL
35178  * <script type="text/javascript">
35179  */
35180  
35181 /**
35182  * @class Roo.tree.TreeNode
35183  * @extends Roo.data.Node
35184  * @cfg {String} text The text for this node
35185  * @cfg {Boolean} expanded true to start the node expanded
35186  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35187  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35188  * @cfg {Boolean} disabled true to start the node disabled
35189  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35190  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35191  * @cfg {String} cls A css class to be added to the node
35192  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35193  * @cfg {String} href URL of the link used for the node (defaults to #)
35194  * @cfg {String} hrefTarget target frame for the link
35195  * @cfg {String} qtip An Ext QuickTip for the node
35196  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35197  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35198  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35199  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35200  * (defaults to undefined with no checkbox rendered)
35201  * @constructor
35202  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35203  */
35204 Roo.tree.TreeNode = function(attributes){
35205     attributes = attributes || {};
35206     if(typeof attributes == "string"){
35207         attributes = {text: attributes};
35208     }
35209     this.childrenRendered = false;
35210     this.rendered = false;
35211     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35212     this.expanded = attributes.expanded === true;
35213     this.isTarget = attributes.isTarget !== false;
35214     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35215     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35216
35217     /**
35218      * Read-only. The text for this node. To change it use setText().
35219      * @type String
35220      */
35221     this.text = attributes.text;
35222     /**
35223      * True if this node is disabled.
35224      * @type Boolean
35225      */
35226     this.disabled = attributes.disabled === true;
35227
35228     this.addEvents({
35229         /**
35230         * @event textchange
35231         * Fires when the text for this node is changed
35232         * @param {Node} this This node
35233         * @param {String} text The new text
35234         * @param {String} oldText The old text
35235         */
35236         "textchange" : true,
35237         /**
35238         * @event beforeexpand
35239         * Fires before this node is expanded, return false to cancel.
35240         * @param {Node} this This node
35241         * @param {Boolean} deep
35242         * @param {Boolean} anim
35243         */
35244         "beforeexpand" : true,
35245         /**
35246         * @event beforecollapse
35247         * Fires before this node is collapsed, return false to cancel.
35248         * @param {Node} this This node
35249         * @param {Boolean} deep
35250         * @param {Boolean} anim
35251         */
35252         "beforecollapse" : true,
35253         /**
35254         * @event expand
35255         * Fires when this node is expanded
35256         * @param {Node} this This node
35257         */
35258         "expand" : true,
35259         /**
35260         * @event disabledchange
35261         * Fires when the disabled status of this node changes
35262         * @param {Node} this This node
35263         * @param {Boolean} disabled
35264         */
35265         "disabledchange" : true,
35266         /**
35267         * @event collapse
35268         * Fires when this node is collapsed
35269         * @param {Node} this This node
35270         */
35271         "collapse" : true,
35272         /**
35273         * @event beforeclick
35274         * Fires before click processing. Return false to cancel the default action.
35275         * @param {Node} this This node
35276         * @param {Roo.EventObject} e The event object
35277         */
35278         "beforeclick":true,
35279         /**
35280         * @event checkchange
35281         * Fires when a node with a checkbox's checked property changes
35282         * @param {Node} this This node
35283         * @param {Boolean} checked
35284         */
35285         "checkchange":true,
35286         /**
35287         * @event click
35288         * Fires when this node is clicked
35289         * @param {Node} this This node
35290         * @param {Roo.EventObject} e The event object
35291         */
35292         "click":true,
35293         /**
35294         * @event dblclick
35295         * Fires when this node is double clicked
35296         * @param {Node} this This node
35297         * @param {Roo.EventObject} e The event object
35298         */
35299         "dblclick":true,
35300         /**
35301         * @event contextmenu
35302         * Fires when this node is right clicked
35303         * @param {Node} this This node
35304         * @param {Roo.EventObject} e The event object
35305         */
35306         "contextmenu":true,
35307         /**
35308         * @event beforechildrenrendered
35309         * Fires right before the child nodes for this node are rendered
35310         * @param {Node} this This node
35311         */
35312         "beforechildrenrendered":true
35313     });
35314
35315     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35316
35317     /**
35318      * Read-only. The UI for this node
35319      * @type TreeNodeUI
35320      */
35321     this.ui = new uiClass(this);
35322     
35323     // finally support items[]
35324     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35325         return;
35326     }
35327     
35328     
35329     Roo.each(this.attributes.items, function(c) {
35330         this.appendChild(Roo.factory(c,Roo.Tree));
35331     }, this);
35332     delete this.attributes.items;
35333     
35334     
35335     
35336 };
35337 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35338     preventHScroll: true,
35339     /**
35340      * Returns true if this node is expanded
35341      * @return {Boolean}
35342      */
35343     isExpanded : function(){
35344         return this.expanded;
35345     },
35346
35347     /**
35348      * Returns the UI object for this node
35349      * @return {TreeNodeUI}
35350      */
35351     getUI : function(){
35352         return this.ui;
35353     },
35354
35355     // private override
35356     setFirstChild : function(node){
35357         var of = this.firstChild;
35358         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35359         if(this.childrenRendered && of && node != of){
35360             of.renderIndent(true, true);
35361         }
35362         if(this.rendered){
35363             this.renderIndent(true, true);
35364         }
35365     },
35366
35367     // private override
35368     setLastChild : function(node){
35369         var ol = this.lastChild;
35370         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35371         if(this.childrenRendered && ol && node != ol){
35372             ol.renderIndent(true, true);
35373         }
35374         if(this.rendered){
35375             this.renderIndent(true, true);
35376         }
35377     },
35378
35379     // these methods are overridden to provide lazy rendering support
35380     // private override
35381     appendChild : function()
35382     {
35383         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35384         if(node && this.childrenRendered){
35385             node.render();
35386         }
35387         this.ui.updateExpandIcon();
35388         return node;
35389     },
35390
35391     // private override
35392     removeChild : function(node){
35393         this.ownerTree.getSelectionModel().unselect(node);
35394         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35395         // if it's been rendered remove dom node
35396         if(this.childrenRendered){
35397             node.ui.remove();
35398         }
35399         if(this.childNodes.length < 1){
35400             this.collapse(false, false);
35401         }else{
35402             this.ui.updateExpandIcon();
35403         }
35404         if(!this.firstChild) {
35405             this.childrenRendered = false;
35406         }
35407         return node;
35408     },
35409
35410     // private override
35411     insertBefore : function(node, refNode){
35412         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35413         if(newNode && refNode && this.childrenRendered){
35414             node.render();
35415         }
35416         this.ui.updateExpandIcon();
35417         return newNode;
35418     },
35419
35420     /**
35421      * Sets the text for this node
35422      * @param {String} text
35423      */
35424     setText : function(text){
35425         var oldText = this.text;
35426         this.text = text;
35427         this.attributes.text = text;
35428         if(this.rendered){ // event without subscribing
35429             this.ui.onTextChange(this, text, oldText);
35430         }
35431         this.fireEvent("textchange", this, text, oldText);
35432     },
35433
35434     /**
35435      * Triggers selection of this node
35436      */
35437     select : function(){
35438         this.getOwnerTree().getSelectionModel().select(this);
35439     },
35440
35441     /**
35442      * Triggers deselection of this node
35443      */
35444     unselect : function(){
35445         this.getOwnerTree().getSelectionModel().unselect(this);
35446     },
35447
35448     /**
35449      * Returns true if this node is selected
35450      * @return {Boolean}
35451      */
35452     isSelected : function(){
35453         return this.getOwnerTree().getSelectionModel().isSelected(this);
35454     },
35455
35456     /**
35457      * Expand this node.
35458      * @param {Boolean} deep (optional) True to expand all children as well
35459      * @param {Boolean} anim (optional) false to cancel the default animation
35460      * @param {Function} callback (optional) A callback to be called when
35461      * expanding this node completes (does not wait for deep expand to complete).
35462      * Called with 1 parameter, this node.
35463      */
35464     expand : function(deep, anim, callback){
35465         if(!this.expanded){
35466             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35467                 return;
35468             }
35469             if(!this.childrenRendered){
35470                 this.renderChildren();
35471             }
35472             this.expanded = true;
35473             
35474             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35475                 this.ui.animExpand(function(){
35476                     this.fireEvent("expand", this);
35477                     if(typeof callback == "function"){
35478                         callback(this);
35479                     }
35480                     if(deep === true){
35481                         this.expandChildNodes(true);
35482                     }
35483                 }.createDelegate(this));
35484                 return;
35485             }else{
35486                 this.ui.expand();
35487                 this.fireEvent("expand", this);
35488                 if(typeof callback == "function"){
35489                     callback(this);
35490                 }
35491             }
35492         }else{
35493            if(typeof callback == "function"){
35494                callback(this);
35495            }
35496         }
35497         if(deep === true){
35498             this.expandChildNodes(true);
35499         }
35500     },
35501
35502     isHiddenRoot : function(){
35503         return this.isRoot && !this.getOwnerTree().rootVisible;
35504     },
35505
35506     /**
35507      * Collapse this node.
35508      * @param {Boolean} deep (optional) True to collapse all children as well
35509      * @param {Boolean} anim (optional) false to cancel the default animation
35510      */
35511     collapse : function(deep, anim){
35512         if(this.expanded && !this.isHiddenRoot()){
35513             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35514                 return;
35515             }
35516             this.expanded = false;
35517             if((this.getOwnerTree().animate && anim !== false) || anim){
35518                 this.ui.animCollapse(function(){
35519                     this.fireEvent("collapse", this);
35520                     if(deep === true){
35521                         this.collapseChildNodes(true);
35522                     }
35523                 }.createDelegate(this));
35524                 return;
35525             }else{
35526                 this.ui.collapse();
35527                 this.fireEvent("collapse", this);
35528             }
35529         }
35530         if(deep === true){
35531             var cs = this.childNodes;
35532             for(var i = 0, len = cs.length; i < len; i++) {
35533                 cs[i].collapse(true, false);
35534             }
35535         }
35536     },
35537
35538     // private
35539     delayedExpand : function(delay){
35540         if(!this.expandProcId){
35541             this.expandProcId = this.expand.defer(delay, this);
35542         }
35543     },
35544
35545     // private
35546     cancelExpand : function(){
35547         if(this.expandProcId){
35548             clearTimeout(this.expandProcId);
35549         }
35550         this.expandProcId = false;
35551     },
35552
35553     /**
35554      * Toggles expanded/collapsed state of the node
35555      */
35556     toggle : function(){
35557         if(this.expanded){
35558             this.collapse();
35559         }else{
35560             this.expand();
35561         }
35562     },
35563
35564     /**
35565      * Ensures all parent nodes are expanded
35566      */
35567     ensureVisible : function(callback){
35568         var tree = this.getOwnerTree();
35569         tree.expandPath(this.parentNode.getPath(), false, function(){
35570             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35571             Roo.callback(callback);
35572         }.createDelegate(this));
35573     },
35574
35575     /**
35576      * Expand all child nodes
35577      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35578      */
35579     expandChildNodes : function(deep){
35580         var cs = this.childNodes;
35581         for(var i = 0, len = cs.length; i < len; i++) {
35582                 cs[i].expand(deep);
35583         }
35584     },
35585
35586     /**
35587      * Collapse all child nodes
35588      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35589      */
35590     collapseChildNodes : function(deep){
35591         var cs = this.childNodes;
35592         for(var i = 0, len = cs.length; i < len; i++) {
35593                 cs[i].collapse(deep);
35594         }
35595     },
35596
35597     /**
35598      * Disables this node
35599      */
35600     disable : function(){
35601         this.disabled = true;
35602         this.unselect();
35603         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35604             this.ui.onDisableChange(this, true);
35605         }
35606         this.fireEvent("disabledchange", this, true);
35607     },
35608
35609     /**
35610      * Enables this node
35611      */
35612     enable : function(){
35613         this.disabled = false;
35614         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35615             this.ui.onDisableChange(this, false);
35616         }
35617         this.fireEvent("disabledchange", this, false);
35618     },
35619
35620     // private
35621     renderChildren : function(suppressEvent){
35622         if(suppressEvent !== false){
35623             this.fireEvent("beforechildrenrendered", this);
35624         }
35625         var cs = this.childNodes;
35626         for(var i = 0, len = cs.length; i < len; i++){
35627             cs[i].render(true);
35628         }
35629         this.childrenRendered = true;
35630     },
35631
35632     // private
35633     sort : function(fn, scope){
35634         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35635         if(this.childrenRendered){
35636             var cs = this.childNodes;
35637             for(var i = 0, len = cs.length; i < len; i++){
35638                 cs[i].render(true);
35639             }
35640         }
35641     },
35642
35643     // private
35644     render : function(bulkRender){
35645         this.ui.render(bulkRender);
35646         if(!this.rendered){
35647             this.rendered = true;
35648             if(this.expanded){
35649                 this.expanded = false;
35650                 this.expand(false, false);
35651             }
35652         }
35653     },
35654
35655     // private
35656     renderIndent : function(deep, refresh){
35657         if(refresh){
35658             this.ui.childIndent = null;
35659         }
35660         this.ui.renderIndent();
35661         if(deep === true && this.childrenRendered){
35662             var cs = this.childNodes;
35663             for(var i = 0, len = cs.length; i < len; i++){
35664                 cs[i].renderIndent(true, refresh);
35665             }
35666         }
35667     }
35668 });/*
35669  * Based on:
35670  * Ext JS Library 1.1.1
35671  * Copyright(c) 2006-2007, Ext JS, LLC.
35672  *
35673  * Originally Released Under LGPL - original licence link has changed is not relivant.
35674  *
35675  * Fork - LGPL
35676  * <script type="text/javascript">
35677  */
35678  
35679 /**
35680  * @class Roo.tree.AsyncTreeNode
35681  * @extends Roo.tree.TreeNode
35682  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35683  * @constructor
35684  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35685  */
35686  Roo.tree.AsyncTreeNode = function(config){
35687     this.loaded = false;
35688     this.loading = false;
35689     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35690     /**
35691     * @event beforeload
35692     * Fires before this node is loaded, return false to cancel
35693     * @param {Node} this This node
35694     */
35695     this.addEvents({'beforeload':true, 'load': true});
35696     /**
35697     * @event load
35698     * Fires when this node is loaded
35699     * @param {Node} this This node
35700     */
35701     /**
35702      * The loader used by this node (defaults to using the tree's defined loader)
35703      * @type TreeLoader
35704      * @property loader
35705      */
35706 };
35707 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35708     expand : function(deep, anim, callback){
35709         if(this.loading){ // if an async load is already running, waiting til it's done
35710             var timer;
35711             var f = function(){
35712                 if(!this.loading){ // done loading
35713                     clearInterval(timer);
35714                     this.expand(deep, anim, callback);
35715                 }
35716             }.createDelegate(this);
35717             timer = setInterval(f, 200);
35718             return;
35719         }
35720         if(!this.loaded){
35721             if(this.fireEvent("beforeload", this) === false){
35722                 return;
35723             }
35724             this.loading = true;
35725             this.ui.beforeLoad(this);
35726             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35727             if(loader){
35728                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35729                 return;
35730             }
35731         }
35732         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35733     },
35734     
35735     /**
35736      * Returns true if this node is currently loading
35737      * @return {Boolean}
35738      */
35739     isLoading : function(){
35740         return this.loading;  
35741     },
35742     
35743     loadComplete : function(deep, anim, callback){
35744         this.loading = false;
35745         this.loaded = true;
35746         this.ui.afterLoad(this);
35747         this.fireEvent("load", this);
35748         this.expand(deep, anim, callback);
35749     },
35750     
35751     /**
35752      * Returns true if this node has been loaded
35753      * @return {Boolean}
35754      */
35755     isLoaded : function(){
35756         return this.loaded;
35757     },
35758     
35759     hasChildNodes : function(){
35760         if(!this.isLeaf() && !this.loaded){
35761             return true;
35762         }else{
35763             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35764         }
35765     },
35766
35767     /**
35768      * Trigger a reload for this node
35769      * @param {Function} callback
35770      */
35771     reload : function(callback){
35772         this.collapse(false, false);
35773         while(this.firstChild){
35774             this.removeChild(this.firstChild);
35775         }
35776         this.childrenRendered = false;
35777         this.loaded = false;
35778         if(this.isHiddenRoot()){
35779             this.expanded = false;
35780         }
35781         this.expand(false, false, callback);
35782     }
35783 });/*
35784  * Based on:
35785  * Ext JS Library 1.1.1
35786  * Copyright(c) 2006-2007, Ext JS, LLC.
35787  *
35788  * Originally Released Under LGPL - original licence link has changed is not relivant.
35789  *
35790  * Fork - LGPL
35791  * <script type="text/javascript">
35792  */
35793  
35794 /**
35795  * @class Roo.tree.TreeNodeUI
35796  * @constructor
35797  * @param {Object} node The node to render
35798  * The TreeNode UI implementation is separate from the
35799  * tree implementation. Unless you are customizing the tree UI,
35800  * you should never have to use this directly.
35801  */
35802 Roo.tree.TreeNodeUI = function(node){
35803     this.node = node;
35804     this.rendered = false;
35805     this.animating = false;
35806     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35807 };
35808
35809 Roo.tree.TreeNodeUI.prototype = {
35810     removeChild : function(node){
35811         if(this.rendered){
35812             this.ctNode.removeChild(node.ui.getEl());
35813         }
35814     },
35815
35816     beforeLoad : function(){
35817          this.addClass("x-tree-node-loading");
35818     },
35819
35820     afterLoad : function(){
35821          this.removeClass("x-tree-node-loading");
35822     },
35823
35824     onTextChange : function(node, text, oldText){
35825         if(this.rendered){
35826             this.textNode.innerHTML = text;
35827         }
35828     },
35829
35830     onDisableChange : function(node, state){
35831         this.disabled = state;
35832         if(state){
35833             this.addClass("x-tree-node-disabled");
35834         }else{
35835             this.removeClass("x-tree-node-disabled");
35836         }
35837     },
35838
35839     onSelectedChange : function(state){
35840         if(state){
35841             this.focus();
35842             this.addClass("x-tree-selected");
35843         }else{
35844             //this.blur();
35845             this.removeClass("x-tree-selected");
35846         }
35847     },
35848
35849     onMove : function(tree, node, oldParent, newParent, index, refNode){
35850         this.childIndent = null;
35851         if(this.rendered){
35852             var targetNode = newParent.ui.getContainer();
35853             if(!targetNode){//target not rendered
35854                 this.holder = document.createElement("div");
35855                 this.holder.appendChild(this.wrap);
35856                 return;
35857             }
35858             var insertBefore = refNode ? refNode.ui.getEl() : null;
35859             if(insertBefore){
35860                 targetNode.insertBefore(this.wrap, insertBefore);
35861             }else{
35862                 targetNode.appendChild(this.wrap);
35863             }
35864             this.node.renderIndent(true);
35865         }
35866     },
35867
35868     addClass : function(cls){
35869         if(this.elNode){
35870             Roo.fly(this.elNode).addClass(cls);
35871         }
35872     },
35873
35874     removeClass : function(cls){
35875         if(this.elNode){
35876             Roo.fly(this.elNode).removeClass(cls);
35877         }
35878     },
35879
35880     remove : function(){
35881         if(this.rendered){
35882             this.holder = document.createElement("div");
35883             this.holder.appendChild(this.wrap);
35884         }
35885     },
35886
35887     fireEvent : function(){
35888         return this.node.fireEvent.apply(this.node, arguments);
35889     },
35890
35891     initEvents : function(){
35892         this.node.on("move", this.onMove, this);
35893         var E = Roo.EventManager;
35894         var a = this.anchor;
35895
35896         var el = Roo.fly(a, '_treeui');
35897
35898         if(Roo.isOpera){ // opera render bug ignores the CSS
35899             el.setStyle("text-decoration", "none");
35900         }
35901
35902         el.on("click", this.onClick, this);
35903         el.on("dblclick", this.onDblClick, this);
35904
35905         if(this.checkbox){
35906             Roo.EventManager.on(this.checkbox,
35907                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35908         }
35909
35910         el.on("contextmenu", this.onContextMenu, this);
35911
35912         var icon = Roo.fly(this.iconNode);
35913         icon.on("click", this.onClick, this);
35914         icon.on("dblclick", this.onDblClick, this);
35915         icon.on("contextmenu", this.onContextMenu, this);
35916         E.on(this.ecNode, "click", this.ecClick, this, true);
35917
35918         if(this.node.disabled){
35919             this.addClass("x-tree-node-disabled");
35920         }
35921         if(this.node.hidden){
35922             this.addClass("x-tree-node-disabled");
35923         }
35924         var ot = this.node.getOwnerTree();
35925         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35926         if(dd && (!this.node.isRoot || ot.rootVisible)){
35927             Roo.dd.Registry.register(this.elNode, {
35928                 node: this.node,
35929                 handles: this.getDDHandles(),
35930                 isHandle: false
35931             });
35932         }
35933     },
35934
35935     getDDHandles : function(){
35936         return [this.iconNode, this.textNode];
35937     },
35938
35939     hide : function(){
35940         if(this.rendered){
35941             this.wrap.style.display = "none";
35942         }
35943     },
35944
35945     show : function(){
35946         if(this.rendered){
35947             this.wrap.style.display = "";
35948         }
35949     },
35950
35951     onContextMenu : function(e){
35952         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35953             e.preventDefault();
35954             this.focus();
35955             this.fireEvent("contextmenu", this.node, e);
35956         }
35957     },
35958
35959     onClick : function(e){
35960         if(this.dropping){
35961             e.stopEvent();
35962             return;
35963         }
35964         if(this.fireEvent("beforeclick", this.node, e) !== false){
35965             if(!this.disabled && this.node.attributes.href){
35966                 this.fireEvent("click", this.node, e);
35967                 return;
35968             }
35969             e.preventDefault();
35970             if(this.disabled){
35971                 return;
35972             }
35973
35974             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35975                 this.node.toggle();
35976             }
35977
35978             this.fireEvent("click", this.node, e);
35979         }else{
35980             e.stopEvent();
35981         }
35982     },
35983
35984     onDblClick : function(e){
35985         e.preventDefault();
35986         if(this.disabled){
35987             return;
35988         }
35989         if(this.checkbox){
35990             this.toggleCheck();
35991         }
35992         if(!this.animating && this.node.hasChildNodes()){
35993             this.node.toggle();
35994         }
35995         this.fireEvent("dblclick", this.node, e);
35996     },
35997
35998     onCheckChange : function(){
35999         var checked = this.checkbox.checked;
36000         this.node.attributes.checked = checked;
36001         this.fireEvent('checkchange', this.node, checked);
36002     },
36003
36004     ecClick : function(e){
36005         if(!this.animating && this.node.hasChildNodes()){
36006             this.node.toggle();
36007         }
36008     },
36009
36010     startDrop : function(){
36011         this.dropping = true;
36012     },
36013
36014     // delayed drop so the click event doesn't get fired on a drop
36015     endDrop : function(){
36016        setTimeout(function(){
36017            this.dropping = false;
36018        }.createDelegate(this), 50);
36019     },
36020
36021     expand : function(){
36022         this.updateExpandIcon();
36023         this.ctNode.style.display = "";
36024     },
36025
36026     focus : function(){
36027         if(!this.node.preventHScroll){
36028             try{this.anchor.focus();
36029             }catch(e){}
36030         }else if(!Roo.isIE){
36031             try{
36032                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36033                 var l = noscroll.scrollLeft;
36034                 this.anchor.focus();
36035                 noscroll.scrollLeft = l;
36036             }catch(e){}
36037         }
36038     },
36039
36040     toggleCheck : function(value){
36041         var cb = this.checkbox;
36042         if(cb){
36043             cb.checked = (value === undefined ? !cb.checked : value);
36044         }
36045     },
36046
36047     blur : function(){
36048         try{
36049             this.anchor.blur();
36050         }catch(e){}
36051     },
36052
36053     animExpand : function(callback){
36054         var ct = Roo.get(this.ctNode);
36055         ct.stopFx();
36056         if(!this.node.hasChildNodes()){
36057             this.updateExpandIcon();
36058             this.ctNode.style.display = "";
36059             Roo.callback(callback);
36060             return;
36061         }
36062         this.animating = true;
36063         this.updateExpandIcon();
36064
36065         ct.slideIn('t', {
36066            callback : function(){
36067                this.animating = false;
36068                Roo.callback(callback);
36069             },
36070             scope: this,
36071             duration: this.node.ownerTree.duration || .25
36072         });
36073     },
36074
36075     highlight : function(){
36076         var tree = this.node.getOwnerTree();
36077         Roo.fly(this.wrap).highlight(
36078             tree.hlColor || "C3DAF9",
36079             {endColor: tree.hlBaseColor}
36080         );
36081     },
36082
36083     collapse : function(){
36084         this.updateExpandIcon();
36085         this.ctNode.style.display = "none";
36086     },
36087
36088     animCollapse : function(callback){
36089         var ct = Roo.get(this.ctNode);
36090         ct.enableDisplayMode('block');
36091         ct.stopFx();
36092
36093         this.animating = true;
36094         this.updateExpandIcon();
36095
36096         ct.slideOut('t', {
36097             callback : function(){
36098                this.animating = false;
36099                Roo.callback(callback);
36100             },
36101             scope: this,
36102             duration: this.node.ownerTree.duration || .25
36103         });
36104     },
36105
36106     getContainer : function(){
36107         return this.ctNode;
36108     },
36109
36110     getEl : function(){
36111         return this.wrap;
36112     },
36113
36114     appendDDGhost : function(ghostNode){
36115         ghostNode.appendChild(this.elNode.cloneNode(true));
36116     },
36117
36118     getDDRepairXY : function(){
36119         return Roo.lib.Dom.getXY(this.iconNode);
36120     },
36121
36122     onRender : function(){
36123         this.render();
36124     },
36125
36126     render : function(bulkRender){
36127         var n = this.node, a = n.attributes;
36128         var targetNode = n.parentNode ?
36129               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36130
36131         if(!this.rendered){
36132             this.rendered = true;
36133
36134             this.renderElements(n, a, targetNode, bulkRender);
36135
36136             if(a.qtip){
36137                if(this.textNode.setAttributeNS){
36138                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36139                    if(a.qtipTitle){
36140                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36141                    }
36142                }else{
36143                    this.textNode.setAttribute("ext:qtip", a.qtip);
36144                    if(a.qtipTitle){
36145                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36146                    }
36147                }
36148             }else if(a.qtipCfg){
36149                 a.qtipCfg.target = Roo.id(this.textNode);
36150                 Roo.QuickTips.register(a.qtipCfg);
36151             }
36152             this.initEvents();
36153             if(!this.node.expanded){
36154                 this.updateExpandIcon();
36155             }
36156         }else{
36157             if(bulkRender === true) {
36158                 targetNode.appendChild(this.wrap);
36159             }
36160         }
36161     },
36162
36163     renderElements : function(n, a, targetNode, bulkRender)
36164     {
36165         // add some indent caching, this helps performance when rendering a large tree
36166         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36167         var t = n.getOwnerTree();
36168         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36169         if (typeof(n.attributes.html) != 'undefined') {
36170             txt = n.attributes.html;
36171         }
36172         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36173         var cb = typeof a.checked == 'boolean';
36174         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36175         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36176             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36177             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36178             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36179             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36180             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36181              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36182                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36183             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36184             "</li>"];
36185
36186         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36187             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36188                                 n.nextSibling.ui.getEl(), buf.join(""));
36189         }else{
36190             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36191         }
36192
36193         this.elNode = this.wrap.childNodes[0];
36194         this.ctNode = this.wrap.childNodes[1];
36195         var cs = this.elNode.childNodes;
36196         this.indentNode = cs[0];
36197         this.ecNode = cs[1];
36198         this.iconNode = cs[2];
36199         var index = 3;
36200         if(cb){
36201             this.checkbox = cs[3];
36202             index++;
36203         }
36204         this.anchor = cs[index];
36205         this.textNode = cs[index].firstChild;
36206     },
36207
36208     getAnchor : function(){
36209         return this.anchor;
36210     },
36211
36212     getTextEl : function(){
36213         return this.textNode;
36214     },
36215
36216     getIconEl : function(){
36217         return this.iconNode;
36218     },
36219
36220     isChecked : function(){
36221         return this.checkbox ? this.checkbox.checked : false;
36222     },
36223
36224     updateExpandIcon : function(){
36225         if(this.rendered){
36226             var n = this.node, c1, c2;
36227             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36228             var hasChild = n.hasChildNodes();
36229             if(hasChild){
36230                 if(n.expanded){
36231                     cls += "-minus";
36232                     c1 = "x-tree-node-collapsed";
36233                     c2 = "x-tree-node-expanded";
36234                 }else{
36235                     cls += "-plus";
36236                     c1 = "x-tree-node-expanded";
36237                     c2 = "x-tree-node-collapsed";
36238                 }
36239                 if(this.wasLeaf){
36240                     this.removeClass("x-tree-node-leaf");
36241                     this.wasLeaf = false;
36242                 }
36243                 if(this.c1 != c1 || this.c2 != c2){
36244                     Roo.fly(this.elNode).replaceClass(c1, c2);
36245                     this.c1 = c1; this.c2 = c2;
36246                 }
36247             }else{
36248                 // this changes non-leafs into leafs if they have no children.
36249                 // it's not very rational behaviour..
36250                 
36251                 if(!this.wasLeaf && this.node.leaf){
36252                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36253                     delete this.c1;
36254                     delete this.c2;
36255                     this.wasLeaf = true;
36256                 }
36257             }
36258             var ecc = "x-tree-ec-icon "+cls;
36259             if(this.ecc != ecc){
36260                 this.ecNode.className = ecc;
36261                 this.ecc = ecc;
36262             }
36263         }
36264     },
36265
36266     getChildIndent : function(){
36267         if(!this.childIndent){
36268             var buf = [];
36269             var p = this.node;
36270             while(p){
36271                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36272                     if(!p.isLast()) {
36273                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36274                     } else {
36275                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36276                     }
36277                 }
36278                 p = p.parentNode;
36279             }
36280             this.childIndent = buf.join("");
36281         }
36282         return this.childIndent;
36283     },
36284
36285     renderIndent : function(){
36286         if(this.rendered){
36287             var indent = "";
36288             var p = this.node.parentNode;
36289             if(p){
36290                 indent = p.ui.getChildIndent();
36291             }
36292             if(this.indentMarkup != indent){ // don't rerender if not required
36293                 this.indentNode.innerHTML = indent;
36294                 this.indentMarkup = indent;
36295             }
36296             this.updateExpandIcon();
36297         }
36298     }
36299 };
36300
36301 Roo.tree.RootTreeNodeUI = function(){
36302     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36303 };
36304 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36305     render : function(){
36306         if(!this.rendered){
36307             var targetNode = this.node.ownerTree.innerCt.dom;
36308             this.node.expanded = true;
36309             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36310             this.wrap = this.ctNode = targetNode.firstChild;
36311         }
36312     },
36313     collapse : function(){
36314     },
36315     expand : function(){
36316     }
36317 });/*
36318  * Based on:
36319  * Ext JS Library 1.1.1
36320  * Copyright(c) 2006-2007, Ext JS, LLC.
36321  *
36322  * Originally Released Under LGPL - original licence link has changed is not relivant.
36323  *
36324  * Fork - LGPL
36325  * <script type="text/javascript">
36326  */
36327 /**
36328  * @class Roo.tree.TreeLoader
36329  * @extends Roo.util.Observable
36330  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36331  * nodes from a specified URL. The response must be a javascript Array definition
36332  * who's elements are node definition objects. eg:
36333  * <pre><code>
36334 {  success : true,
36335    data :      [
36336    
36337     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36338     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36339     ]
36340 }
36341
36342
36343 </code></pre>
36344  * <br><br>
36345  * The old style respose with just an array is still supported, but not recommended.
36346  * <br><br>
36347  *
36348  * A server request is sent, and child nodes are loaded only when a node is expanded.
36349  * The loading node's id is passed to the server under the parameter name "node" to
36350  * enable the server to produce the correct child nodes.
36351  * <br><br>
36352  * To pass extra parameters, an event handler may be attached to the "beforeload"
36353  * event, and the parameters specified in the TreeLoader's baseParams property:
36354  * <pre><code>
36355     myTreeLoader.on("beforeload", function(treeLoader, node) {
36356         this.baseParams.category = node.attributes.category;
36357     }, this);
36358     
36359 </code></pre>
36360  *
36361  * This would pass an HTTP parameter called "category" to the server containing
36362  * the value of the Node's "category" attribute.
36363  * @constructor
36364  * Creates a new Treeloader.
36365  * @param {Object} config A config object containing config properties.
36366  */
36367 Roo.tree.TreeLoader = function(config){
36368     this.baseParams = {};
36369     this.requestMethod = "POST";
36370     Roo.apply(this, config);
36371
36372     this.addEvents({
36373     
36374         /**
36375          * @event beforeload
36376          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36377          * @param {Object} This TreeLoader object.
36378          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36379          * @param {Object} callback The callback function specified in the {@link #load} call.
36380          */
36381         beforeload : true,
36382         /**
36383          * @event load
36384          * Fires when the node has been successfuly loaded.
36385          * @param {Object} This TreeLoader object.
36386          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36387          * @param {Object} response The response object containing the data from the server.
36388          */
36389         load : true,
36390         /**
36391          * @event loadexception
36392          * Fires if the network request failed.
36393          * @param {Object} This TreeLoader object.
36394          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36395          * @param {Object} response The response object containing the data from the server.
36396          */
36397         loadexception : true,
36398         /**
36399          * @event create
36400          * Fires before a node is created, enabling you to return custom Node types 
36401          * @param {Object} This TreeLoader object.
36402          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36403          */
36404         create : true
36405     });
36406
36407     Roo.tree.TreeLoader.superclass.constructor.call(this);
36408 };
36409
36410 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36411     /**
36412     * @cfg {String} dataUrl The URL from which to request a Json string which
36413     * specifies an array of node definition object representing the child nodes
36414     * to be loaded.
36415     */
36416     /**
36417     * @cfg {String} requestMethod either GET or POST
36418     * defaults to POST (due to BC)
36419     * to be loaded.
36420     */
36421     /**
36422     * @cfg {Object} baseParams (optional) An object containing properties which
36423     * specify HTTP parameters to be passed to each request for child nodes.
36424     */
36425     /**
36426     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36427     * created by this loader. If the attributes sent by the server have an attribute in this object,
36428     * they take priority.
36429     */
36430     /**
36431     * @cfg {Object} uiProviders (optional) An object containing properties which
36432     * 
36433     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36434     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36435     * <i>uiProvider</i> attribute of a returned child node is a string rather
36436     * than a reference to a TreeNodeUI implementation, this that string value
36437     * is used as a property name in the uiProviders object. You can define the provider named
36438     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36439     */
36440     uiProviders : {},
36441
36442     /**
36443     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36444     * child nodes before loading.
36445     */
36446     clearOnLoad : true,
36447
36448     /**
36449     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36450     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36451     * Grid query { data : [ .....] }
36452     */
36453     
36454     root : false,
36455      /**
36456     * @cfg {String} queryParam (optional) 
36457     * Name of the query as it will be passed on the querystring (defaults to 'node')
36458     * eg. the request will be ?node=[id]
36459     */
36460     
36461     
36462     queryParam: false,
36463     
36464     /**
36465      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36466      * This is called automatically when a node is expanded, but may be used to reload
36467      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36468      * @param {Roo.tree.TreeNode} node
36469      * @param {Function} callback
36470      */
36471     load : function(node, callback){
36472         if(this.clearOnLoad){
36473             while(node.firstChild){
36474                 node.removeChild(node.firstChild);
36475             }
36476         }
36477         if(node.attributes.children){ // preloaded json children
36478             var cs = node.attributes.children;
36479             for(var i = 0, len = cs.length; i < len; i++){
36480                 node.appendChild(this.createNode(cs[i]));
36481             }
36482             if(typeof callback == "function"){
36483                 callback();
36484             }
36485         }else if(this.dataUrl){
36486             this.requestData(node, callback);
36487         }
36488     },
36489
36490     getParams: function(node){
36491         var buf = [], bp = this.baseParams;
36492         for(var key in bp){
36493             if(typeof bp[key] != "function"){
36494                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36495             }
36496         }
36497         var n = this.queryParam === false ? 'node' : this.queryParam;
36498         buf.push(n + "=", encodeURIComponent(node.id));
36499         return buf.join("");
36500     },
36501
36502     requestData : function(node, callback){
36503         if(this.fireEvent("beforeload", this, node, callback) !== false){
36504             this.transId = Roo.Ajax.request({
36505                 method:this.requestMethod,
36506                 url: this.dataUrl||this.url,
36507                 success: this.handleResponse,
36508                 failure: this.handleFailure,
36509                 scope: this,
36510                 argument: {callback: callback, node: node},
36511                 params: this.getParams(node)
36512             });
36513         }else{
36514             // if the load is cancelled, make sure we notify
36515             // the node that we are done
36516             if(typeof callback == "function"){
36517                 callback();
36518             }
36519         }
36520     },
36521
36522     isLoading : function(){
36523         return this.transId ? true : false;
36524     },
36525
36526     abort : function(){
36527         if(this.isLoading()){
36528             Roo.Ajax.abort(this.transId);
36529         }
36530     },
36531
36532     // private
36533     createNode : function(attr)
36534     {
36535         // apply baseAttrs, nice idea Corey!
36536         if(this.baseAttrs){
36537             Roo.applyIf(attr, this.baseAttrs);
36538         }
36539         if(this.applyLoader !== false){
36540             attr.loader = this;
36541         }
36542         // uiProvider = depreciated..
36543         
36544         if(typeof(attr.uiProvider) == 'string'){
36545            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36546                 /**  eval:var:attr */ eval(attr.uiProvider);
36547         }
36548         if(typeof(this.uiProviders['default']) != 'undefined') {
36549             attr.uiProvider = this.uiProviders['default'];
36550         }
36551         
36552         this.fireEvent('create', this, attr);
36553         
36554         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36555         return(attr.leaf ?
36556                         new Roo.tree.TreeNode(attr) :
36557                         new Roo.tree.AsyncTreeNode(attr));
36558     },
36559
36560     processResponse : function(response, node, callback)
36561     {
36562         var json = response.responseText;
36563         try {
36564             
36565             var o = Roo.decode(json);
36566             
36567             if (this.root === false && typeof(o.success) != undefined) {
36568                 this.root = 'data'; // the default behaviour for list like data..
36569                 }
36570                 
36571             if (this.root !== false &&  !o.success) {
36572                 // it's a failure condition.
36573                 var a = response.argument;
36574                 this.fireEvent("loadexception", this, a.node, response);
36575                 Roo.log("Load failed - should have a handler really");
36576                 return;
36577             }
36578             
36579             
36580             
36581             if (this.root !== false) {
36582                  o = o[this.root];
36583             }
36584             
36585             for(var i = 0, len = o.length; i < len; i++){
36586                 var n = this.createNode(o[i]);
36587                 if(n){
36588                     node.appendChild(n);
36589                 }
36590             }
36591             if(typeof callback == "function"){
36592                 callback(this, node);
36593             }
36594         }catch(e){
36595             this.handleFailure(response);
36596         }
36597     },
36598
36599     handleResponse : function(response){
36600         this.transId = false;
36601         var a = response.argument;
36602         this.processResponse(response, a.node, a.callback);
36603         this.fireEvent("load", this, a.node, response);
36604     },
36605
36606     handleFailure : function(response)
36607     {
36608         // should handle failure better..
36609         this.transId = false;
36610         var a = response.argument;
36611         this.fireEvent("loadexception", this, a.node, response);
36612         if(typeof a.callback == "function"){
36613             a.callback(this, a.node);
36614         }
36615     }
36616 });/*
36617  * Based on:
36618  * Ext JS Library 1.1.1
36619  * Copyright(c) 2006-2007, Ext JS, LLC.
36620  *
36621  * Originally Released Under LGPL - original licence link has changed is not relivant.
36622  *
36623  * Fork - LGPL
36624  * <script type="text/javascript">
36625  */
36626
36627 /**
36628 * @class Roo.tree.TreeFilter
36629 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36630 * @param {TreePanel} tree
36631 * @param {Object} config (optional)
36632  */
36633 Roo.tree.TreeFilter = function(tree, config){
36634     this.tree = tree;
36635     this.filtered = {};
36636     Roo.apply(this, config);
36637 };
36638
36639 Roo.tree.TreeFilter.prototype = {
36640     clearBlank:false,
36641     reverse:false,
36642     autoClear:false,
36643     remove:false,
36644
36645      /**
36646      * Filter the data by a specific attribute.
36647      * @param {String/RegExp} value Either string that the attribute value
36648      * should start with or a RegExp to test against the attribute
36649      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36650      * @param {TreeNode} startNode (optional) The node to start the filter at.
36651      */
36652     filter : function(value, attr, startNode){
36653         attr = attr || "text";
36654         var f;
36655         if(typeof value == "string"){
36656             var vlen = value.length;
36657             // auto clear empty filter
36658             if(vlen == 0 && this.clearBlank){
36659                 this.clear();
36660                 return;
36661             }
36662             value = value.toLowerCase();
36663             f = function(n){
36664                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36665             };
36666         }else if(value.exec){ // regex?
36667             f = function(n){
36668                 return value.test(n.attributes[attr]);
36669             };
36670         }else{
36671             throw 'Illegal filter type, must be string or regex';
36672         }
36673         this.filterBy(f, null, startNode);
36674         },
36675
36676     /**
36677      * Filter by a function. The passed function will be called with each
36678      * node in the tree (or from the startNode). If the function returns true, the node is kept
36679      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36680      * @param {Function} fn The filter function
36681      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36682      */
36683     filterBy : function(fn, scope, startNode){
36684         startNode = startNode || this.tree.root;
36685         if(this.autoClear){
36686             this.clear();
36687         }
36688         var af = this.filtered, rv = this.reverse;
36689         var f = function(n){
36690             if(n == startNode){
36691                 return true;
36692             }
36693             if(af[n.id]){
36694                 return false;
36695             }
36696             var m = fn.call(scope || n, n);
36697             if(!m || rv){
36698                 af[n.id] = n;
36699                 n.ui.hide();
36700                 return false;
36701             }
36702             return true;
36703         };
36704         startNode.cascade(f);
36705         if(this.remove){
36706            for(var id in af){
36707                if(typeof id != "function"){
36708                    var n = af[id];
36709                    if(n && n.parentNode){
36710                        n.parentNode.removeChild(n);
36711                    }
36712                }
36713            }
36714         }
36715     },
36716
36717     /**
36718      * Clears the current filter. Note: with the "remove" option
36719      * set a filter cannot be cleared.
36720      */
36721     clear : function(){
36722         var t = this.tree;
36723         var af = this.filtered;
36724         for(var id in af){
36725             if(typeof id != "function"){
36726                 var n = af[id];
36727                 if(n){
36728                     n.ui.show();
36729                 }
36730             }
36731         }
36732         this.filtered = {};
36733     }
36734 };
36735 /*
36736  * Based on:
36737  * Ext JS Library 1.1.1
36738  * Copyright(c) 2006-2007, Ext JS, LLC.
36739  *
36740  * Originally Released Under LGPL - original licence link has changed is not relivant.
36741  *
36742  * Fork - LGPL
36743  * <script type="text/javascript">
36744  */
36745  
36746
36747 /**
36748  * @class Roo.tree.TreeSorter
36749  * Provides sorting of nodes in a TreePanel
36750  * 
36751  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36752  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36753  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36754  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36755  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36756  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36757  * @constructor
36758  * @param {TreePanel} tree
36759  * @param {Object} config
36760  */
36761 Roo.tree.TreeSorter = function(tree, config){
36762     Roo.apply(this, config);
36763     tree.on("beforechildrenrendered", this.doSort, this);
36764     tree.on("append", this.updateSort, this);
36765     tree.on("insert", this.updateSort, this);
36766     
36767     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36768     var p = this.property || "text";
36769     var sortType = this.sortType;
36770     var fs = this.folderSort;
36771     var cs = this.caseSensitive === true;
36772     var leafAttr = this.leafAttr || 'leaf';
36773
36774     this.sortFn = function(n1, n2){
36775         if(fs){
36776             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36777                 return 1;
36778             }
36779             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36780                 return -1;
36781             }
36782         }
36783         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36784         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36785         if(v1 < v2){
36786                         return dsc ? +1 : -1;
36787                 }else if(v1 > v2){
36788                         return dsc ? -1 : +1;
36789         }else{
36790                 return 0;
36791         }
36792     };
36793 };
36794
36795 Roo.tree.TreeSorter.prototype = {
36796     doSort : function(node){
36797         node.sort(this.sortFn);
36798     },
36799     
36800     compareNodes : function(n1, n2){
36801         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36802     },
36803     
36804     updateSort : function(tree, node){
36805         if(node.childrenRendered){
36806             this.doSort.defer(1, this, [node]);
36807         }
36808     }
36809 };/*
36810  * Based on:
36811  * Ext JS Library 1.1.1
36812  * Copyright(c) 2006-2007, Ext JS, LLC.
36813  *
36814  * Originally Released Under LGPL - original licence link has changed is not relivant.
36815  *
36816  * Fork - LGPL
36817  * <script type="text/javascript">
36818  */
36819
36820 if(Roo.dd.DropZone){
36821     
36822 Roo.tree.TreeDropZone = function(tree, config){
36823     this.allowParentInsert = false;
36824     this.allowContainerDrop = false;
36825     this.appendOnly = false;
36826     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36827     this.tree = tree;
36828     this.lastInsertClass = "x-tree-no-status";
36829     this.dragOverData = {};
36830 };
36831
36832 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36833     ddGroup : "TreeDD",
36834     scroll:  true,
36835     
36836     expandDelay : 1000,
36837     
36838     expandNode : function(node){
36839         if(node.hasChildNodes() && !node.isExpanded()){
36840             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36841         }
36842     },
36843     
36844     queueExpand : function(node){
36845         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36846     },
36847     
36848     cancelExpand : function(){
36849         if(this.expandProcId){
36850             clearTimeout(this.expandProcId);
36851             this.expandProcId = false;
36852         }
36853     },
36854     
36855     isValidDropPoint : function(n, pt, dd, e, data){
36856         if(!n || !data){ return false; }
36857         var targetNode = n.node;
36858         var dropNode = data.node;
36859         // default drop rules
36860         if(!(targetNode && targetNode.isTarget && pt)){
36861             return false;
36862         }
36863         if(pt == "append" && targetNode.allowChildren === false){
36864             return false;
36865         }
36866         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36867             return false;
36868         }
36869         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36870             return false;
36871         }
36872         // reuse the object
36873         var overEvent = this.dragOverData;
36874         overEvent.tree = this.tree;
36875         overEvent.target = targetNode;
36876         overEvent.data = data;
36877         overEvent.point = pt;
36878         overEvent.source = dd;
36879         overEvent.rawEvent = e;
36880         overEvent.dropNode = dropNode;
36881         overEvent.cancel = false;  
36882         var result = this.tree.fireEvent("nodedragover", overEvent);
36883         return overEvent.cancel === false && result !== false;
36884     },
36885     
36886     getDropPoint : function(e, n, dd)
36887     {
36888         var tn = n.node;
36889         if(tn.isRoot){
36890             return tn.allowChildren !== false ? "append" : false; // always append for root
36891         }
36892         var dragEl = n.ddel;
36893         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36894         var y = Roo.lib.Event.getPageY(e);
36895         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36896         
36897         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36898         var noAppend = tn.allowChildren === false;
36899         if(this.appendOnly || tn.parentNode.allowChildren === false){
36900             return noAppend ? false : "append";
36901         }
36902         var noBelow = false;
36903         if(!this.allowParentInsert){
36904             noBelow = tn.hasChildNodes() && tn.isExpanded();
36905         }
36906         var q = (b - t) / (noAppend ? 2 : 3);
36907         if(y >= t && y < (t + q)){
36908             return "above";
36909         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36910             return "below";
36911         }else{
36912             return "append";
36913         }
36914     },
36915     
36916     onNodeEnter : function(n, dd, e, data)
36917     {
36918         this.cancelExpand();
36919     },
36920     
36921     onNodeOver : function(n, dd, e, data)
36922     {
36923        
36924         var pt = this.getDropPoint(e, n, dd);
36925         var node = n.node;
36926         
36927         // auto node expand check
36928         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36929             this.queueExpand(node);
36930         }else if(pt != "append"){
36931             this.cancelExpand();
36932         }
36933         
36934         // set the insert point style on the target node
36935         var returnCls = this.dropNotAllowed;
36936         if(this.isValidDropPoint(n, pt, dd, e, data)){
36937            if(pt){
36938                var el = n.ddel;
36939                var cls;
36940                if(pt == "above"){
36941                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36942                    cls = "x-tree-drag-insert-above";
36943                }else if(pt == "below"){
36944                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36945                    cls = "x-tree-drag-insert-below";
36946                }else{
36947                    returnCls = "x-tree-drop-ok-append";
36948                    cls = "x-tree-drag-append";
36949                }
36950                if(this.lastInsertClass != cls){
36951                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36952                    this.lastInsertClass = cls;
36953                }
36954            }
36955        }
36956        return returnCls;
36957     },
36958     
36959     onNodeOut : function(n, dd, e, data){
36960         
36961         this.cancelExpand();
36962         this.removeDropIndicators(n);
36963     },
36964     
36965     onNodeDrop : function(n, dd, e, data){
36966         var point = this.getDropPoint(e, n, dd);
36967         var targetNode = n.node;
36968         targetNode.ui.startDrop();
36969         if(!this.isValidDropPoint(n, point, dd, e, data)){
36970             targetNode.ui.endDrop();
36971             return false;
36972         }
36973         // first try to find the drop node
36974         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36975         var dropEvent = {
36976             tree : this.tree,
36977             target: targetNode,
36978             data: data,
36979             point: point,
36980             source: dd,
36981             rawEvent: e,
36982             dropNode: dropNode,
36983             cancel: !dropNode   
36984         };
36985         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36986         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36987             targetNode.ui.endDrop();
36988             return false;
36989         }
36990         // allow target changing
36991         targetNode = dropEvent.target;
36992         if(point == "append" && !targetNode.isExpanded()){
36993             targetNode.expand(false, null, function(){
36994                 this.completeDrop(dropEvent);
36995             }.createDelegate(this));
36996         }else{
36997             this.completeDrop(dropEvent);
36998         }
36999         return true;
37000     },
37001     
37002     completeDrop : function(de){
37003         var ns = de.dropNode, p = de.point, t = de.target;
37004         if(!(ns instanceof Array)){
37005             ns = [ns];
37006         }
37007         var n;
37008         for(var i = 0, len = ns.length; i < len; i++){
37009             n = ns[i];
37010             if(p == "above"){
37011                 t.parentNode.insertBefore(n, t);
37012             }else if(p == "below"){
37013                 t.parentNode.insertBefore(n, t.nextSibling);
37014             }else{
37015                 t.appendChild(n);
37016             }
37017         }
37018         n.ui.focus();
37019         if(this.tree.hlDrop){
37020             n.ui.highlight();
37021         }
37022         t.ui.endDrop();
37023         this.tree.fireEvent("nodedrop", de);
37024     },
37025     
37026     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37027         if(this.tree.hlDrop){
37028             dropNode.ui.focus();
37029             dropNode.ui.highlight();
37030         }
37031         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37032     },
37033     
37034     getTree : function(){
37035         return this.tree;
37036     },
37037     
37038     removeDropIndicators : function(n){
37039         if(n && n.ddel){
37040             var el = n.ddel;
37041             Roo.fly(el).removeClass([
37042                     "x-tree-drag-insert-above",
37043                     "x-tree-drag-insert-below",
37044                     "x-tree-drag-append"]);
37045             this.lastInsertClass = "_noclass";
37046         }
37047     },
37048     
37049     beforeDragDrop : function(target, e, id){
37050         this.cancelExpand();
37051         return true;
37052     },
37053     
37054     afterRepair : function(data){
37055         if(data && Roo.enableFx){
37056             data.node.ui.highlight();
37057         }
37058         this.hideProxy();
37059     } 
37060     
37061 });
37062
37063 }
37064 /*
37065  * Based on:
37066  * Ext JS Library 1.1.1
37067  * Copyright(c) 2006-2007, Ext JS, LLC.
37068  *
37069  * Originally Released Under LGPL - original licence link has changed is not relivant.
37070  *
37071  * Fork - LGPL
37072  * <script type="text/javascript">
37073  */
37074  
37075
37076 if(Roo.dd.DragZone){
37077 Roo.tree.TreeDragZone = function(tree, config){
37078     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37079     this.tree = tree;
37080 };
37081
37082 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37083     ddGroup : "TreeDD",
37084    
37085     onBeforeDrag : function(data, e){
37086         var n = data.node;
37087         return n && n.draggable && !n.disabled;
37088     },
37089      
37090     
37091     onInitDrag : function(e){
37092         var data = this.dragData;
37093         this.tree.getSelectionModel().select(data.node);
37094         this.proxy.update("");
37095         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37096         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37097     },
37098     
37099     getRepairXY : function(e, data){
37100         return data.node.ui.getDDRepairXY();
37101     },
37102     
37103     onEndDrag : function(data, e){
37104         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37105         
37106         
37107     },
37108     
37109     onValidDrop : function(dd, e, id){
37110         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37111         this.hideProxy();
37112     },
37113     
37114     beforeInvalidDrop : function(e, id){
37115         // this scrolls the original position back into view
37116         var sm = this.tree.getSelectionModel();
37117         sm.clearSelections();
37118         sm.select(this.dragData.node);
37119     }
37120 });
37121 }/*
37122  * Based on:
37123  * Ext JS Library 1.1.1
37124  * Copyright(c) 2006-2007, Ext JS, LLC.
37125  *
37126  * Originally Released Under LGPL - original licence link has changed is not relivant.
37127  *
37128  * Fork - LGPL
37129  * <script type="text/javascript">
37130  */
37131 /**
37132  * @class Roo.tree.TreeEditor
37133  * @extends Roo.Editor
37134  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37135  * as the editor field.
37136  * @constructor
37137  * @param {Object} config (used to be the tree panel.)
37138  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37139  * 
37140  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37141  * @cfg {Roo.form.TextField|Object} field The field configuration
37142  *
37143  * 
37144  */
37145 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37146     var tree = config;
37147     var field;
37148     if (oldconfig) { // old style..
37149         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37150     } else {
37151         // new style..
37152         tree = config.tree;
37153         config.field = config.field  || {};
37154         config.field.xtype = 'TextField';
37155         field = Roo.factory(config.field, Roo.form);
37156     }
37157     config = config || {};
37158     
37159     
37160     this.addEvents({
37161         /**
37162          * @event beforenodeedit
37163          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37164          * false from the handler of this event.
37165          * @param {Editor} this
37166          * @param {Roo.tree.Node} node 
37167          */
37168         "beforenodeedit" : true
37169     });
37170     
37171     //Roo.log(config);
37172     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37173
37174     this.tree = tree;
37175
37176     tree.on('beforeclick', this.beforeNodeClick, this);
37177     tree.getTreeEl().on('mousedown', this.hide, this);
37178     this.on('complete', this.updateNode, this);
37179     this.on('beforestartedit', this.fitToTree, this);
37180     this.on('startedit', this.bindScroll, this, {delay:10});
37181     this.on('specialkey', this.onSpecialKey, this);
37182 };
37183
37184 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37185     /**
37186      * @cfg {String} alignment
37187      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37188      */
37189     alignment: "l-l",
37190     // inherit
37191     autoSize: false,
37192     /**
37193      * @cfg {Boolean} hideEl
37194      * True to hide the bound element while the editor is displayed (defaults to false)
37195      */
37196     hideEl : false,
37197     /**
37198      * @cfg {String} cls
37199      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37200      */
37201     cls: "x-small-editor x-tree-editor",
37202     /**
37203      * @cfg {Boolean} shim
37204      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37205      */
37206     shim:false,
37207     // inherit
37208     shadow:"frame",
37209     /**
37210      * @cfg {Number} maxWidth
37211      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37212      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37213      * scroll and client offsets into account prior to each edit.
37214      */
37215     maxWidth: 250,
37216
37217     editDelay : 350,
37218
37219     // private
37220     fitToTree : function(ed, el){
37221         var td = this.tree.getTreeEl().dom, nd = el.dom;
37222         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37223             td.scrollLeft = nd.offsetLeft;
37224         }
37225         var w = Math.min(
37226                 this.maxWidth,
37227                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37228         this.setSize(w, '');
37229         
37230         return this.fireEvent('beforenodeedit', this, this.editNode);
37231         
37232     },
37233
37234     // private
37235     triggerEdit : function(node){
37236         this.completeEdit();
37237         this.editNode = node;
37238         this.startEdit(node.ui.textNode, node.text);
37239     },
37240
37241     // private
37242     bindScroll : function(){
37243         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37244     },
37245
37246     // private
37247     beforeNodeClick : function(node, e){
37248         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37249         this.lastClick = new Date();
37250         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37251             e.stopEvent();
37252             this.triggerEdit(node);
37253             return false;
37254         }
37255         return true;
37256     },
37257
37258     // private
37259     updateNode : function(ed, value){
37260         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37261         this.editNode.setText(value);
37262     },
37263
37264     // private
37265     onHide : function(){
37266         Roo.tree.TreeEditor.superclass.onHide.call(this);
37267         if(this.editNode){
37268             this.editNode.ui.focus();
37269         }
37270     },
37271
37272     // private
37273     onSpecialKey : function(field, e){
37274         var k = e.getKey();
37275         if(k == e.ESC){
37276             e.stopEvent();
37277             this.cancelEdit();
37278         }else if(k == e.ENTER && !e.hasModifier()){
37279             e.stopEvent();
37280             this.completeEdit();
37281         }
37282     }
37283 });//<Script type="text/javascript">
37284 /*
37285  * Based on:
37286  * Ext JS Library 1.1.1
37287  * Copyright(c) 2006-2007, Ext JS, LLC.
37288  *
37289  * Originally Released Under LGPL - original licence link has changed is not relivant.
37290  *
37291  * Fork - LGPL
37292  * <script type="text/javascript">
37293  */
37294  
37295 /**
37296  * Not documented??? - probably should be...
37297  */
37298
37299 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37300     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37301     
37302     renderElements : function(n, a, targetNode, bulkRender){
37303         //consel.log("renderElements?");
37304         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37305
37306         var t = n.getOwnerTree();
37307         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37308         
37309         var cols = t.columns;
37310         var bw = t.borderWidth;
37311         var c = cols[0];
37312         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37313          var cb = typeof a.checked == "boolean";
37314         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37315         var colcls = 'x-t-' + tid + '-c0';
37316         var buf = [
37317             '<li class="x-tree-node">',
37318             
37319                 
37320                 '<div class="x-tree-node-el ', a.cls,'">',
37321                     // extran...
37322                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37323                 
37324                 
37325                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37326                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37327                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37328                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37329                            (a.iconCls ? ' '+a.iconCls : ''),
37330                            '" unselectable="on" />',
37331                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37332                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37333                              
37334                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37335                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37336                             '<span unselectable="on" qtip="' + tx + '">',
37337                              tx,
37338                              '</span></a>' ,
37339                     '</div>',
37340                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37341                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37342                  ];
37343         for(var i = 1, len = cols.length; i < len; i++){
37344             c = cols[i];
37345             colcls = 'x-t-' + tid + '-c' +i;
37346             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37347             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37348                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37349                       "</div>");
37350          }
37351          
37352          buf.push(
37353             '</a>',
37354             '<div class="x-clear"></div></div>',
37355             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37356             "</li>");
37357         
37358         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37359             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37360                                 n.nextSibling.ui.getEl(), buf.join(""));
37361         }else{
37362             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37363         }
37364         var el = this.wrap.firstChild;
37365         this.elRow = el;
37366         this.elNode = el.firstChild;
37367         this.ranchor = el.childNodes[1];
37368         this.ctNode = this.wrap.childNodes[1];
37369         var cs = el.firstChild.childNodes;
37370         this.indentNode = cs[0];
37371         this.ecNode = cs[1];
37372         this.iconNode = cs[2];
37373         var index = 3;
37374         if(cb){
37375             this.checkbox = cs[3];
37376             index++;
37377         }
37378         this.anchor = cs[index];
37379         
37380         this.textNode = cs[index].firstChild;
37381         
37382         //el.on("click", this.onClick, this);
37383         //el.on("dblclick", this.onDblClick, this);
37384         
37385         
37386        // console.log(this);
37387     },
37388     initEvents : function(){
37389         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37390         
37391             
37392         var a = this.ranchor;
37393
37394         var el = Roo.get(a);
37395
37396         if(Roo.isOpera){ // opera render bug ignores the CSS
37397             el.setStyle("text-decoration", "none");
37398         }
37399
37400         el.on("click", this.onClick, this);
37401         el.on("dblclick", this.onDblClick, this);
37402         el.on("contextmenu", this.onContextMenu, this);
37403         
37404     },
37405     
37406     /*onSelectedChange : function(state){
37407         if(state){
37408             this.focus();
37409             this.addClass("x-tree-selected");
37410         }else{
37411             //this.blur();
37412             this.removeClass("x-tree-selected");
37413         }
37414     },*/
37415     addClass : function(cls){
37416         if(this.elRow){
37417             Roo.fly(this.elRow).addClass(cls);
37418         }
37419         
37420     },
37421     
37422     
37423     removeClass : function(cls){
37424         if(this.elRow){
37425             Roo.fly(this.elRow).removeClass(cls);
37426         }
37427     }
37428
37429     
37430     
37431 });//<Script type="text/javascript">
37432
37433 /*
37434  * Based on:
37435  * Ext JS Library 1.1.1
37436  * Copyright(c) 2006-2007, Ext JS, LLC.
37437  *
37438  * Originally Released Under LGPL - original licence link has changed is not relivant.
37439  *
37440  * Fork - LGPL
37441  * <script type="text/javascript">
37442  */
37443  
37444
37445 /**
37446  * @class Roo.tree.ColumnTree
37447  * @extends Roo.data.TreePanel
37448  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37449  * @cfg {int} borderWidth  compined right/left border allowance
37450  * @constructor
37451  * @param {String/HTMLElement/Element} el The container element
37452  * @param {Object} config
37453  */
37454 Roo.tree.ColumnTree =  function(el, config)
37455 {
37456    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37457    this.addEvents({
37458         /**
37459         * @event resize
37460         * Fire this event on a container when it resizes
37461         * @param {int} w Width
37462         * @param {int} h Height
37463         */
37464        "resize" : true
37465     });
37466     this.on('resize', this.onResize, this);
37467 };
37468
37469 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37470     //lines:false,
37471     
37472     
37473     borderWidth: Roo.isBorderBox ? 0 : 2, 
37474     headEls : false,
37475     
37476     render : function(){
37477         // add the header.....
37478        
37479         Roo.tree.ColumnTree.superclass.render.apply(this);
37480         
37481         this.el.addClass('x-column-tree');
37482         
37483         this.headers = this.el.createChild(
37484             {cls:'x-tree-headers'},this.innerCt.dom);
37485    
37486         var cols = this.columns, c;
37487         var totalWidth = 0;
37488         this.headEls = [];
37489         var  len = cols.length;
37490         for(var i = 0; i < len; i++){
37491              c = cols[i];
37492              totalWidth += c.width;
37493             this.headEls.push(this.headers.createChild({
37494                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37495                  cn: {
37496                      cls:'x-tree-hd-text',
37497                      html: c.header
37498                  },
37499                  style:'width:'+(c.width-this.borderWidth)+'px;'
37500              }));
37501         }
37502         this.headers.createChild({cls:'x-clear'});
37503         // prevent floats from wrapping when clipped
37504         this.headers.setWidth(totalWidth);
37505         //this.innerCt.setWidth(totalWidth);
37506         this.innerCt.setStyle({ overflow: 'auto' });
37507         this.onResize(this.width, this.height);
37508              
37509         
37510     },
37511     onResize : function(w,h)
37512     {
37513         this.height = h;
37514         this.width = w;
37515         // resize cols..
37516         this.innerCt.setWidth(this.width);
37517         this.innerCt.setHeight(this.height-20);
37518         
37519         // headers...
37520         var cols = this.columns, c;
37521         var totalWidth = 0;
37522         var expEl = false;
37523         var len = cols.length;
37524         for(var i = 0; i < len; i++){
37525             c = cols[i];
37526             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37527                 // it's the expander..
37528                 expEl  = this.headEls[i];
37529                 continue;
37530             }
37531             totalWidth += c.width;
37532             
37533         }
37534         if (expEl) {
37535             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37536         }
37537         this.headers.setWidth(w-20);
37538
37539         
37540         
37541         
37542     }
37543 });
37544 /*
37545  * Based on:
37546  * Ext JS Library 1.1.1
37547  * Copyright(c) 2006-2007, Ext JS, LLC.
37548  *
37549  * Originally Released Under LGPL - original licence link has changed is not relivant.
37550  *
37551  * Fork - LGPL
37552  * <script type="text/javascript">
37553  */
37554  
37555 /**
37556  * @class Roo.menu.Menu
37557  * @extends Roo.util.Observable
37558  * @children Roo.menu.BaseItem
37559  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37560  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37561  * @constructor
37562  * Creates a new Menu
37563  * @param {Object} config Configuration options
37564  */
37565 Roo.menu.Menu = function(config){
37566     
37567     Roo.menu.Menu.superclass.constructor.call(this, config);
37568     
37569     this.id = this.id || Roo.id();
37570     this.addEvents({
37571         /**
37572          * @event beforeshow
37573          * Fires before this menu is displayed
37574          * @param {Roo.menu.Menu} this
37575          */
37576         beforeshow : true,
37577         /**
37578          * @event beforehide
37579          * Fires before this menu is hidden
37580          * @param {Roo.menu.Menu} this
37581          */
37582         beforehide : true,
37583         /**
37584          * @event show
37585          * Fires after this menu is displayed
37586          * @param {Roo.menu.Menu} this
37587          */
37588         show : true,
37589         /**
37590          * @event hide
37591          * Fires after this menu is hidden
37592          * @param {Roo.menu.Menu} this
37593          */
37594         hide : true,
37595         /**
37596          * @event click
37597          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37598          * @param {Roo.menu.Menu} this
37599          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37600          * @param {Roo.EventObject} e
37601          */
37602         click : true,
37603         /**
37604          * @event mouseover
37605          * Fires when the mouse is hovering over this menu
37606          * @param {Roo.menu.Menu} this
37607          * @param {Roo.EventObject} e
37608          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37609          */
37610         mouseover : true,
37611         /**
37612          * @event mouseout
37613          * Fires when the mouse exits this menu
37614          * @param {Roo.menu.Menu} this
37615          * @param {Roo.EventObject} e
37616          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37617          */
37618         mouseout : true,
37619         /**
37620          * @event itemclick
37621          * Fires when a menu item contained in this menu is clicked
37622          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37623          * @param {Roo.EventObject} e
37624          */
37625         itemclick: true
37626     });
37627     if (this.registerMenu) {
37628         Roo.menu.MenuMgr.register(this);
37629     }
37630     
37631     var mis = this.items;
37632     this.items = new Roo.util.MixedCollection();
37633     if(mis){
37634         this.add.apply(this, mis);
37635     }
37636 };
37637
37638 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37639     /**
37640      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37641      */
37642     minWidth : 120,
37643     /**
37644      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37645      * for bottom-right shadow (defaults to "sides")
37646      */
37647     shadow : "sides",
37648     /**
37649      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37650      * this menu (defaults to "tl-tr?")
37651      */
37652     subMenuAlign : "tl-tr?",
37653     /**
37654      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37655      * relative to its element of origin (defaults to "tl-bl?")
37656      */
37657     defaultAlign : "tl-bl?",
37658     /**
37659      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37660      */
37661     allowOtherMenus : false,
37662     /**
37663      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37664      */
37665     registerMenu : true,
37666
37667     hidden:true,
37668
37669     // private
37670     render : function(){
37671         if(this.el){
37672             return;
37673         }
37674         var el = this.el = new Roo.Layer({
37675             cls: "x-menu",
37676             shadow:this.shadow,
37677             constrain: false,
37678             parentEl: this.parentEl || document.body,
37679             zindex:15000
37680         });
37681
37682         this.keyNav = new Roo.menu.MenuNav(this);
37683
37684         if(this.plain){
37685             el.addClass("x-menu-plain");
37686         }
37687         if(this.cls){
37688             el.addClass(this.cls);
37689         }
37690         // generic focus element
37691         this.focusEl = el.createChild({
37692             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37693         });
37694         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37695         //disabling touch- as it's causing issues ..
37696         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37697         ul.on('click'   , this.onClick, this);
37698         
37699         
37700         ul.on("mouseover", this.onMouseOver, this);
37701         ul.on("mouseout", this.onMouseOut, this);
37702         this.items.each(function(item){
37703             if (item.hidden) {
37704                 return;
37705             }
37706             
37707             var li = document.createElement("li");
37708             li.className = "x-menu-list-item";
37709             ul.dom.appendChild(li);
37710             item.render(li, this);
37711         }, this);
37712         this.ul = ul;
37713         this.autoWidth();
37714     },
37715
37716     // private
37717     autoWidth : function(){
37718         var el = this.el, ul = this.ul;
37719         if(!el){
37720             return;
37721         }
37722         var w = this.width;
37723         if(w){
37724             el.setWidth(w);
37725         }else if(Roo.isIE){
37726             el.setWidth(this.minWidth);
37727             var t = el.dom.offsetWidth; // force recalc
37728             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37729         }
37730     },
37731
37732     // private
37733     delayAutoWidth : function(){
37734         if(this.rendered){
37735             if(!this.awTask){
37736                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37737             }
37738             this.awTask.delay(20);
37739         }
37740     },
37741
37742     // private
37743     findTargetItem : function(e){
37744         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37745         if(t && t.menuItemId){
37746             return this.items.get(t.menuItemId);
37747         }
37748     },
37749
37750     // private
37751     onClick : function(e){
37752         Roo.log("menu.onClick");
37753         var t = this.findTargetItem(e);
37754         if(!t){
37755             return;
37756         }
37757         Roo.log(e);
37758         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37759             if(t == this.activeItem && t.shouldDeactivate(e)){
37760                 this.activeItem.deactivate();
37761                 delete this.activeItem;
37762                 return;
37763             }
37764             if(t.canActivate){
37765                 this.setActiveItem(t, true);
37766             }
37767             return;
37768             
37769             
37770         }
37771         
37772         t.onClick(e);
37773         this.fireEvent("click", this, t, e);
37774     },
37775
37776     // private
37777     setActiveItem : function(item, autoExpand){
37778         if(item != this.activeItem){
37779             if(this.activeItem){
37780                 this.activeItem.deactivate();
37781             }
37782             this.activeItem = item;
37783             item.activate(autoExpand);
37784         }else if(autoExpand){
37785             item.expandMenu();
37786         }
37787     },
37788
37789     // private
37790     tryActivate : function(start, step){
37791         var items = this.items;
37792         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37793             var item = items.get(i);
37794             if(!item.disabled && item.canActivate){
37795                 this.setActiveItem(item, false);
37796                 return item;
37797             }
37798         }
37799         return false;
37800     },
37801
37802     // private
37803     onMouseOver : function(e){
37804         var t;
37805         if(t = this.findTargetItem(e)){
37806             if(t.canActivate && !t.disabled){
37807                 this.setActiveItem(t, true);
37808             }
37809         }
37810         this.fireEvent("mouseover", this, e, t);
37811     },
37812
37813     // private
37814     onMouseOut : function(e){
37815         var t;
37816         if(t = this.findTargetItem(e)){
37817             if(t == this.activeItem && t.shouldDeactivate(e)){
37818                 this.activeItem.deactivate();
37819                 delete this.activeItem;
37820             }
37821         }
37822         this.fireEvent("mouseout", this, e, t);
37823     },
37824
37825     /**
37826      * Read-only.  Returns true if the menu is currently displayed, else false.
37827      * @type Boolean
37828      */
37829     isVisible : function(){
37830         return this.el && !this.hidden;
37831     },
37832
37833     /**
37834      * Displays this menu relative to another element
37835      * @param {String/HTMLElement/Roo.Element} element The element to align to
37836      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37837      * the element (defaults to this.defaultAlign)
37838      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37839      */
37840     show : function(el, pos, parentMenu){
37841         this.parentMenu = parentMenu;
37842         if(!this.el){
37843             this.render();
37844         }
37845         this.fireEvent("beforeshow", this);
37846         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37847     },
37848
37849     /**
37850      * Displays this menu at a specific xy position
37851      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37852      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37853      */
37854     showAt : function(xy, parentMenu, /* private: */_e){
37855         this.parentMenu = parentMenu;
37856         if(!this.el){
37857             this.render();
37858         }
37859         if(_e !== false){
37860             this.fireEvent("beforeshow", this);
37861             xy = this.el.adjustForConstraints(xy);
37862         }
37863         this.el.setXY(xy);
37864         this.el.show();
37865         this.hidden = false;
37866         this.focus();
37867         this.fireEvent("show", this);
37868     },
37869
37870     focus : function(){
37871         if(!this.hidden){
37872             this.doFocus.defer(50, this);
37873         }
37874     },
37875
37876     doFocus : function(){
37877         if(!this.hidden){
37878             this.focusEl.focus();
37879         }
37880     },
37881
37882     /**
37883      * Hides this menu and optionally all parent menus
37884      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37885      */
37886     hide : function(deep){
37887         if(this.el && this.isVisible()){
37888             this.fireEvent("beforehide", this);
37889             if(this.activeItem){
37890                 this.activeItem.deactivate();
37891                 this.activeItem = null;
37892             }
37893             this.el.hide();
37894             this.hidden = true;
37895             this.fireEvent("hide", this);
37896         }
37897         if(deep === true && this.parentMenu){
37898             this.parentMenu.hide(true);
37899         }
37900     },
37901
37902     /**
37903      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37904      * Any of the following are valid:
37905      * <ul>
37906      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37907      * <li>An HTMLElement object which will be converted to a menu item</li>
37908      * <li>A menu item config object that will be created as a new menu item</li>
37909      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37910      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37911      * </ul>
37912      * Usage:
37913      * <pre><code>
37914 // Create the menu
37915 var menu = new Roo.menu.Menu();
37916
37917 // Create a menu item to add by reference
37918 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37919
37920 // Add a bunch of items at once using different methods.
37921 // Only the last item added will be returned.
37922 var item = menu.add(
37923     menuItem,                // add existing item by ref
37924     'Dynamic Item',          // new TextItem
37925     '-',                     // new separator
37926     { text: 'Config Item' }  // new item by config
37927 );
37928 </code></pre>
37929      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37930      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37931      */
37932     add : function(){
37933         var a = arguments, l = a.length, item;
37934         for(var i = 0; i < l; i++){
37935             var el = a[i];
37936             if ((typeof(el) == "object") && el.xtype && el.xns) {
37937                 el = Roo.factory(el, Roo.menu);
37938             }
37939             
37940             if(el.render){ // some kind of Item
37941                 item = this.addItem(el);
37942             }else if(typeof el == "string"){ // string
37943                 if(el == "separator" || el == "-"){
37944                     item = this.addSeparator();
37945                 }else{
37946                     item = this.addText(el);
37947                 }
37948             }else if(el.tagName || el.el){ // element
37949                 item = this.addElement(el);
37950             }else if(typeof el == "object"){ // must be menu item config?
37951                 item = this.addMenuItem(el);
37952             }
37953         }
37954         return item;
37955     },
37956
37957     /**
37958      * Returns this menu's underlying {@link Roo.Element} object
37959      * @return {Roo.Element} The element
37960      */
37961     getEl : function(){
37962         if(!this.el){
37963             this.render();
37964         }
37965         return this.el;
37966     },
37967
37968     /**
37969      * Adds a separator bar to the menu
37970      * @return {Roo.menu.Item} The menu item that was added
37971      */
37972     addSeparator : function(){
37973         return this.addItem(new Roo.menu.Separator());
37974     },
37975
37976     /**
37977      * Adds an {@link Roo.Element} object to the menu
37978      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37979      * @return {Roo.menu.Item} The menu item that was added
37980      */
37981     addElement : function(el){
37982         return this.addItem(new Roo.menu.BaseItem(el));
37983     },
37984
37985     /**
37986      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37987      * @param {Roo.menu.Item} item The menu item to add
37988      * @return {Roo.menu.Item} The menu item that was added
37989      */
37990     addItem : function(item){
37991         this.items.add(item);
37992         if(this.ul){
37993             var li = document.createElement("li");
37994             li.className = "x-menu-list-item";
37995             this.ul.dom.appendChild(li);
37996             item.render(li, this);
37997             this.delayAutoWidth();
37998         }
37999         return item;
38000     },
38001
38002     /**
38003      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38004      * @param {Object} config A MenuItem config object
38005      * @return {Roo.menu.Item} The menu item that was added
38006      */
38007     addMenuItem : function(config){
38008         if(!(config instanceof Roo.menu.Item)){
38009             if(typeof config.checked == "boolean"){ // must be check menu item config?
38010                 config = new Roo.menu.CheckItem(config);
38011             }else{
38012                 config = new Roo.menu.Item(config);
38013             }
38014         }
38015         return this.addItem(config);
38016     },
38017
38018     /**
38019      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38020      * @param {String} text The text to display in the menu item
38021      * @return {Roo.menu.Item} The menu item that was added
38022      */
38023     addText : function(text){
38024         return this.addItem(new Roo.menu.TextItem({ text : text }));
38025     },
38026
38027     /**
38028      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38029      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38030      * @param {Roo.menu.Item} item The menu item to add
38031      * @return {Roo.menu.Item} The menu item that was added
38032      */
38033     insert : function(index, item){
38034         this.items.insert(index, item);
38035         if(this.ul){
38036             var li = document.createElement("li");
38037             li.className = "x-menu-list-item";
38038             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38039             item.render(li, this);
38040             this.delayAutoWidth();
38041         }
38042         return item;
38043     },
38044
38045     /**
38046      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38047      * @param {Roo.menu.Item} item The menu item to remove
38048      */
38049     remove : function(item){
38050         this.items.removeKey(item.id);
38051         item.destroy();
38052     },
38053
38054     /**
38055      * Removes and destroys all items in the menu
38056      */
38057     removeAll : function(){
38058         var f;
38059         while(f = this.items.first()){
38060             this.remove(f);
38061         }
38062     }
38063 });
38064
38065 // MenuNav is a private utility class used internally by the Menu
38066 Roo.menu.MenuNav = function(menu){
38067     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38068     this.scope = this.menu = menu;
38069 };
38070
38071 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38072     doRelay : function(e, h){
38073         var k = e.getKey();
38074         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38075             this.menu.tryActivate(0, 1);
38076             return false;
38077         }
38078         return h.call(this.scope || this, e, this.menu);
38079     },
38080
38081     up : function(e, m){
38082         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38083             m.tryActivate(m.items.length-1, -1);
38084         }
38085     },
38086
38087     down : function(e, m){
38088         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38089             m.tryActivate(0, 1);
38090         }
38091     },
38092
38093     right : function(e, m){
38094         if(m.activeItem){
38095             m.activeItem.expandMenu(true);
38096         }
38097     },
38098
38099     left : function(e, m){
38100         m.hide();
38101         if(m.parentMenu && m.parentMenu.activeItem){
38102             m.parentMenu.activeItem.activate();
38103         }
38104     },
38105
38106     enter : function(e, m){
38107         if(m.activeItem){
38108             e.stopPropagation();
38109             m.activeItem.onClick(e);
38110             m.fireEvent("click", this, m.activeItem);
38111             return true;
38112         }
38113     }
38114 });/*
38115  * Based on:
38116  * Ext JS Library 1.1.1
38117  * Copyright(c) 2006-2007, Ext JS, LLC.
38118  *
38119  * Originally Released Under LGPL - original licence link has changed is not relivant.
38120  *
38121  * Fork - LGPL
38122  * <script type="text/javascript">
38123  */
38124  
38125 /**
38126  * @class Roo.menu.MenuMgr
38127  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38128  * @singleton
38129  */
38130 Roo.menu.MenuMgr = function(){
38131    var menus, active, groups = {}, attached = false, lastShow = new Date();
38132
38133    // private - called when first menu is created
38134    function init(){
38135        menus = {};
38136        active = new Roo.util.MixedCollection();
38137        Roo.get(document).addKeyListener(27, function(){
38138            if(active.length > 0){
38139                hideAll();
38140            }
38141        });
38142    }
38143
38144    // private
38145    function hideAll(){
38146        if(active && active.length > 0){
38147            var c = active.clone();
38148            c.each(function(m){
38149                m.hide();
38150            });
38151        }
38152    }
38153
38154    // private
38155    function onHide(m){
38156        active.remove(m);
38157        if(active.length < 1){
38158            Roo.get(document).un("mousedown", onMouseDown);
38159            attached = false;
38160        }
38161    }
38162
38163    // private
38164    function onShow(m){
38165        var last = active.last();
38166        lastShow = new Date();
38167        active.add(m);
38168        if(!attached){
38169            Roo.get(document).on("mousedown", onMouseDown);
38170            attached = true;
38171        }
38172        if(m.parentMenu){
38173           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38174           m.parentMenu.activeChild = m;
38175        }else if(last && last.isVisible()){
38176           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38177        }
38178    }
38179
38180    // private
38181    function onBeforeHide(m){
38182        if(m.activeChild){
38183            m.activeChild.hide();
38184        }
38185        if(m.autoHideTimer){
38186            clearTimeout(m.autoHideTimer);
38187            delete m.autoHideTimer;
38188        }
38189    }
38190
38191    // private
38192    function onBeforeShow(m){
38193        var pm = m.parentMenu;
38194        if(!pm && !m.allowOtherMenus){
38195            hideAll();
38196        }else if(pm && pm.activeChild && active != m){
38197            pm.activeChild.hide();
38198        }
38199    }
38200
38201    // private
38202    function onMouseDown(e){
38203        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38204            hideAll();
38205        }
38206    }
38207
38208    // private
38209    function onBeforeCheck(mi, state){
38210        if(state){
38211            var g = groups[mi.group];
38212            for(var i = 0, l = g.length; i < l; i++){
38213                if(g[i] != mi){
38214                    g[i].setChecked(false);
38215                }
38216            }
38217        }
38218    }
38219
38220    return {
38221
38222        /**
38223         * Hides all menus that are currently visible
38224         */
38225        hideAll : function(){
38226             hideAll();  
38227        },
38228
38229        // private
38230        register : function(menu){
38231            if(!menus){
38232                init();
38233            }
38234            menus[menu.id] = menu;
38235            menu.on("beforehide", onBeforeHide);
38236            menu.on("hide", onHide);
38237            menu.on("beforeshow", onBeforeShow);
38238            menu.on("show", onShow);
38239            var g = menu.group;
38240            if(g && menu.events["checkchange"]){
38241                if(!groups[g]){
38242                    groups[g] = [];
38243                }
38244                groups[g].push(menu);
38245                menu.on("checkchange", onCheck);
38246            }
38247        },
38248
38249         /**
38250          * Returns a {@link Roo.menu.Menu} object
38251          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38252          * be used to generate and return a new Menu instance.
38253          */
38254        get : function(menu){
38255            if(typeof menu == "string"){ // menu id
38256                return menus[menu];
38257            }else if(menu.events){  // menu instance
38258                return menu;
38259            }else if(typeof menu.length == 'number'){ // array of menu items?
38260                return new Roo.menu.Menu({items:menu});
38261            }else{ // otherwise, must be a config
38262                return new Roo.menu.Menu(menu);
38263            }
38264        },
38265
38266        // private
38267        unregister : function(menu){
38268            delete menus[menu.id];
38269            menu.un("beforehide", onBeforeHide);
38270            menu.un("hide", onHide);
38271            menu.un("beforeshow", onBeforeShow);
38272            menu.un("show", onShow);
38273            var g = menu.group;
38274            if(g && menu.events["checkchange"]){
38275                groups[g].remove(menu);
38276                menu.un("checkchange", onCheck);
38277            }
38278        },
38279
38280        // private
38281        registerCheckable : function(menuItem){
38282            var g = menuItem.group;
38283            if(g){
38284                if(!groups[g]){
38285                    groups[g] = [];
38286                }
38287                groups[g].push(menuItem);
38288                menuItem.on("beforecheckchange", onBeforeCheck);
38289            }
38290        },
38291
38292        // private
38293        unregisterCheckable : function(menuItem){
38294            var g = menuItem.group;
38295            if(g){
38296                groups[g].remove(menuItem);
38297                menuItem.un("beforecheckchange", onBeforeCheck);
38298            }
38299        }
38300    };
38301 }();/*
38302  * Based on:
38303  * Ext JS Library 1.1.1
38304  * Copyright(c) 2006-2007, Ext JS, LLC.
38305  *
38306  * Originally Released Under LGPL - original licence link has changed is not relivant.
38307  *
38308  * Fork - LGPL
38309  * <script type="text/javascript">
38310  */
38311  
38312
38313 /**
38314  * @class Roo.menu.BaseItem
38315  * @extends Roo.Component
38316  * @abstract
38317  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38318  * management and base configuration options shared by all menu components.
38319  * @constructor
38320  * Creates a new BaseItem
38321  * @param {Object} config Configuration options
38322  */
38323 Roo.menu.BaseItem = function(config){
38324     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38325
38326     this.addEvents({
38327         /**
38328          * @event click
38329          * Fires when this item is clicked
38330          * @param {Roo.menu.BaseItem} this
38331          * @param {Roo.EventObject} e
38332          */
38333         click: true,
38334         /**
38335          * @event activate
38336          * Fires when this item is activated
38337          * @param {Roo.menu.BaseItem} this
38338          */
38339         activate : true,
38340         /**
38341          * @event deactivate
38342          * Fires when this item is deactivated
38343          * @param {Roo.menu.BaseItem} this
38344          */
38345         deactivate : true
38346     });
38347
38348     if(this.handler){
38349         this.on("click", this.handler, this.scope, true);
38350     }
38351 };
38352
38353 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38354     /**
38355      * @cfg {Function} handler
38356      * A function that will handle the click event of this menu item (defaults to undefined)
38357      */
38358     /**
38359      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38360      */
38361     canActivate : false,
38362     
38363      /**
38364      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38365      */
38366     hidden: false,
38367     
38368     /**
38369      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38370      */
38371     activeClass : "x-menu-item-active",
38372     /**
38373      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38374      */
38375     hideOnClick : true,
38376     /**
38377      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38378      */
38379     hideDelay : 100,
38380
38381     // private
38382     ctype: "Roo.menu.BaseItem",
38383
38384     // private
38385     actionMode : "container",
38386
38387     // private
38388     render : function(container, parentMenu){
38389         this.parentMenu = parentMenu;
38390         Roo.menu.BaseItem.superclass.render.call(this, container);
38391         this.container.menuItemId = this.id;
38392     },
38393
38394     // private
38395     onRender : function(container, position){
38396         this.el = Roo.get(this.el);
38397         container.dom.appendChild(this.el.dom);
38398     },
38399
38400     // private
38401     onClick : function(e){
38402         if(!this.disabled && this.fireEvent("click", this, e) !== false
38403                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38404             this.handleClick(e);
38405         }else{
38406             e.stopEvent();
38407         }
38408     },
38409
38410     // private
38411     activate : function(){
38412         if(this.disabled){
38413             return false;
38414         }
38415         var li = this.container;
38416         li.addClass(this.activeClass);
38417         this.region = li.getRegion().adjust(2, 2, -2, -2);
38418         this.fireEvent("activate", this);
38419         return true;
38420     },
38421
38422     // private
38423     deactivate : function(){
38424         this.container.removeClass(this.activeClass);
38425         this.fireEvent("deactivate", this);
38426     },
38427
38428     // private
38429     shouldDeactivate : function(e){
38430         return !this.region || !this.region.contains(e.getPoint());
38431     },
38432
38433     // private
38434     handleClick : function(e){
38435         if(this.hideOnClick){
38436             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38437         }
38438     },
38439
38440     // private
38441     expandMenu : function(autoActivate){
38442         // do nothing
38443     },
38444
38445     // private
38446     hideMenu : function(){
38447         // do nothing
38448     }
38449 });/*
38450  * Based on:
38451  * Ext JS Library 1.1.1
38452  * Copyright(c) 2006-2007, Ext JS, LLC.
38453  *
38454  * Originally Released Under LGPL - original licence link has changed is not relivant.
38455  *
38456  * Fork - LGPL
38457  * <script type="text/javascript">
38458  */
38459  
38460 /**
38461  * @class Roo.menu.Adapter
38462  * @extends Roo.menu.BaseItem
38463  * @abstract
38464  * 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.
38465  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38466  * @constructor
38467  * Creates a new Adapter
38468  * @param {Object} config Configuration options
38469  */
38470 Roo.menu.Adapter = function(component, config){
38471     Roo.menu.Adapter.superclass.constructor.call(this, config);
38472     this.component = component;
38473 };
38474 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38475     // private
38476     canActivate : true,
38477
38478     // private
38479     onRender : function(container, position){
38480         this.component.render(container);
38481         this.el = this.component.getEl();
38482     },
38483
38484     // private
38485     activate : function(){
38486         if(this.disabled){
38487             return false;
38488         }
38489         this.component.focus();
38490         this.fireEvent("activate", this);
38491         return true;
38492     },
38493
38494     // private
38495     deactivate : function(){
38496         this.fireEvent("deactivate", this);
38497     },
38498
38499     // private
38500     disable : function(){
38501         this.component.disable();
38502         Roo.menu.Adapter.superclass.disable.call(this);
38503     },
38504
38505     // private
38506     enable : function(){
38507         this.component.enable();
38508         Roo.menu.Adapter.superclass.enable.call(this);
38509     }
38510 });/*
38511  * Based on:
38512  * Ext JS Library 1.1.1
38513  * Copyright(c) 2006-2007, Ext JS, LLC.
38514  *
38515  * Originally Released Under LGPL - original licence link has changed is not relivant.
38516  *
38517  * Fork - LGPL
38518  * <script type="text/javascript">
38519  */
38520
38521 /**
38522  * @class Roo.menu.TextItem
38523  * @extends Roo.menu.BaseItem
38524  * Adds a static text string to a menu, usually used as either a heading or group separator.
38525  * Note: old style constructor with text is still supported.
38526  * 
38527  * @constructor
38528  * Creates a new TextItem
38529  * @param {Object} cfg Configuration
38530  */
38531 Roo.menu.TextItem = function(cfg){
38532     if (typeof(cfg) == 'string') {
38533         this.text = cfg;
38534     } else {
38535         Roo.apply(this,cfg);
38536     }
38537     
38538     Roo.menu.TextItem.superclass.constructor.call(this);
38539 };
38540
38541 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38542     /**
38543      * @cfg {String} text Text to show on item.
38544      */
38545     text : '',
38546     
38547     /**
38548      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38549      */
38550     hideOnClick : false,
38551     /**
38552      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38553      */
38554     itemCls : "x-menu-text",
38555
38556     // private
38557     onRender : function(){
38558         var s = document.createElement("span");
38559         s.className = this.itemCls;
38560         s.innerHTML = this.text;
38561         this.el = s;
38562         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38563     }
38564 });/*
38565  * Based on:
38566  * Ext JS Library 1.1.1
38567  * Copyright(c) 2006-2007, Ext JS, LLC.
38568  *
38569  * Originally Released Under LGPL - original licence link has changed is not relivant.
38570  *
38571  * Fork - LGPL
38572  * <script type="text/javascript">
38573  */
38574
38575 /**
38576  * @class Roo.menu.Separator
38577  * @extends Roo.menu.BaseItem
38578  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38579  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38580  * @constructor
38581  * @param {Object} config Configuration options
38582  */
38583 Roo.menu.Separator = function(config){
38584     Roo.menu.Separator.superclass.constructor.call(this, config);
38585 };
38586
38587 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38588     /**
38589      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38590      */
38591     itemCls : "x-menu-sep",
38592     /**
38593      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38594      */
38595     hideOnClick : false,
38596
38597     // private
38598     onRender : function(li){
38599         var s = document.createElement("span");
38600         s.className = this.itemCls;
38601         s.innerHTML = "&#160;";
38602         this.el = s;
38603         li.addClass("x-menu-sep-li");
38604         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38605     }
38606 });/*
38607  * Based on:
38608  * Ext JS Library 1.1.1
38609  * Copyright(c) 2006-2007, Ext JS, LLC.
38610  *
38611  * Originally Released Under LGPL - original licence link has changed is not relivant.
38612  *
38613  * Fork - LGPL
38614  * <script type="text/javascript">
38615  */
38616 /**
38617  * @class Roo.menu.Item
38618  * @extends Roo.menu.BaseItem
38619  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38620  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38621  * activation and click handling.
38622  * @constructor
38623  * Creates a new Item
38624  * @param {Object} config Configuration options
38625  */
38626 Roo.menu.Item = function(config){
38627     Roo.menu.Item.superclass.constructor.call(this, config);
38628     if(this.menu){
38629         this.menu = Roo.menu.MenuMgr.get(this.menu);
38630     }
38631 };
38632 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38633     /**
38634      * @cfg {Roo.menu.Menu} menu
38635      * A Sub menu
38636      */
38637     /**
38638      * @cfg {String} text
38639      * The text to show on the menu item.
38640      */
38641     text: '',
38642      /**
38643      * @cfg {String} HTML to render in menu
38644      * The text to show on the menu item (HTML version).
38645      */
38646     html: '',
38647     /**
38648      * @cfg {String} icon
38649      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38650      */
38651     icon: undefined,
38652     /**
38653      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38654      */
38655     itemCls : "x-menu-item",
38656     /**
38657      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38658      */
38659     canActivate : true,
38660     /**
38661      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38662      */
38663     showDelay: 200,
38664     // doc'd in BaseItem
38665     hideDelay: 200,
38666
38667     // private
38668     ctype: "Roo.menu.Item",
38669     
38670     // private
38671     onRender : function(container, position){
38672         var el = document.createElement("a");
38673         el.hideFocus = true;
38674         el.unselectable = "on";
38675         el.href = this.href || "#";
38676         if(this.hrefTarget){
38677             el.target = this.hrefTarget;
38678         }
38679         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38680         
38681         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38682         
38683         el.innerHTML = String.format(
38684                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38685                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38686         this.el = el;
38687         Roo.menu.Item.superclass.onRender.call(this, container, position);
38688     },
38689
38690     /**
38691      * Sets the text to display in this menu item
38692      * @param {String} text The text to display
38693      * @param {Boolean} isHTML true to indicate text is pure html.
38694      */
38695     setText : function(text, isHTML){
38696         if (isHTML) {
38697             this.html = text;
38698         } else {
38699             this.text = text;
38700             this.html = '';
38701         }
38702         if(this.rendered){
38703             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38704      
38705             this.el.update(String.format(
38706                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38707                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38708             this.parentMenu.autoWidth();
38709         }
38710     },
38711
38712     // private
38713     handleClick : function(e){
38714         if(!this.href){ // if no link defined, stop the event automatically
38715             e.stopEvent();
38716         }
38717         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38718     },
38719
38720     // private
38721     activate : function(autoExpand){
38722         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38723             this.focus();
38724             if(autoExpand){
38725                 this.expandMenu();
38726             }
38727         }
38728         return true;
38729     },
38730
38731     // private
38732     shouldDeactivate : function(e){
38733         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38734             if(this.menu && this.menu.isVisible()){
38735                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38736             }
38737             return true;
38738         }
38739         return false;
38740     },
38741
38742     // private
38743     deactivate : function(){
38744         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38745         this.hideMenu();
38746     },
38747
38748     // private
38749     expandMenu : function(autoActivate){
38750         if(!this.disabled && this.menu){
38751             clearTimeout(this.hideTimer);
38752             delete this.hideTimer;
38753             if(!this.menu.isVisible() && !this.showTimer){
38754                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38755             }else if (this.menu.isVisible() && autoActivate){
38756                 this.menu.tryActivate(0, 1);
38757             }
38758         }
38759     },
38760
38761     // private
38762     deferExpand : function(autoActivate){
38763         delete this.showTimer;
38764         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38765         if(autoActivate){
38766             this.menu.tryActivate(0, 1);
38767         }
38768     },
38769
38770     // private
38771     hideMenu : function(){
38772         clearTimeout(this.showTimer);
38773         delete this.showTimer;
38774         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38775             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38776         }
38777     },
38778
38779     // private
38780     deferHide : function(){
38781         delete this.hideTimer;
38782         this.menu.hide();
38783     }
38784 });/*
38785  * Based on:
38786  * Ext JS Library 1.1.1
38787  * Copyright(c) 2006-2007, Ext JS, LLC.
38788  *
38789  * Originally Released Under LGPL - original licence link has changed is not relivant.
38790  *
38791  * Fork - LGPL
38792  * <script type="text/javascript">
38793  */
38794  
38795 /**
38796  * @class Roo.menu.CheckItem
38797  * @extends Roo.menu.Item
38798  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38799  * @constructor
38800  * Creates a new CheckItem
38801  * @param {Object} config Configuration options
38802  */
38803 Roo.menu.CheckItem = function(config){
38804     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38805     this.addEvents({
38806         /**
38807          * @event beforecheckchange
38808          * Fires before the checked value is set, providing an opportunity to cancel if needed
38809          * @param {Roo.menu.CheckItem} this
38810          * @param {Boolean} checked The new checked value that will be set
38811          */
38812         "beforecheckchange" : true,
38813         /**
38814          * @event checkchange
38815          * Fires after the checked value has been set
38816          * @param {Roo.menu.CheckItem} this
38817          * @param {Boolean} checked The checked value that was set
38818          */
38819         "checkchange" : true
38820     });
38821     if(this.checkHandler){
38822         this.on('checkchange', this.checkHandler, this.scope);
38823     }
38824 };
38825 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38826     /**
38827      * @cfg {String} group
38828      * All check items with the same group name will automatically be grouped into a single-select
38829      * radio button group (defaults to '')
38830      */
38831     /**
38832      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38833      */
38834     itemCls : "x-menu-item x-menu-check-item",
38835     /**
38836      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38837      */
38838     groupClass : "x-menu-group-item",
38839
38840     /**
38841      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38842      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38843      * initialized with checked = true will be rendered as checked.
38844      */
38845     checked: false,
38846
38847     // private
38848     ctype: "Roo.menu.CheckItem",
38849
38850     // private
38851     onRender : function(c){
38852         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38853         if(this.group){
38854             this.el.addClass(this.groupClass);
38855         }
38856         Roo.menu.MenuMgr.registerCheckable(this);
38857         if(this.checked){
38858             this.checked = false;
38859             this.setChecked(true, true);
38860         }
38861     },
38862
38863     // private
38864     destroy : function(){
38865         if(this.rendered){
38866             Roo.menu.MenuMgr.unregisterCheckable(this);
38867         }
38868         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38869     },
38870
38871     /**
38872      * Set the checked state of this item
38873      * @param {Boolean} checked The new checked value
38874      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38875      */
38876     setChecked : function(state, suppressEvent){
38877         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38878             if(this.container){
38879                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38880             }
38881             this.checked = state;
38882             if(suppressEvent !== true){
38883                 this.fireEvent("checkchange", this, state);
38884             }
38885         }
38886     },
38887
38888     // private
38889     handleClick : function(e){
38890        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38891            this.setChecked(!this.checked);
38892        }
38893        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38894     }
38895 });/*
38896  * Based on:
38897  * Ext JS Library 1.1.1
38898  * Copyright(c) 2006-2007, Ext JS, LLC.
38899  *
38900  * Originally Released Under LGPL - original licence link has changed is not relivant.
38901  *
38902  * Fork - LGPL
38903  * <script type="text/javascript">
38904  */
38905  
38906 /**
38907  * @class Roo.menu.DateItem
38908  * @extends Roo.menu.Adapter
38909  * A menu item that wraps the {@link Roo.DatPicker} component.
38910  * @constructor
38911  * Creates a new DateItem
38912  * @param {Object} config Configuration options
38913  */
38914 Roo.menu.DateItem = function(config){
38915     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38916     /** The Roo.DatePicker object @type Roo.DatePicker */
38917     this.picker = this.component;
38918     this.addEvents({select: true});
38919     
38920     this.picker.on("render", function(picker){
38921         picker.getEl().swallowEvent("click");
38922         picker.container.addClass("x-menu-date-item");
38923     });
38924
38925     this.picker.on("select", this.onSelect, this);
38926 };
38927
38928 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38929     // private
38930     onSelect : function(picker, date){
38931         this.fireEvent("select", this, date, picker);
38932         Roo.menu.DateItem.superclass.handleClick.call(this);
38933     }
38934 });/*
38935  * Based on:
38936  * Ext JS Library 1.1.1
38937  * Copyright(c) 2006-2007, Ext JS, LLC.
38938  *
38939  * Originally Released Under LGPL - original licence link has changed is not relivant.
38940  *
38941  * Fork - LGPL
38942  * <script type="text/javascript">
38943  */
38944  
38945 /**
38946  * @class Roo.menu.ColorItem
38947  * @extends Roo.menu.Adapter
38948  * A menu item that wraps the {@link Roo.ColorPalette} component.
38949  * @constructor
38950  * Creates a new ColorItem
38951  * @param {Object} config Configuration options
38952  */
38953 Roo.menu.ColorItem = function(config){
38954     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38955     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38956     this.palette = this.component;
38957     this.relayEvents(this.palette, ["select"]);
38958     if(this.selectHandler){
38959         this.on('select', this.selectHandler, this.scope);
38960     }
38961 };
38962 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38963  * Based on:
38964  * Ext JS Library 1.1.1
38965  * Copyright(c) 2006-2007, Ext JS, LLC.
38966  *
38967  * Originally Released Under LGPL - original licence link has changed is not relivant.
38968  *
38969  * Fork - LGPL
38970  * <script type="text/javascript">
38971  */
38972  
38973
38974 /**
38975  * @class Roo.menu.DateMenu
38976  * @extends Roo.menu.Menu
38977  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38978  * @constructor
38979  * Creates a new DateMenu
38980  * @param {Object} config Configuration options
38981  */
38982 Roo.menu.DateMenu = function(config){
38983     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38984     this.plain = true;
38985     var di = new Roo.menu.DateItem(config);
38986     this.add(di);
38987     /**
38988      * The {@link Roo.DatePicker} instance for this DateMenu
38989      * @type DatePicker
38990      */
38991     this.picker = di.picker;
38992     /**
38993      * @event select
38994      * @param {DatePicker} picker
38995      * @param {Date} date
38996      */
38997     this.relayEvents(di, ["select"]);
38998     this.on('beforeshow', function(){
38999         if(this.picker){
39000             this.picker.hideMonthPicker(false);
39001         }
39002     }, this);
39003 };
39004 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39005     cls:'x-date-menu'
39006 });/*
39007  * Based on:
39008  * Ext JS Library 1.1.1
39009  * Copyright(c) 2006-2007, Ext JS, LLC.
39010  *
39011  * Originally Released Under LGPL - original licence link has changed is not relivant.
39012  *
39013  * Fork - LGPL
39014  * <script type="text/javascript">
39015  */
39016  
39017
39018 /**
39019  * @class Roo.menu.ColorMenu
39020  * @extends Roo.menu.Menu
39021  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39022  * @constructor
39023  * Creates a new ColorMenu
39024  * @param {Object} config Configuration options
39025  */
39026 Roo.menu.ColorMenu = function(config){
39027     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39028     this.plain = true;
39029     var ci = new Roo.menu.ColorItem(config);
39030     this.add(ci);
39031     /**
39032      * The {@link Roo.ColorPalette} instance for this ColorMenu
39033      * @type ColorPalette
39034      */
39035     this.palette = ci.palette;
39036     /**
39037      * @event select
39038      * @param {ColorPalette} palette
39039      * @param {String} color
39040      */
39041     this.relayEvents(ci, ["select"]);
39042 };
39043 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39044  * Based on:
39045  * Ext JS Library 1.1.1
39046  * Copyright(c) 2006-2007, Ext JS, LLC.
39047  *
39048  * Originally Released Under LGPL - original licence link has changed is not relivant.
39049  *
39050  * Fork - LGPL
39051  * <script type="text/javascript">
39052  */
39053  
39054 /**
39055  * @class Roo.form.TextItem
39056  * @extends Roo.BoxComponent
39057  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39058  * @constructor
39059  * Creates a new TextItem
39060  * @param {Object} config Configuration options
39061  */
39062 Roo.form.TextItem = function(config){
39063     Roo.form.TextItem.superclass.constructor.call(this, config);
39064 };
39065
39066 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39067     
39068     /**
39069      * @cfg {String} tag the tag for this item (default div)
39070      */
39071     tag : 'div',
39072     /**
39073      * @cfg {String} html the content for this item
39074      */
39075     html : '',
39076     
39077     getAutoCreate : function()
39078     {
39079         var cfg = {
39080             id: this.id,
39081             tag: this.tag,
39082             html: this.html,
39083             cls: 'x-form-item'
39084         };
39085         
39086         return cfg;
39087         
39088     },
39089     
39090     onRender : function(ct, position)
39091     {
39092         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39093         
39094         if(!this.el){
39095             var cfg = this.getAutoCreate();
39096             if(!cfg.name){
39097                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39098             }
39099             if (!cfg.name.length) {
39100                 delete cfg.name;
39101             }
39102             this.el = ct.createChild(cfg, position);
39103         }
39104     },
39105     /*
39106      * setHTML
39107      * @param {String} html update the Contents of the element.
39108      */
39109     setHTML : function(html)
39110     {
39111         this.fieldEl.dom.innerHTML = html;
39112     }
39113     
39114 });/*
39115  * Based on:
39116  * Ext JS Library 1.1.1
39117  * Copyright(c) 2006-2007, Ext JS, LLC.
39118  *
39119  * Originally Released Under LGPL - original licence link has changed is not relivant.
39120  *
39121  * Fork - LGPL
39122  * <script type="text/javascript">
39123  */
39124  
39125 /**
39126  * @class Roo.form.Field
39127  * @extends Roo.BoxComponent
39128  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39129  * @constructor
39130  * Creates a new Field
39131  * @param {Object} config Configuration options
39132  */
39133 Roo.form.Field = function(config){
39134     Roo.form.Field.superclass.constructor.call(this, config);
39135 };
39136
39137 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39138     /**
39139      * @cfg {String} fieldLabel Label to use when rendering a form.
39140      */
39141        /**
39142      * @cfg {String} qtip Mouse over tip
39143      */
39144      
39145     /**
39146      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39147      */
39148     invalidClass : "x-form-invalid",
39149     /**
39150      * @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")
39151      */
39152     invalidText : "The value in this field is invalid",
39153     /**
39154      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39155      */
39156     focusClass : "x-form-focus",
39157     /**
39158      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39159       automatic validation (defaults to "keyup").
39160      */
39161     validationEvent : "keyup",
39162     /**
39163      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39164      */
39165     validateOnBlur : true,
39166     /**
39167      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39168      */
39169     validationDelay : 250,
39170     /**
39171      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39172      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39173      */
39174     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39175     /**
39176      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39177      */
39178     fieldClass : "x-form-field",
39179     /**
39180      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39181      *<pre>
39182 Value         Description
39183 -----------   ----------------------------------------------------------------------
39184 qtip          Display a quick tip when the user hovers over the field
39185 title         Display a default browser title attribute popup
39186 under         Add a block div beneath the field containing the error text
39187 side          Add an error icon to the right of the field with a popup on hover
39188 [element id]  Add the error text directly to the innerHTML of the specified element
39189 </pre>
39190      */
39191     msgTarget : 'qtip',
39192     /**
39193      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39194      */
39195     msgFx : 'normal',
39196
39197     /**
39198      * @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.
39199      */
39200     readOnly : false,
39201
39202     /**
39203      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39204      */
39205     disabled : false,
39206
39207     /**
39208      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39209      */
39210     inputType : undefined,
39211     
39212     /**
39213      * @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).
39214          */
39215         tabIndex : undefined,
39216         
39217     // private
39218     isFormField : true,
39219
39220     // private
39221     hasFocus : false,
39222     /**
39223      * @property {Roo.Element} fieldEl
39224      * Element Containing the rendered Field (with label etc.)
39225      */
39226     /**
39227      * @cfg {Mixed} value A value to initialize this field with.
39228      */
39229     value : undefined,
39230
39231     /**
39232      * @cfg {String} name The field's HTML name attribute.
39233      */
39234     /**
39235      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39236      */
39237     // private
39238     loadedValue : false,
39239      
39240      
39241         // private ??
39242         initComponent : function(){
39243         Roo.form.Field.superclass.initComponent.call(this);
39244         this.addEvents({
39245             /**
39246              * @event focus
39247              * Fires when this field receives input focus.
39248              * @param {Roo.form.Field} this
39249              */
39250             focus : true,
39251             /**
39252              * @event blur
39253              * Fires when this field loses input focus.
39254              * @param {Roo.form.Field} this
39255              */
39256             blur : true,
39257             /**
39258              * @event specialkey
39259              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39260              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39261              * @param {Roo.form.Field} this
39262              * @param {Roo.EventObject} e The event object
39263              */
39264             specialkey : true,
39265             /**
39266              * @event change
39267              * Fires just before the field blurs if the field value has changed.
39268              * @param {Roo.form.Field} this
39269              * @param {Mixed} newValue The new value
39270              * @param {Mixed} oldValue The original value
39271              */
39272             change : true,
39273             /**
39274              * @event invalid
39275              * Fires after the field has been marked as invalid.
39276              * @param {Roo.form.Field} this
39277              * @param {String} msg The validation message
39278              */
39279             invalid : true,
39280             /**
39281              * @event valid
39282              * Fires after the field has been validated with no errors.
39283              * @param {Roo.form.Field} this
39284              */
39285             valid : true,
39286              /**
39287              * @event keyup
39288              * Fires after the key up
39289              * @param {Roo.form.Field} this
39290              * @param {Roo.EventObject}  e The event Object
39291              */
39292             keyup : true
39293         });
39294     },
39295
39296     /**
39297      * Returns the name attribute of the field if available
39298      * @return {String} name The field name
39299      */
39300     getName: function(){
39301          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39302     },
39303
39304     // private
39305     onRender : function(ct, position){
39306         Roo.form.Field.superclass.onRender.call(this, ct, position);
39307         if(!this.el){
39308             var cfg = this.getAutoCreate();
39309             if(!cfg.name){
39310                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39311             }
39312             if (!cfg.name.length) {
39313                 delete cfg.name;
39314             }
39315             if(this.inputType){
39316                 cfg.type = this.inputType;
39317             }
39318             this.el = ct.createChild(cfg, position);
39319         }
39320         var type = this.el.dom.type;
39321         if(type){
39322             if(type == 'password'){
39323                 type = 'text';
39324             }
39325             this.el.addClass('x-form-'+type);
39326         }
39327         if(this.readOnly){
39328             this.el.dom.readOnly = true;
39329         }
39330         if(this.tabIndex !== undefined){
39331             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39332         }
39333
39334         this.el.addClass([this.fieldClass, this.cls]);
39335         this.initValue();
39336     },
39337
39338     /**
39339      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39340      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39341      * @return {Roo.form.Field} this
39342      */
39343     applyTo : function(target){
39344         this.allowDomMove = false;
39345         this.el = Roo.get(target);
39346         this.render(this.el.dom.parentNode);
39347         return this;
39348     },
39349
39350     // private
39351     initValue : function(){
39352         if(this.value !== undefined){
39353             this.setValue(this.value);
39354         }else if(this.el.dom.value.length > 0){
39355             this.setValue(this.el.dom.value);
39356         }
39357     },
39358
39359     /**
39360      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39361      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39362      */
39363     isDirty : function() {
39364         if(this.disabled) {
39365             return false;
39366         }
39367         return String(this.getValue()) !== String(this.originalValue);
39368     },
39369
39370     /**
39371      * stores the current value in loadedValue
39372      */
39373     resetHasChanged : function()
39374     {
39375         this.loadedValue = String(this.getValue());
39376     },
39377     /**
39378      * checks the current value against the 'loaded' value.
39379      * Note - will return false if 'resetHasChanged' has not been called first.
39380      */
39381     hasChanged : function()
39382     {
39383         if(this.disabled || this.readOnly) {
39384             return false;
39385         }
39386         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39387     },
39388     
39389     
39390     
39391     // private
39392     afterRender : function(){
39393         Roo.form.Field.superclass.afterRender.call(this);
39394         this.initEvents();
39395     },
39396
39397     // private
39398     fireKey : function(e){
39399         //Roo.log('field ' + e.getKey());
39400         if(e.isNavKeyPress()){
39401             this.fireEvent("specialkey", this, e);
39402         }
39403     },
39404
39405     /**
39406      * Resets the current field value to the originally loaded value and clears any validation messages
39407      */
39408     reset : function(){
39409         this.setValue(this.resetValue);
39410         this.originalValue = this.getValue();
39411         this.clearInvalid();
39412     },
39413
39414     // private
39415     initEvents : function(){
39416         // safari killled keypress - so keydown is now used..
39417         this.el.on("keydown" , this.fireKey,  this);
39418         this.el.on("focus", this.onFocus,  this);
39419         this.el.on("blur", this.onBlur,  this);
39420         this.el.relayEvent('keyup', this);
39421
39422         // reference to original value for reset
39423         this.originalValue = this.getValue();
39424         this.resetValue =  this.getValue();
39425     },
39426
39427     // private
39428     onFocus : function(){
39429         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39430             this.el.addClass(this.focusClass);
39431         }
39432         if(!this.hasFocus){
39433             this.hasFocus = true;
39434             this.startValue = this.getValue();
39435             this.fireEvent("focus", this);
39436         }
39437     },
39438
39439     beforeBlur : Roo.emptyFn,
39440
39441     // private
39442     onBlur : function(){
39443         this.beforeBlur();
39444         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39445             this.el.removeClass(this.focusClass);
39446         }
39447         this.hasFocus = false;
39448         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39449             this.validate();
39450         }
39451         var v = this.getValue();
39452         if(String(v) !== String(this.startValue)){
39453             this.fireEvent('change', this, v, this.startValue);
39454         }
39455         this.fireEvent("blur", this);
39456     },
39457
39458     /**
39459      * Returns whether or not the field value is currently valid
39460      * @param {Boolean} preventMark True to disable marking the field invalid
39461      * @return {Boolean} True if the value is valid, else false
39462      */
39463     isValid : function(preventMark){
39464         if(this.disabled){
39465             return true;
39466         }
39467         var restore = this.preventMark;
39468         this.preventMark = preventMark === true;
39469         var v = this.validateValue(this.processValue(this.getRawValue()));
39470         this.preventMark = restore;
39471         return v;
39472     },
39473
39474     /**
39475      * Validates the field value
39476      * @return {Boolean} True if the value is valid, else false
39477      */
39478     validate : function(){
39479         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39480             this.clearInvalid();
39481             return true;
39482         }
39483         return false;
39484     },
39485
39486     processValue : function(value){
39487         return value;
39488     },
39489
39490     // private
39491     // Subclasses should provide the validation implementation by overriding this
39492     validateValue : function(value){
39493         return true;
39494     },
39495
39496     /**
39497      * Mark this field as invalid
39498      * @param {String} msg The validation message
39499      */
39500     markInvalid : function(msg){
39501         if(!this.rendered || this.preventMark){ // not rendered
39502             return;
39503         }
39504         
39505         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39506         
39507         obj.el.addClass(this.invalidClass);
39508         msg = msg || this.invalidText;
39509         switch(this.msgTarget){
39510             case 'qtip':
39511                 obj.el.dom.qtip = msg;
39512                 obj.el.dom.qclass = 'x-form-invalid-tip';
39513                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39514                     Roo.QuickTips.enable();
39515                 }
39516                 break;
39517             case 'title':
39518                 this.el.dom.title = msg;
39519                 break;
39520             case 'under':
39521                 if(!this.errorEl){
39522                     var elp = this.el.findParent('.x-form-element', 5, true);
39523                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39524                     this.errorEl.setWidth(elp.getWidth(true)-20);
39525                 }
39526                 this.errorEl.update(msg);
39527                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39528                 break;
39529             case 'side':
39530                 if(!this.errorIcon){
39531                     var elp = this.el.findParent('.x-form-element', 5, true);
39532                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39533                 }
39534                 this.alignErrorIcon();
39535                 this.errorIcon.dom.qtip = msg;
39536                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39537                 this.errorIcon.show();
39538                 this.on('resize', this.alignErrorIcon, this);
39539                 break;
39540             default:
39541                 var t = Roo.getDom(this.msgTarget);
39542                 t.innerHTML = msg;
39543                 t.style.display = this.msgDisplay;
39544                 break;
39545         }
39546         this.fireEvent('invalid', this, msg);
39547     },
39548
39549     // private
39550     alignErrorIcon : function(){
39551         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39552     },
39553
39554     /**
39555      * Clear any invalid styles/messages for this field
39556      */
39557     clearInvalid : function(){
39558         if(!this.rendered || this.preventMark){ // not rendered
39559             return;
39560         }
39561         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39562         
39563         obj.el.removeClass(this.invalidClass);
39564         switch(this.msgTarget){
39565             case 'qtip':
39566                 obj.el.dom.qtip = '';
39567                 break;
39568             case 'title':
39569                 this.el.dom.title = '';
39570                 break;
39571             case 'under':
39572                 if(this.errorEl){
39573                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39574                 }
39575                 break;
39576             case 'side':
39577                 if(this.errorIcon){
39578                     this.errorIcon.dom.qtip = '';
39579                     this.errorIcon.hide();
39580                     this.un('resize', this.alignErrorIcon, this);
39581                 }
39582                 break;
39583             default:
39584                 var t = Roo.getDom(this.msgTarget);
39585                 t.innerHTML = '';
39586                 t.style.display = 'none';
39587                 break;
39588         }
39589         this.fireEvent('valid', this);
39590     },
39591
39592     /**
39593      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39594      * @return {Mixed} value The field value
39595      */
39596     getRawValue : function(){
39597         var v = this.el.getValue();
39598         
39599         return v;
39600     },
39601
39602     /**
39603      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39604      * @return {Mixed} value The field value
39605      */
39606     getValue : function(){
39607         var v = this.el.getValue();
39608          
39609         return v;
39610     },
39611
39612     /**
39613      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39614      * @param {Mixed} value The value to set
39615      */
39616     setRawValue : function(v){
39617         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39618     },
39619
39620     /**
39621      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39622      * @param {Mixed} value The value to set
39623      */
39624     setValue : function(v){
39625         this.value = v;
39626         if(this.rendered){
39627             this.el.dom.value = (v === null || v === undefined ? '' : v);
39628              this.validate();
39629         }
39630     },
39631
39632     adjustSize : function(w, h){
39633         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39634         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39635         return s;
39636     },
39637
39638     adjustWidth : function(tag, w){
39639         tag = tag.toLowerCase();
39640         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39641             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39642                 if(tag == 'input'){
39643                     return w + 2;
39644                 }
39645                 if(tag == 'textarea'){
39646                     return w-2;
39647                 }
39648             }else if(Roo.isOpera){
39649                 if(tag == 'input'){
39650                     return w + 2;
39651                 }
39652                 if(tag == 'textarea'){
39653                     return w-2;
39654                 }
39655             }
39656         }
39657         return w;
39658     }
39659 });
39660
39661
39662 // anything other than normal should be considered experimental
39663 Roo.form.Field.msgFx = {
39664     normal : {
39665         show: function(msgEl, f){
39666             msgEl.setDisplayed('block');
39667         },
39668
39669         hide : function(msgEl, f){
39670             msgEl.setDisplayed(false).update('');
39671         }
39672     },
39673
39674     slide : {
39675         show: function(msgEl, f){
39676             msgEl.slideIn('t', {stopFx:true});
39677         },
39678
39679         hide : function(msgEl, f){
39680             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39681         }
39682     },
39683
39684     slideRight : {
39685         show: function(msgEl, f){
39686             msgEl.fixDisplay();
39687             msgEl.alignTo(f.el, 'tl-tr');
39688             msgEl.slideIn('l', {stopFx:true});
39689         },
39690
39691         hide : function(msgEl, f){
39692             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39693         }
39694     }
39695 };/*
39696  * Based on:
39697  * Ext JS Library 1.1.1
39698  * Copyright(c) 2006-2007, Ext JS, LLC.
39699  *
39700  * Originally Released Under LGPL - original licence link has changed is not relivant.
39701  *
39702  * Fork - LGPL
39703  * <script type="text/javascript">
39704  */
39705  
39706
39707 /**
39708  * @class Roo.form.TextField
39709  * @extends Roo.form.Field
39710  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39711  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39712  * @constructor
39713  * Creates a new TextField
39714  * @param {Object} config Configuration options
39715  */
39716 Roo.form.TextField = function(config){
39717     Roo.form.TextField.superclass.constructor.call(this, config);
39718     this.addEvents({
39719         /**
39720          * @event autosize
39721          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39722          * according to the default logic, but this event provides a hook for the developer to apply additional
39723          * logic at runtime to resize the field if needed.
39724              * @param {Roo.form.Field} this This text field
39725              * @param {Number} width The new field width
39726              */
39727         autosize : true
39728     });
39729 };
39730
39731 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39732     /**
39733      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39734      */
39735     grow : false,
39736     /**
39737      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39738      */
39739     growMin : 30,
39740     /**
39741      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39742      */
39743     growMax : 800,
39744     /**
39745      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39746      */
39747     vtype : null,
39748     /**
39749      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39750      */
39751     maskRe : null,
39752     /**
39753      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39754      */
39755     disableKeyFilter : false,
39756     /**
39757      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39758      */
39759     allowBlank : true,
39760     /**
39761      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39762      */
39763     minLength : 0,
39764     /**
39765      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39766      */
39767     maxLength : Number.MAX_VALUE,
39768     /**
39769      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39770      */
39771     minLengthText : "The minimum length for this field is {0}",
39772     /**
39773      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39774      */
39775     maxLengthText : "The maximum length for this field is {0}",
39776     /**
39777      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39778      */
39779     selectOnFocus : false,
39780     /**
39781      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39782      */    
39783     allowLeadingSpace : false,
39784     /**
39785      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39786      */
39787     blankText : "This field is required",
39788     /**
39789      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39790      * If available, this function will be called only after the basic validators all return true, and will be passed the
39791      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39792      */
39793     validator : null,
39794     /**
39795      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39796      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39797      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39798      */
39799     regex : null,
39800     /**
39801      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39802      */
39803     regexText : "",
39804     /**
39805      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39806      */
39807     emptyText : null,
39808    
39809
39810     // private
39811     initEvents : function()
39812     {
39813         if (this.emptyText) {
39814             this.el.attr('placeholder', this.emptyText);
39815         }
39816         
39817         Roo.form.TextField.superclass.initEvents.call(this);
39818         if(this.validationEvent == 'keyup'){
39819             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39820             this.el.on('keyup', this.filterValidation, this);
39821         }
39822         else if(this.validationEvent !== false){
39823             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39824         }
39825         
39826         if(this.selectOnFocus){
39827             this.on("focus", this.preFocus, this);
39828         }
39829         if (!this.allowLeadingSpace) {
39830             this.on('blur', this.cleanLeadingSpace, this);
39831         }
39832         
39833         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39834             this.el.on("keypress", this.filterKeys, this);
39835         }
39836         if(this.grow){
39837             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39838             this.el.on("click", this.autoSize,  this);
39839         }
39840         if(this.el.is('input[type=password]') && Roo.isSafari){
39841             this.el.on('keydown', this.SafariOnKeyDown, this);
39842         }
39843     },
39844
39845     processValue : function(value){
39846         if(this.stripCharsRe){
39847             var newValue = value.replace(this.stripCharsRe, '');
39848             if(newValue !== value){
39849                 this.setRawValue(newValue);
39850                 return newValue;
39851             }
39852         }
39853         return value;
39854     },
39855
39856     filterValidation : function(e){
39857         if(!e.isNavKeyPress()){
39858             this.validationTask.delay(this.validationDelay);
39859         }
39860     },
39861
39862     // private
39863     onKeyUp : function(e){
39864         if(!e.isNavKeyPress()){
39865             this.autoSize();
39866         }
39867     },
39868     // private - clean the leading white space
39869     cleanLeadingSpace : function(e)
39870     {
39871         if ( this.inputType == 'file') {
39872             return;
39873         }
39874         
39875         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39876     },
39877     /**
39878      * Resets the current field value to the originally-loaded value and clears any validation messages.
39879      *  
39880      */
39881     reset : function(){
39882         Roo.form.TextField.superclass.reset.call(this);
39883        
39884     }, 
39885     // private
39886     preFocus : function(){
39887         
39888         if(this.selectOnFocus){
39889             this.el.dom.select();
39890         }
39891     },
39892
39893     
39894     // private
39895     filterKeys : function(e){
39896         var k = e.getKey();
39897         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39898             return;
39899         }
39900         var c = e.getCharCode(), cc = String.fromCharCode(c);
39901         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39902             return;
39903         }
39904         if(!this.maskRe.test(cc)){
39905             e.stopEvent();
39906         }
39907     },
39908
39909     setValue : function(v){
39910         
39911         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39912         
39913         this.autoSize();
39914     },
39915
39916     /**
39917      * Validates a value according to the field's validation rules and marks the field as invalid
39918      * if the validation fails
39919      * @param {Mixed} value The value to validate
39920      * @return {Boolean} True if the value is valid, else false
39921      */
39922     validateValue : function(value){
39923         if(value.length < 1)  { // if it's blank
39924              if(this.allowBlank){
39925                 this.clearInvalid();
39926                 return true;
39927              }else{
39928                 this.markInvalid(this.blankText);
39929                 return false;
39930              }
39931         }
39932         if(value.length < this.minLength){
39933             this.markInvalid(String.format(this.minLengthText, this.minLength));
39934             return false;
39935         }
39936         if(value.length > this.maxLength){
39937             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39938             return false;
39939         }
39940         if(this.vtype){
39941             var vt = Roo.form.VTypes;
39942             if(!vt[this.vtype](value, this)){
39943                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39944                 return false;
39945             }
39946         }
39947         if(typeof this.validator == "function"){
39948             var msg = this.validator(value);
39949             if(msg !== true){
39950                 this.markInvalid(msg);
39951                 return false;
39952             }
39953         }
39954         if(this.regex && !this.regex.test(value)){
39955             this.markInvalid(this.regexText);
39956             return false;
39957         }
39958         return true;
39959     },
39960
39961     /**
39962      * Selects text in this field
39963      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39964      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39965      */
39966     selectText : function(start, end){
39967         var v = this.getRawValue();
39968         if(v.length > 0){
39969             start = start === undefined ? 0 : start;
39970             end = end === undefined ? v.length : end;
39971             var d = this.el.dom;
39972             if(d.setSelectionRange){
39973                 d.setSelectionRange(start, end);
39974             }else if(d.createTextRange){
39975                 var range = d.createTextRange();
39976                 range.moveStart("character", start);
39977                 range.moveEnd("character", v.length-end);
39978                 range.select();
39979             }
39980         }
39981     },
39982
39983     /**
39984      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39985      * This only takes effect if grow = true, and fires the autosize event.
39986      */
39987     autoSize : function(){
39988         if(!this.grow || !this.rendered){
39989             return;
39990         }
39991         if(!this.metrics){
39992             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39993         }
39994         var el = this.el;
39995         var v = el.dom.value;
39996         var d = document.createElement('div');
39997         d.appendChild(document.createTextNode(v));
39998         v = d.innerHTML;
39999         d = null;
40000         v += "&#160;";
40001         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40002         this.el.setWidth(w);
40003         this.fireEvent("autosize", this, w);
40004     },
40005     
40006     // private
40007     SafariOnKeyDown : function(event)
40008     {
40009         // this is a workaround for a password hang bug on chrome/ webkit.
40010         
40011         var isSelectAll = false;
40012         
40013         if(this.el.dom.selectionEnd > 0){
40014             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40015         }
40016         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40017             event.preventDefault();
40018             this.setValue('');
40019             return;
40020         }
40021         
40022         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40023             
40024             event.preventDefault();
40025             // this is very hacky as keydown always get's upper case.
40026             
40027             var cc = String.fromCharCode(event.getCharCode());
40028             
40029             
40030             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40031             
40032         }
40033         
40034         
40035     }
40036 });/*
40037  * Based on:
40038  * Ext JS Library 1.1.1
40039  * Copyright(c) 2006-2007, Ext JS, LLC.
40040  *
40041  * Originally Released Under LGPL - original licence link has changed is not relivant.
40042  *
40043  * Fork - LGPL
40044  * <script type="text/javascript">
40045  */
40046  
40047 /**
40048  * @class Roo.form.Hidden
40049  * @extends Roo.form.TextField
40050  * Simple Hidden element used on forms 
40051  * 
40052  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40053  * 
40054  * @constructor
40055  * Creates a new Hidden form element.
40056  * @param {Object} config Configuration options
40057  */
40058
40059
40060
40061 // easy hidden field...
40062 Roo.form.Hidden = function(config){
40063     Roo.form.Hidden.superclass.constructor.call(this, config);
40064 };
40065   
40066 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40067     fieldLabel:      '',
40068     inputType:      'hidden',
40069     width:          50,
40070     allowBlank:     true,
40071     labelSeparator: '',
40072     hidden:         true,
40073     itemCls :       'x-form-item-display-none'
40074
40075
40076 });
40077
40078
40079 /*
40080  * Based on:
40081  * Ext JS Library 1.1.1
40082  * Copyright(c) 2006-2007, Ext JS, LLC.
40083  *
40084  * Originally Released Under LGPL - original licence link has changed is not relivant.
40085  *
40086  * Fork - LGPL
40087  * <script type="text/javascript">
40088  */
40089  
40090 /**
40091  * @class Roo.form.TriggerField
40092  * @extends Roo.form.TextField
40093  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40094  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40095  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40096  * for which you can provide a custom implementation.  For example:
40097  * <pre><code>
40098 var trigger = new Roo.form.TriggerField();
40099 trigger.onTriggerClick = myTriggerFn;
40100 trigger.applyTo('my-field');
40101 </code></pre>
40102  *
40103  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40104  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40105  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40106  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40107  * @constructor
40108  * Create a new TriggerField.
40109  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40110  * to the base TextField)
40111  */
40112 Roo.form.TriggerField = function(config){
40113     this.mimicing = false;
40114     Roo.form.TriggerField.superclass.constructor.call(this, config);
40115 };
40116
40117 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40118     /**
40119      * @cfg {String} triggerClass A CSS class to apply to the trigger
40120      */
40121     /**
40122      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40123      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40124      */
40125     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40126     /**
40127      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40128      */
40129     hideTrigger:false,
40130
40131     /** @cfg {Boolean} grow @hide */
40132     /** @cfg {Number} growMin @hide */
40133     /** @cfg {Number} growMax @hide */
40134
40135     /**
40136      * @hide 
40137      * @method
40138      */
40139     autoSize: Roo.emptyFn,
40140     // private
40141     monitorTab : true,
40142     // private
40143     deferHeight : true,
40144
40145     
40146     actionMode : 'wrap',
40147     // private
40148     onResize : function(w, h){
40149         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40150         if(typeof w == 'number'){
40151             var x = w - this.trigger.getWidth();
40152             this.el.setWidth(this.adjustWidth('input', x));
40153             this.trigger.setStyle('left', x+'px');
40154         }
40155     },
40156
40157     // private
40158     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40159
40160     // private
40161     getResizeEl : function(){
40162         return this.wrap;
40163     },
40164
40165     // private
40166     getPositionEl : function(){
40167         return this.wrap;
40168     },
40169
40170     // private
40171     alignErrorIcon : function(){
40172         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40173     },
40174
40175     // private
40176     onRender : function(ct, position){
40177         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40178         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40179         this.trigger = this.wrap.createChild(this.triggerConfig ||
40180                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40181         if(this.hideTrigger){
40182             this.trigger.setDisplayed(false);
40183         }
40184         this.initTrigger();
40185         if(!this.width){
40186             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40187         }
40188     },
40189
40190     // private
40191     initTrigger : function(){
40192         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40193         this.trigger.addClassOnOver('x-form-trigger-over');
40194         this.trigger.addClassOnClick('x-form-trigger-click');
40195     },
40196
40197     // private
40198     onDestroy : function(){
40199         if(this.trigger){
40200             this.trigger.removeAllListeners();
40201             this.trigger.remove();
40202         }
40203         if(this.wrap){
40204             this.wrap.remove();
40205         }
40206         Roo.form.TriggerField.superclass.onDestroy.call(this);
40207     },
40208
40209     // private
40210     onFocus : function(){
40211         Roo.form.TriggerField.superclass.onFocus.call(this);
40212         if(!this.mimicing){
40213             this.wrap.addClass('x-trigger-wrap-focus');
40214             this.mimicing = true;
40215             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40216             if(this.monitorTab){
40217                 this.el.on("keydown", this.checkTab, this);
40218             }
40219         }
40220     },
40221
40222     // private
40223     checkTab : function(e){
40224         if(e.getKey() == e.TAB){
40225             this.triggerBlur();
40226         }
40227     },
40228
40229     // private
40230     onBlur : function(){
40231         // do nothing
40232     },
40233
40234     // private
40235     mimicBlur : function(e, t){
40236         if(!this.wrap.contains(t) && this.validateBlur()){
40237             this.triggerBlur();
40238         }
40239     },
40240
40241     // private
40242     triggerBlur : function(){
40243         this.mimicing = false;
40244         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40245         if(this.monitorTab){
40246             this.el.un("keydown", this.checkTab, this);
40247         }
40248         this.wrap.removeClass('x-trigger-wrap-focus');
40249         Roo.form.TriggerField.superclass.onBlur.call(this);
40250     },
40251
40252     // private
40253     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40254     validateBlur : function(e, t){
40255         return true;
40256     },
40257
40258     // private
40259     onDisable : function(){
40260         Roo.form.TriggerField.superclass.onDisable.call(this);
40261         if(this.wrap){
40262             this.wrap.addClass('x-item-disabled');
40263         }
40264     },
40265
40266     // private
40267     onEnable : function(){
40268         Roo.form.TriggerField.superclass.onEnable.call(this);
40269         if(this.wrap){
40270             this.wrap.removeClass('x-item-disabled');
40271         }
40272     },
40273
40274     // private
40275     onShow : function(){
40276         var ae = this.getActionEl();
40277         
40278         if(ae){
40279             ae.dom.style.display = '';
40280             ae.dom.style.visibility = 'visible';
40281         }
40282     },
40283
40284     // private
40285     
40286     onHide : function(){
40287         var ae = this.getActionEl();
40288         ae.dom.style.display = 'none';
40289     },
40290
40291     /**
40292      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40293      * by an implementing function.
40294      * @method
40295      * @param {EventObject} e
40296      */
40297     onTriggerClick : Roo.emptyFn
40298 });
40299
40300 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40301 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40302 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40303 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40304     initComponent : function(){
40305         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40306
40307         this.triggerConfig = {
40308             tag:'span', cls:'x-form-twin-triggers', cn:[
40309             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40310             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40311         ]};
40312     },
40313
40314     getTrigger : function(index){
40315         return this.triggers[index];
40316     },
40317
40318     initTrigger : function(){
40319         var ts = this.trigger.select('.x-form-trigger', true);
40320         this.wrap.setStyle('overflow', 'hidden');
40321         var triggerField = this;
40322         ts.each(function(t, all, index){
40323             t.hide = function(){
40324                 var w = triggerField.wrap.getWidth();
40325                 this.dom.style.display = 'none';
40326                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40327             };
40328             t.show = function(){
40329                 var w = triggerField.wrap.getWidth();
40330                 this.dom.style.display = '';
40331                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40332             };
40333             var triggerIndex = 'Trigger'+(index+1);
40334
40335             if(this['hide'+triggerIndex]){
40336                 t.dom.style.display = 'none';
40337             }
40338             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40339             t.addClassOnOver('x-form-trigger-over');
40340             t.addClassOnClick('x-form-trigger-click');
40341         }, this);
40342         this.triggers = ts.elements;
40343     },
40344
40345     onTrigger1Click : Roo.emptyFn,
40346     onTrigger2Click : Roo.emptyFn
40347 });/*
40348  * Based on:
40349  * Ext JS Library 1.1.1
40350  * Copyright(c) 2006-2007, Ext JS, LLC.
40351  *
40352  * Originally Released Under LGPL - original licence link has changed is not relivant.
40353  *
40354  * Fork - LGPL
40355  * <script type="text/javascript">
40356  */
40357  
40358 /**
40359  * @class Roo.form.TextArea
40360  * @extends Roo.form.TextField
40361  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40362  * support for auto-sizing.
40363  * @constructor
40364  * Creates a new TextArea
40365  * @param {Object} config Configuration options
40366  */
40367 Roo.form.TextArea = function(config){
40368     Roo.form.TextArea.superclass.constructor.call(this, config);
40369     // these are provided exchanges for backwards compat
40370     // minHeight/maxHeight were replaced by growMin/growMax to be
40371     // compatible with TextField growing config values
40372     if(this.minHeight !== undefined){
40373         this.growMin = this.minHeight;
40374     }
40375     if(this.maxHeight !== undefined){
40376         this.growMax = this.maxHeight;
40377     }
40378 };
40379
40380 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40381     /**
40382      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40383      */
40384     growMin : 60,
40385     /**
40386      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40387      */
40388     growMax: 1000,
40389     /**
40390      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40391      * in the field (equivalent to setting overflow: hidden, defaults to false)
40392      */
40393     preventScrollbars: false,
40394     /**
40395      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40396      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40397      */
40398
40399     // private
40400     onRender : function(ct, position){
40401         if(!this.el){
40402             this.defaultAutoCreate = {
40403                 tag: "textarea",
40404                 style:"width:300px;height:60px;",
40405                 autocomplete: "new-password"
40406             };
40407         }
40408         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40409         if(this.grow){
40410             this.textSizeEl = Roo.DomHelper.append(document.body, {
40411                 tag: "pre", cls: "x-form-grow-sizer"
40412             });
40413             if(this.preventScrollbars){
40414                 this.el.setStyle("overflow", "hidden");
40415             }
40416             this.el.setHeight(this.growMin);
40417         }
40418     },
40419
40420     onDestroy : function(){
40421         if(this.textSizeEl){
40422             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40423         }
40424         Roo.form.TextArea.superclass.onDestroy.call(this);
40425     },
40426
40427     // private
40428     onKeyUp : function(e){
40429         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40430             this.autoSize();
40431         }
40432     },
40433
40434     /**
40435      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40436      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40437      */
40438     autoSize : function(){
40439         if(!this.grow || !this.textSizeEl){
40440             return;
40441         }
40442         var el = this.el;
40443         var v = el.dom.value;
40444         var ts = this.textSizeEl;
40445
40446         ts.innerHTML = '';
40447         ts.appendChild(document.createTextNode(v));
40448         v = ts.innerHTML;
40449
40450         Roo.fly(ts).setWidth(this.el.getWidth());
40451         if(v.length < 1){
40452             v = "&#160;&#160;";
40453         }else{
40454             if(Roo.isIE){
40455                 v = v.replace(/\n/g, '<p>&#160;</p>');
40456             }
40457             v += "&#160;\n&#160;";
40458         }
40459         ts.innerHTML = v;
40460         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40461         if(h != this.lastHeight){
40462             this.lastHeight = h;
40463             this.el.setHeight(h);
40464             this.fireEvent("autosize", this, h);
40465         }
40466     }
40467 });/*
40468  * Based on:
40469  * Ext JS Library 1.1.1
40470  * Copyright(c) 2006-2007, Ext JS, LLC.
40471  *
40472  * Originally Released Under LGPL - original licence link has changed is not relivant.
40473  *
40474  * Fork - LGPL
40475  * <script type="text/javascript">
40476  */
40477  
40478
40479 /**
40480  * @class Roo.form.NumberField
40481  * @extends Roo.form.TextField
40482  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40483  * @constructor
40484  * Creates a new NumberField
40485  * @param {Object} config Configuration options
40486  */
40487 Roo.form.NumberField = function(config){
40488     Roo.form.NumberField.superclass.constructor.call(this, config);
40489 };
40490
40491 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40492     /**
40493      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40494      */
40495     fieldClass: "x-form-field x-form-num-field",
40496     /**
40497      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40498      */
40499     allowDecimals : true,
40500     /**
40501      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40502      */
40503     decimalSeparator : ".",
40504     /**
40505      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40506      */
40507     decimalPrecision : 2,
40508     /**
40509      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40510      */
40511     allowNegative : true,
40512     /**
40513      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40514      */
40515     minValue : Number.NEGATIVE_INFINITY,
40516     /**
40517      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40518      */
40519     maxValue : Number.MAX_VALUE,
40520     /**
40521      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40522      */
40523     minText : "The minimum value for this field is {0}",
40524     /**
40525      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40526      */
40527     maxText : "The maximum value for this field is {0}",
40528     /**
40529      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40530      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40531      */
40532     nanText : "{0} is not a valid number",
40533
40534     // private
40535     initEvents : function(){
40536         Roo.form.NumberField.superclass.initEvents.call(this);
40537         var allowed = "0123456789";
40538         if(this.allowDecimals){
40539             allowed += this.decimalSeparator;
40540         }
40541         if(this.allowNegative){
40542             allowed += "-";
40543         }
40544         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40545         var keyPress = function(e){
40546             var k = e.getKey();
40547             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40548                 return;
40549             }
40550             var c = e.getCharCode();
40551             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40552                 e.stopEvent();
40553             }
40554         };
40555         this.el.on("keypress", keyPress, this);
40556     },
40557
40558     // private
40559     validateValue : function(value){
40560         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40561             return false;
40562         }
40563         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40564              return true;
40565         }
40566         var num = this.parseValue(value);
40567         if(isNaN(num)){
40568             this.markInvalid(String.format(this.nanText, value));
40569             return false;
40570         }
40571         if(num < this.minValue){
40572             this.markInvalid(String.format(this.minText, this.minValue));
40573             return false;
40574         }
40575         if(num > this.maxValue){
40576             this.markInvalid(String.format(this.maxText, this.maxValue));
40577             return false;
40578         }
40579         return true;
40580     },
40581
40582     getValue : function(){
40583         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40584     },
40585
40586     // private
40587     parseValue : function(value){
40588         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40589         return isNaN(value) ? '' : value;
40590     },
40591
40592     // private
40593     fixPrecision : function(value){
40594         var nan = isNaN(value);
40595         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40596             return nan ? '' : value;
40597         }
40598         return parseFloat(value).toFixed(this.decimalPrecision);
40599     },
40600
40601     setValue : function(v){
40602         v = this.fixPrecision(v);
40603         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40604     },
40605
40606     // private
40607     decimalPrecisionFcn : function(v){
40608         return Math.floor(v);
40609     },
40610
40611     beforeBlur : function(){
40612         var v = this.parseValue(this.getRawValue());
40613         if(v){
40614             this.setValue(v);
40615         }
40616     }
40617 });/*
40618  * Based on:
40619  * Ext JS Library 1.1.1
40620  * Copyright(c) 2006-2007, Ext JS, LLC.
40621  *
40622  * Originally Released Under LGPL - original licence link has changed is not relivant.
40623  *
40624  * Fork - LGPL
40625  * <script type="text/javascript">
40626  */
40627  
40628 /**
40629  * @class Roo.form.DateField
40630  * @extends Roo.form.TriggerField
40631  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40632 * @constructor
40633 * Create a new DateField
40634 * @param {Object} config
40635  */
40636 Roo.form.DateField = function(config)
40637 {
40638     Roo.form.DateField.superclass.constructor.call(this, config);
40639     
40640       this.addEvents({
40641          
40642         /**
40643          * @event select
40644          * Fires when a date is selected
40645              * @param {Roo.form.DateField} combo This combo box
40646              * @param {Date} date The date selected
40647              */
40648         'select' : true
40649          
40650     });
40651     
40652     
40653     if(typeof this.minValue == "string") {
40654         this.minValue = this.parseDate(this.minValue);
40655     }
40656     if(typeof this.maxValue == "string") {
40657         this.maxValue = this.parseDate(this.maxValue);
40658     }
40659     this.ddMatch = null;
40660     if(this.disabledDates){
40661         var dd = this.disabledDates;
40662         var re = "(?:";
40663         for(var i = 0; i < dd.length; i++){
40664             re += dd[i];
40665             if(i != dd.length-1) {
40666                 re += "|";
40667             }
40668         }
40669         this.ddMatch = new RegExp(re + ")");
40670     }
40671 };
40672
40673 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40674     /**
40675      * @cfg {String} format
40676      * The default date format string which can be overriden for localization support.  The format must be
40677      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40678      */
40679     format : "m/d/y",
40680     /**
40681      * @cfg {String} altFormats
40682      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40683      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40684      */
40685     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40686     /**
40687      * @cfg {Array} disabledDays
40688      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40689      */
40690     disabledDays : null,
40691     /**
40692      * @cfg {String} disabledDaysText
40693      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40694      */
40695     disabledDaysText : "Disabled",
40696     /**
40697      * @cfg {Array} disabledDates
40698      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40699      * expression so they are very powerful. Some examples:
40700      * <ul>
40701      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40702      * <li>["03/08", "09/16"] would disable those days for every year</li>
40703      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40704      * <li>["03/../2006"] would disable every day in March 2006</li>
40705      * <li>["^03"] would disable every day in every March</li>
40706      * </ul>
40707      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40708      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40709      */
40710     disabledDates : null,
40711     /**
40712      * @cfg {String} disabledDatesText
40713      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40714      */
40715     disabledDatesText : "Disabled",
40716     /**
40717      * @cfg {Date/String} minValue
40718      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40719      * valid format (defaults to null).
40720      */
40721     minValue : null,
40722     /**
40723      * @cfg {Date/String} maxValue
40724      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40725      * valid format (defaults to null).
40726      */
40727     maxValue : null,
40728     /**
40729      * @cfg {String} minText
40730      * The error text to display when the date in the cell is before minValue (defaults to
40731      * 'The date in this field must be after {minValue}').
40732      */
40733     minText : "The date in this field must be equal to or after {0}",
40734     /**
40735      * @cfg {String} maxText
40736      * The error text to display when the date in the cell is after maxValue (defaults to
40737      * 'The date in this field must be before {maxValue}').
40738      */
40739     maxText : "The date in this field must be equal to or before {0}",
40740     /**
40741      * @cfg {String} invalidText
40742      * The error text to display when the date in the field is invalid (defaults to
40743      * '{value} is not a valid date - it must be in the format {format}').
40744      */
40745     invalidText : "{0} is not a valid date - it must be in the format {1}",
40746     /**
40747      * @cfg {String} triggerClass
40748      * An additional CSS class used to style the trigger button.  The trigger will always get the
40749      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40750      * which displays a calendar icon).
40751      */
40752     triggerClass : 'x-form-date-trigger',
40753     
40754
40755     /**
40756      * @cfg {Boolean} useIso
40757      * if enabled, then the date field will use a hidden field to store the 
40758      * real value as iso formated date. default (false)
40759      */ 
40760     useIso : false,
40761     /**
40762      * @cfg {String/Object} autoCreate
40763      * A DomHelper element spec, or true for a default element spec (defaults to
40764      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40765      */ 
40766     // private
40767     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40768     
40769     // private
40770     hiddenField: false,
40771     
40772     onRender : function(ct, position)
40773     {
40774         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40775         if (this.useIso) {
40776             //this.el.dom.removeAttribute('name'); 
40777             Roo.log("Changing name?");
40778             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40779             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40780                     'before', true);
40781             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40782             // prevent input submission
40783             this.hiddenName = this.name;
40784         }
40785             
40786             
40787     },
40788     
40789     // private
40790     validateValue : function(value)
40791     {
40792         value = this.formatDate(value);
40793         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40794             Roo.log('super failed');
40795             return false;
40796         }
40797         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40798              return true;
40799         }
40800         var svalue = value;
40801         value = this.parseDate(value);
40802         if(!value){
40803             Roo.log('parse date failed' + svalue);
40804             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40805             return false;
40806         }
40807         var time = value.getTime();
40808         if(this.minValue && time < this.minValue.getTime()){
40809             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40810             return false;
40811         }
40812         if(this.maxValue && time > this.maxValue.getTime()){
40813             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40814             return false;
40815         }
40816         if(this.disabledDays){
40817             var day = value.getDay();
40818             for(var i = 0; i < this.disabledDays.length; i++) {
40819                 if(day === this.disabledDays[i]){
40820                     this.markInvalid(this.disabledDaysText);
40821                     return false;
40822                 }
40823             }
40824         }
40825         var fvalue = this.formatDate(value);
40826         if(this.ddMatch && this.ddMatch.test(fvalue)){
40827             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40828             return false;
40829         }
40830         return true;
40831     },
40832
40833     // private
40834     // Provides logic to override the default TriggerField.validateBlur which just returns true
40835     validateBlur : function(){
40836         return !this.menu || !this.menu.isVisible();
40837     },
40838     
40839     getName: function()
40840     {
40841         // returns hidden if it's set..
40842         if (!this.rendered) {return ''};
40843         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40844         
40845     },
40846
40847     /**
40848      * Returns the current date value of the date field.
40849      * @return {Date} The date value
40850      */
40851     getValue : function(){
40852         
40853         return  this.hiddenField ?
40854                 this.hiddenField.value :
40855                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40856     },
40857
40858     /**
40859      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40860      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40861      * (the default format used is "m/d/y").
40862      * <br />Usage:
40863      * <pre><code>
40864 //All of these calls set the same date value (May 4, 2006)
40865
40866 //Pass a date object:
40867 var dt = new Date('5/4/06');
40868 dateField.setValue(dt);
40869
40870 //Pass a date string (default format):
40871 dateField.setValue('5/4/06');
40872
40873 //Pass a date string (custom format):
40874 dateField.format = 'Y-m-d';
40875 dateField.setValue('2006-5-4');
40876 </code></pre>
40877      * @param {String/Date} date The date or valid date string
40878      */
40879     setValue : function(date){
40880         if (this.hiddenField) {
40881             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40882         }
40883         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40884         // make sure the value field is always stored as a date..
40885         this.value = this.parseDate(date);
40886         
40887         
40888     },
40889
40890     // private
40891     parseDate : function(value){
40892         if(!value || value instanceof Date){
40893             return value;
40894         }
40895         var v = Date.parseDate(value, this.format);
40896          if (!v && this.useIso) {
40897             v = Date.parseDate(value, 'Y-m-d');
40898         }
40899         if(!v && this.altFormats){
40900             if(!this.altFormatsArray){
40901                 this.altFormatsArray = this.altFormats.split("|");
40902             }
40903             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40904                 v = Date.parseDate(value, this.altFormatsArray[i]);
40905             }
40906         }
40907         return v;
40908     },
40909
40910     // private
40911     formatDate : function(date, fmt){
40912         return (!date || !(date instanceof Date)) ?
40913                date : date.dateFormat(fmt || this.format);
40914     },
40915
40916     // private
40917     menuListeners : {
40918         select: function(m, d){
40919             
40920             this.setValue(d);
40921             this.fireEvent('select', this, d);
40922         },
40923         show : function(){ // retain focus styling
40924             this.onFocus();
40925         },
40926         hide : function(){
40927             this.focus.defer(10, this);
40928             var ml = this.menuListeners;
40929             this.menu.un("select", ml.select,  this);
40930             this.menu.un("show", ml.show,  this);
40931             this.menu.un("hide", ml.hide,  this);
40932         }
40933     },
40934
40935     // private
40936     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40937     onTriggerClick : function(){
40938         if(this.disabled){
40939             return;
40940         }
40941         if(this.menu == null){
40942             this.menu = new Roo.menu.DateMenu();
40943         }
40944         Roo.apply(this.menu.picker,  {
40945             showClear: this.allowBlank,
40946             minDate : this.minValue,
40947             maxDate : this.maxValue,
40948             disabledDatesRE : this.ddMatch,
40949             disabledDatesText : this.disabledDatesText,
40950             disabledDays : this.disabledDays,
40951             disabledDaysText : this.disabledDaysText,
40952             format : this.useIso ? 'Y-m-d' : this.format,
40953             minText : String.format(this.minText, this.formatDate(this.minValue)),
40954             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40955         });
40956         this.menu.on(Roo.apply({}, this.menuListeners, {
40957             scope:this
40958         }));
40959         this.menu.picker.setValue(this.getValue() || new Date());
40960         this.menu.show(this.el, "tl-bl?");
40961     },
40962
40963     beforeBlur : function(){
40964         var v = this.parseDate(this.getRawValue());
40965         if(v){
40966             this.setValue(v);
40967         }
40968     },
40969
40970     /*@
40971      * overide
40972      * 
40973      */
40974     isDirty : function() {
40975         if(this.disabled) {
40976             return false;
40977         }
40978         
40979         if(typeof(this.startValue) === 'undefined'){
40980             return false;
40981         }
40982         
40983         return String(this.getValue()) !== String(this.startValue);
40984         
40985     },
40986     // @overide
40987     cleanLeadingSpace : function(e)
40988     {
40989        return;
40990     }
40991     
40992 });/*
40993  * Based on:
40994  * Ext JS Library 1.1.1
40995  * Copyright(c) 2006-2007, Ext JS, LLC.
40996  *
40997  * Originally Released Under LGPL - original licence link has changed is not relivant.
40998  *
40999  * Fork - LGPL
41000  * <script type="text/javascript">
41001  */
41002  
41003 /**
41004  * @class Roo.form.MonthField
41005  * @extends Roo.form.TriggerField
41006  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41007 * @constructor
41008 * Create a new MonthField
41009 * @param {Object} config
41010  */
41011 Roo.form.MonthField = function(config){
41012     
41013     Roo.form.MonthField.superclass.constructor.call(this, config);
41014     
41015       this.addEvents({
41016          
41017         /**
41018          * @event select
41019          * Fires when a date is selected
41020              * @param {Roo.form.MonthFieeld} combo This combo box
41021              * @param {Date} date The date selected
41022              */
41023         'select' : true
41024          
41025     });
41026     
41027     
41028     if(typeof this.minValue == "string") {
41029         this.minValue = this.parseDate(this.minValue);
41030     }
41031     if(typeof this.maxValue == "string") {
41032         this.maxValue = this.parseDate(this.maxValue);
41033     }
41034     this.ddMatch = null;
41035     if(this.disabledDates){
41036         var dd = this.disabledDates;
41037         var re = "(?:";
41038         for(var i = 0; i < dd.length; i++){
41039             re += dd[i];
41040             if(i != dd.length-1) {
41041                 re += "|";
41042             }
41043         }
41044         this.ddMatch = new RegExp(re + ")");
41045     }
41046 };
41047
41048 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41049     /**
41050      * @cfg {String} format
41051      * The default date format string which can be overriden for localization support.  The format must be
41052      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41053      */
41054     format : "M Y",
41055     /**
41056      * @cfg {String} altFormats
41057      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41058      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41059      */
41060     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41061     /**
41062      * @cfg {Array} disabledDays
41063      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41064      */
41065     disabledDays : [0,1,2,3,4,5,6],
41066     /**
41067      * @cfg {String} disabledDaysText
41068      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41069      */
41070     disabledDaysText : "Disabled",
41071     /**
41072      * @cfg {Array} disabledDates
41073      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41074      * expression so they are very powerful. Some examples:
41075      * <ul>
41076      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41077      * <li>["03/08", "09/16"] would disable those days for every year</li>
41078      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41079      * <li>["03/../2006"] would disable every day in March 2006</li>
41080      * <li>["^03"] would disable every day in every March</li>
41081      * </ul>
41082      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41083      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41084      */
41085     disabledDates : null,
41086     /**
41087      * @cfg {String} disabledDatesText
41088      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41089      */
41090     disabledDatesText : "Disabled",
41091     /**
41092      * @cfg {Date/String} minValue
41093      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41094      * valid format (defaults to null).
41095      */
41096     minValue : null,
41097     /**
41098      * @cfg {Date/String} maxValue
41099      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41100      * valid format (defaults to null).
41101      */
41102     maxValue : null,
41103     /**
41104      * @cfg {String} minText
41105      * The error text to display when the date in the cell is before minValue (defaults to
41106      * 'The date in this field must be after {minValue}').
41107      */
41108     minText : "The date in this field must be equal to or after {0}",
41109     /**
41110      * @cfg {String} maxTextf
41111      * The error text to display when the date in the cell is after maxValue (defaults to
41112      * 'The date in this field must be before {maxValue}').
41113      */
41114     maxText : "The date in this field must be equal to or before {0}",
41115     /**
41116      * @cfg {String} invalidText
41117      * The error text to display when the date in the field is invalid (defaults to
41118      * '{value} is not a valid date - it must be in the format {format}').
41119      */
41120     invalidText : "{0} is not a valid date - it must be in the format {1}",
41121     /**
41122      * @cfg {String} triggerClass
41123      * An additional CSS class used to style the trigger button.  The trigger will always get the
41124      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41125      * which displays a calendar icon).
41126      */
41127     triggerClass : 'x-form-date-trigger',
41128     
41129
41130     /**
41131      * @cfg {Boolean} useIso
41132      * if enabled, then the date field will use a hidden field to store the 
41133      * real value as iso formated date. default (true)
41134      */ 
41135     useIso : true,
41136     /**
41137      * @cfg {String/Object} autoCreate
41138      * A DomHelper element spec, or true for a default element spec (defaults to
41139      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41140      */ 
41141     // private
41142     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41143     
41144     // private
41145     hiddenField: false,
41146     
41147     hideMonthPicker : false,
41148     
41149     onRender : function(ct, position)
41150     {
41151         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41152         if (this.useIso) {
41153             this.el.dom.removeAttribute('name'); 
41154             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41155                     'before', true);
41156             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41157             // prevent input submission
41158             this.hiddenName = this.name;
41159         }
41160             
41161             
41162     },
41163     
41164     // private
41165     validateValue : function(value)
41166     {
41167         value = this.formatDate(value);
41168         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41169             return false;
41170         }
41171         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41172              return true;
41173         }
41174         var svalue = value;
41175         value = this.parseDate(value);
41176         if(!value){
41177             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41178             return false;
41179         }
41180         var time = value.getTime();
41181         if(this.minValue && time < this.minValue.getTime()){
41182             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41183             return false;
41184         }
41185         if(this.maxValue && time > this.maxValue.getTime()){
41186             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41187             return false;
41188         }
41189         /*if(this.disabledDays){
41190             var day = value.getDay();
41191             for(var i = 0; i < this.disabledDays.length; i++) {
41192                 if(day === this.disabledDays[i]){
41193                     this.markInvalid(this.disabledDaysText);
41194                     return false;
41195                 }
41196             }
41197         }
41198         */
41199         var fvalue = this.formatDate(value);
41200         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41201             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41202             return false;
41203         }
41204         */
41205         return true;
41206     },
41207
41208     // private
41209     // Provides logic to override the default TriggerField.validateBlur which just returns true
41210     validateBlur : function(){
41211         return !this.menu || !this.menu.isVisible();
41212     },
41213
41214     /**
41215      * Returns the current date value of the date field.
41216      * @return {Date} The date value
41217      */
41218     getValue : function(){
41219         
41220         
41221         
41222         return  this.hiddenField ?
41223                 this.hiddenField.value :
41224                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41225     },
41226
41227     /**
41228      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41229      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41230      * (the default format used is "m/d/y").
41231      * <br />Usage:
41232      * <pre><code>
41233 //All of these calls set the same date value (May 4, 2006)
41234
41235 //Pass a date object:
41236 var dt = new Date('5/4/06');
41237 monthField.setValue(dt);
41238
41239 //Pass a date string (default format):
41240 monthField.setValue('5/4/06');
41241
41242 //Pass a date string (custom format):
41243 monthField.format = 'Y-m-d';
41244 monthField.setValue('2006-5-4');
41245 </code></pre>
41246      * @param {String/Date} date The date or valid date string
41247      */
41248     setValue : function(date){
41249         Roo.log('month setValue' + date);
41250         // can only be first of month..
41251         
41252         var val = this.parseDate(date);
41253         
41254         if (this.hiddenField) {
41255             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41256         }
41257         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41258         this.value = this.parseDate(date);
41259     },
41260
41261     // private
41262     parseDate : function(value){
41263         if(!value || value instanceof Date){
41264             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41265             return value;
41266         }
41267         var v = Date.parseDate(value, this.format);
41268         if (!v && this.useIso) {
41269             v = Date.parseDate(value, 'Y-m-d');
41270         }
41271         if (v) {
41272             // 
41273             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41274         }
41275         
41276         
41277         if(!v && this.altFormats){
41278             if(!this.altFormatsArray){
41279                 this.altFormatsArray = this.altFormats.split("|");
41280             }
41281             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41282                 v = Date.parseDate(value, this.altFormatsArray[i]);
41283             }
41284         }
41285         return v;
41286     },
41287
41288     // private
41289     formatDate : function(date, fmt){
41290         return (!date || !(date instanceof Date)) ?
41291                date : date.dateFormat(fmt || this.format);
41292     },
41293
41294     // private
41295     menuListeners : {
41296         select: function(m, d){
41297             this.setValue(d);
41298             this.fireEvent('select', this, d);
41299         },
41300         show : function(){ // retain focus styling
41301             this.onFocus();
41302         },
41303         hide : function(){
41304             this.focus.defer(10, this);
41305             var ml = this.menuListeners;
41306             this.menu.un("select", ml.select,  this);
41307             this.menu.un("show", ml.show,  this);
41308             this.menu.un("hide", ml.hide,  this);
41309         }
41310     },
41311     // private
41312     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41313     onTriggerClick : function(){
41314         if(this.disabled){
41315             return;
41316         }
41317         if(this.menu == null){
41318             this.menu = new Roo.menu.DateMenu();
41319            
41320         }
41321         
41322         Roo.apply(this.menu.picker,  {
41323             
41324             showClear: this.allowBlank,
41325             minDate : this.minValue,
41326             maxDate : this.maxValue,
41327             disabledDatesRE : this.ddMatch,
41328             disabledDatesText : this.disabledDatesText,
41329             
41330             format : this.useIso ? 'Y-m-d' : this.format,
41331             minText : String.format(this.minText, this.formatDate(this.minValue)),
41332             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41333             
41334         });
41335          this.menu.on(Roo.apply({}, this.menuListeners, {
41336             scope:this
41337         }));
41338        
41339         
41340         var m = this.menu;
41341         var p = m.picker;
41342         
41343         // hide month picker get's called when we called by 'before hide';
41344         
41345         var ignorehide = true;
41346         p.hideMonthPicker  = function(disableAnim){
41347             if (ignorehide) {
41348                 return;
41349             }
41350              if(this.monthPicker){
41351                 Roo.log("hideMonthPicker called");
41352                 if(disableAnim === true){
41353                     this.monthPicker.hide();
41354                 }else{
41355                     this.monthPicker.slideOut('t', {duration:.2});
41356                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41357                     p.fireEvent("select", this, this.value);
41358                     m.hide();
41359                 }
41360             }
41361         }
41362         
41363         Roo.log('picker set value');
41364         Roo.log(this.getValue());
41365         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41366         m.show(this.el, 'tl-bl?');
41367         ignorehide  = false;
41368         // this will trigger hideMonthPicker..
41369         
41370         
41371         // hidden the day picker
41372         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41373         
41374         
41375         
41376       
41377         
41378         p.showMonthPicker.defer(100, p);
41379     
41380         
41381        
41382     },
41383
41384     beforeBlur : function(){
41385         var v = this.parseDate(this.getRawValue());
41386         if(v){
41387             this.setValue(v);
41388         }
41389     }
41390
41391     /** @cfg {Boolean} grow @hide */
41392     /** @cfg {Number} growMin @hide */
41393     /** @cfg {Number} growMax @hide */
41394     /**
41395      * @hide
41396      * @method autoSize
41397      */
41398 });/*
41399  * Based on:
41400  * Ext JS Library 1.1.1
41401  * Copyright(c) 2006-2007, Ext JS, LLC.
41402  *
41403  * Originally Released Under LGPL - original licence link has changed is not relivant.
41404  *
41405  * Fork - LGPL
41406  * <script type="text/javascript">
41407  */
41408  
41409
41410 /**
41411  * @class Roo.form.ComboBox
41412  * @extends Roo.form.TriggerField
41413  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41414  * @constructor
41415  * Create a new ComboBox.
41416  * @param {Object} config Configuration options
41417  */
41418 Roo.form.ComboBox = function(config){
41419     Roo.form.ComboBox.superclass.constructor.call(this, config);
41420     this.addEvents({
41421         /**
41422          * @event expand
41423          * Fires when the dropdown list is expanded
41424              * @param {Roo.form.ComboBox} combo This combo box
41425              */
41426         'expand' : true,
41427         /**
41428          * @event collapse
41429          * Fires when the dropdown list is collapsed
41430              * @param {Roo.form.ComboBox} combo This combo box
41431              */
41432         'collapse' : true,
41433         /**
41434          * @event beforeselect
41435          * Fires before a list item is selected. Return false to cancel the selection.
41436              * @param {Roo.form.ComboBox} combo This combo box
41437              * @param {Roo.data.Record} record The data record returned from the underlying store
41438              * @param {Number} index The index of the selected item in the dropdown list
41439              */
41440         'beforeselect' : true,
41441         /**
41442          * @event select
41443          * Fires when a list item is selected
41444              * @param {Roo.form.ComboBox} combo This combo box
41445              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41446              * @param {Number} index The index of the selected item in the dropdown list
41447              */
41448         'select' : true,
41449         /**
41450          * @event beforequery
41451          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41452          * The event object passed has these properties:
41453              * @param {Roo.form.ComboBox} combo This combo box
41454              * @param {String} query The query
41455              * @param {Boolean} forceAll true to force "all" query
41456              * @param {Boolean} cancel true to cancel the query
41457              * @param {Object} e The query event object
41458              */
41459         'beforequery': true,
41460          /**
41461          * @event add
41462          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41463              * @param {Roo.form.ComboBox} combo This combo box
41464              */
41465         'add' : true,
41466         /**
41467          * @event edit
41468          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41469              * @param {Roo.form.ComboBox} combo This combo box
41470              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41471              */
41472         'edit' : true
41473         
41474         
41475     });
41476     if(this.transform){
41477         this.allowDomMove = false;
41478         var s = Roo.getDom(this.transform);
41479         if(!this.hiddenName){
41480             this.hiddenName = s.name;
41481         }
41482         if(!this.store){
41483             this.mode = 'local';
41484             var d = [], opts = s.options;
41485             for(var i = 0, len = opts.length;i < len; i++){
41486                 var o = opts[i];
41487                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41488                 if(o.selected) {
41489                     this.value = value;
41490                 }
41491                 d.push([value, o.text]);
41492             }
41493             this.store = new Roo.data.SimpleStore({
41494                 'id': 0,
41495                 fields: ['value', 'text'],
41496                 data : d
41497             });
41498             this.valueField = 'value';
41499             this.displayField = 'text';
41500         }
41501         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41502         if(!this.lazyRender){
41503             this.target = true;
41504             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41505             s.parentNode.removeChild(s); // remove it
41506             this.render(this.el.parentNode);
41507         }else{
41508             s.parentNode.removeChild(s); // remove it
41509         }
41510
41511     }
41512     if (this.store) {
41513         this.store = Roo.factory(this.store, Roo.data);
41514     }
41515     
41516     this.selectedIndex = -1;
41517     if(this.mode == 'local'){
41518         if(config.queryDelay === undefined){
41519             this.queryDelay = 10;
41520         }
41521         if(config.minChars === undefined){
41522             this.minChars = 0;
41523         }
41524     }
41525 };
41526
41527 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41528     /**
41529      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41530      */
41531     /**
41532      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41533      * rendering into an Roo.Editor, defaults to false)
41534      */
41535     /**
41536      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41537      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41538      */
41539     /**
41540      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41541      */
41542     /**
41543      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41544      * the dropdown list (defaults to undefined, with no header element)
41545      */
41546
41547      /**
41548      * @cfg {String/Roo.Template} tpl The template to use to render the output
41549      */
41550      
41551     // private
41552     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41553     /**
41554      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41555      */
41556     listWidth: undefined,
41557     /**
41558      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41559      * mode = 'remote' or 'text' if mode = 'local')
41560      */
41561     displayField: undefined,
41562     /**
41563      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41564      * mode = 'remote' or 'value' if mode = 'local'). 
41565      * Note: use of a valueField requires the user make a selection
41566      * in order for a value to be mapped.
41567      */
41568     valueField: undefined,
41569     
41570     
41571     /**
41572      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41573      * field's data value (defaults to the underlying DOM element's name)
41574      */
41575     hiddenName: undefined,
41576     /**
41577      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41578      */
41579     listClass: '',
41580     /**
41581      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41582      */
41583     selectedClass: 'x-combo-selected',
41584     /**
41585      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41586      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41587      * which displays a downward arrow icon).
41588      */
41589     triggerClass : 'x-form-arrow-trigger',
41590     /**
41591      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41592      */
41593     shadow:'sides',
41594     /**
41595      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41596      * anchor positions (defaults to 'tl-bl')
41597      */
41598     listAlign: 'tl-bl?',
41599     /**
41600      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41601      */
41602     maxHeight: 300,
41603     /**
41604      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41605      * query specified by the allQuery config option (defaults to 'query')
41606      */
41607     triggerAction: 'query',
41608     /**
41609      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41610      * (defaults to 4, does not apply if editable = false)
41611      */
41612     minChars : 4,
41613     /**
41614      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41615      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41616      */
41617     typeAhead: false,
41618     /**
41619      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41620      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41621      */
41622     queryDelay: 500,
41623     /**
41624      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41625      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41626      */
41627     pageSize: 0,
41628     /**
41629      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41630      * when editable = true (defaults to false)
41631      */
41632     selectOnFocus:false,
41633     /**
41634      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41635      */
41636     queryParam: 'query',
41637     /**
41638      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41639      * when mode = 'remote' (defaults to 'Loading...')
41640      */
41641     loadingText: 'Loading...',
41642     /**
41643      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41644      */
41645     resizable: false,
41646     /**
41647      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41648      */
41649     handleHeight : 8,
41650     /**
41651      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41652      * traditional select (defaults to true)
41653      */
41654     editable: true,
41655     /**
41656      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41657      */
41658     allQuery: '',
41659     /**
41660      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41661      */
41662     mode: 'remote',
41663     /**
41664      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41665      * listWidth has a higher value)
41666      */
41667     minListWidth : 70,
41668     /**
41669      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41670      * allow the user to set arbitrary text into the field (defaults to false)
41671      */
41672     forceSelection:false,
41673     /**
41674      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41675      * if typeAhead = true (defaults to 250)
41676      */
41677     typeAheadDelay : 250,
41678     /**
41679      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41680      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41681      */
41682     valueNotFoundText : undefined,
41683     /**
41684      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41685      */
41686     blockFocus : false,
41687     
41688     /**
41689      * @cfg {Boolean} disableClear Disable showing of clear button.
41690      */
41691     disableClear : false,
41692     /**
41693      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41694      */
41695     alwaysQuery : false,
41696     
41697     //private
41698     addicon : false,
41699     editicon: false,
41700     
41701     // element that contains real text value.. (when hidden is used..)
41702      
41703     // private
41704     onRender : function(ct, position)
41705     {
41706         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41707         
41708         if(this.hiddenName){
41709             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41710                     'before', true);
41711             this.hiddenField.value =
41712                 this.hiddenValue !== undefined ? this.hiddenValue :
41713                 this.value !== undefined ? this.value : '';
41714
41715             // prevent input submission
41716             this.el.dom.removeAttribute('name');
41717              
41718              
41719         }
41720         
41721         if(Roo.isGecko){
41722             this.el.dom.setAttribute('autocomplete', 'off');
41723         }
41724
41725         var cls = 'x-combo-list';
41726
41727         this.list = new Roo.Layer({
41728             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41729         });
41730
41731         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41732         this.list.setWidth(lw);
41733         this.list.swallowEvent('mousewheel');
41734         this.assetHeight = 0;
41735
41736         if(this.title){
41737             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41738             this.assetHeight += this.header.getHeight();
41739         }
41740
41741         this.innerList = this.list.createChild({cls:cls+'-inner'});
41742         this.innerList.on('mouseover', this.onViewOver, this);
41743         this.innerList.on('mousemove', this.onViewMove, this);
41744         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41745         
41746         if(this.allowBlank && !this.pageSize && !this.disableClear){
41747             this.footer = this.list.createChild({cls:cls+'-ft'});
41748             this.pageTb = new Roo.Toolbar(this.footer);
41749            
41750         }
41751         if(this.pageSize){
41752             this.footer = this.list.createChild({cls:cls+'-ft'});
41753             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41754                     {pageSize: this.pageSize});
41755             
41756         }
41757         
41758         if (this.pageTb && this.allowBlank && !this.disableClear) {
41759             var _this = this;
41760             this.pageTb.add(new Roo.Toolbar.Fill(), {
41761                 cls: 'x-btn-icon x-btn-clear',
41762                 text: '&#160;',
41763                 handler: function()
41764                 {
41765                     _this.collapse();
41766                     _this.clearValue();
41767                     _this.onSelect(false, -1);
41768                 }
41769             });
41770         }
41771         if (this.footer) {
41772             this.assetHeight += this.footer.getHeight();
41773         }
41774         
41775
41776         if(!this.tpl){
41777             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41778         }
41779
41780         this.view = new Roo.View(this.innerList, this.tpl, {
41781             singleSelect:true,
41782             store: this.store,
41783             selectedClass: this.selectedClass
41784         });
41785
41786         this.view.on('click', this.onViewClick, this);
41787
41788         this.store.on('beforeload', this.onBeforeLoad, this);
41789         this.store.on('load', this.onLoad, this);
41790         this.store.on('loadexception', this.onLoadException, this);
41791
41792         if(this.resizable){
41793             this.resizer = new Roo.Resizable(this.list,  {
41794                pinned:true, handles:'se'
41795             });
41796             this.resizer.on('resize', function(r, w, h){
41797                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41798                 this.listWidth = w;
41799                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41800                 this.restrictHeight();
41801             }, this);
41802             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41803         }
41804         if(!this.editable){
41805             this.editable = true;
41806             this.setEditable(false);
41807         }  
41808         
41809         
41810         if (typeof(this.events.add.listeners) != 'undefined') {
41811             
41812             this.addicon = this.wrap.createChild(
41813                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41814        
41815             this.addicon.on('click', function(e) {
41816                 this.fireEvent('add', this);
41817             }, this);
41818         }
41819         if (typeof(this.events.edit.listeners) != 'undefined') {
41820             
41821             this.editicon = this.wrap.createChild(
41822                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41823             if (this.addicon) {
41824                 this.editicon.setStyle('margin-left', '40px');
41825             }
41826             this.editicon.on('click', function(e) {
41827                 
41828                 // we fire even  if inothing is selected..
41829                 this.fireEvent('edit', this, this.lastData );
41830                 
41831             }, this);
41832         }
41833         
41834         
41835         
41836     },
41837
41838     // private
41839     initEvents : function(){
41840         Roo.form.ComboBox.superclass.initEvents.call(this);
41841
41842         this.keyNav = new Roo.KeyNav(this.el, {
41843             "up" : function(e){
41844                 this.inKeyMode = true;
41845                 this.selectPrev();
41846             },
41847
41848             "down" : function(e){
41849                 if(!this.isExpanded()){
41850                     this.onTriggerClick();
41851                 }else{
41852                     this.inKeyMode = true;
41853                     this.selectNext();
41854                 }
41855             },
41856
41857             "enter" : function(e){
41858                 this.onViewClick();
41859                 //return true;
41860             },
41861
41862             "esc" : function(e){
41863                 this.collapse();
41864             },
41865
41866             "tab" : function(e){
41867                 this.onViewClick(false);
41868                 this.fireEvent("specialkey", this, e);
41869                 return true;
41870             },
41871
41872             scope : this,
41873
41874             doRelay : function(foo, bar, hname){
41875                 if(hname == 'down' || this.scope.isExpanded()){
41876                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41877                 }
41878                 return true;
41879             },
41880
41881             forceKeyDown: true
41882         });
41883         this.queryDelay = Math.max(this.queryDelay || 10,
41884                 this.mode == 'local' ? 10 : 250);
41885         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41886         if(this.typeAhead){
41887             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41888         }
41889         if(this.editable !== false){
41890             this.el.on("keyup", this.onKeyUp, this);
41891         }
41892         if(this.forceSelection){
41893             this.on('blur', this.doForce, this);
41894         }
41895     },
41896
41897     onDestroy : function(){
41898         if(this.view){
41899             this.view.setStore(null);
41900             this.view.el.removeAllListeners();
41901             this.view.el.remove();
41902             this.view.purgeListeners();
41903         }
41904         if(this.list){
41905             this.list.destroy();
41906         }
41907         if(this.store){
41908             this.store.un('beforeload', this.onBeforeLoad, this);
41909             this.store.un('load', this.onLoad, this);
41910             this.store.un('loadexception', this.onLoadException, this);
41911         }
41912         Roo.form.ComboBox.superclass.onDestroy.call(this);
41913     },
41914
41915     // private
41916     fireKey : function(e){
41917         if(e.isNavKeyPress() && !this.list.isVisible()){
41918             this.fireEvent("specialkey", this, e);
41919         }
41920     },
41921
41922     // private
41923     onResize: function(w, h){
41924         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41925         
41926         if(typeof w != 'number'){
41927             // we do not handle it!?!?
41928             return;
41929         }
41930         var tw = this.trigger.getWidth();
41931         tw += this.addicon ? this.addicon.getWidth() : 0;
41932         tw += this.editicon ? this.editicon.getWidth() : 0;
41933         var x = w - tw;
41934         this.el.setWidth( this.adjustWidth('input', x));
41935             
41936         this.trigger.setStyle('left', x+'px');
41937         
41938         if(this.list && this.listWidth === undefined){
41939             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41940             this.list.setWidth(lw);
41941             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41942         }
41943         
41944     
41945         
41946     },
41947
41948     /**
41949      * Allow or prevent the user from directly editing the field text.  If false is passed,
41950      * the user will only be able to select from the items defined in the dropdown list.  This method
41951      * is the runtime equivalent of setting the 'editable' config option at config time.
41952      * @param {Boolean} value True to allow the user to directly edit the field text
41953      */
41954     setEditable : function(value){
41955         if(value == this.editable){
41956             return;
41957         }
41958         this.editable = value;
41959         if(!value){
41960             this.el.dom.setAttribute('readOnly', true);
41961             this.el.on('mousedown', this.onTriggerClick,  this);
41962             this.el.addClass('x-combo-noedit');
41963         }else{
41964             this.el.dom.setAttribute('readOnly', false);
41965             this.el.un('mousedown', this.onTriggerClick,  this);
41966             this.el.removeClass('x-combo-noedit');
41967         }
41968     },
41969
41970     // private
41971     onBeforeLoad : function(){
41972         if(!this.hasFocus){
41973             return;
41974         }
41975         this.innerList.update(this.loadingText ?
41976                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41977         this.restrictHeight();
41978         this.selectedIndex = -1;
41979     },
41980
41981     // private
41982     onLoad : function(){
41983         if(!this.hasFocus){
41984             return;
41985         }
41986         if(this.store.getCount() > 0){
41987             this.expand();
41988             this.restrictHeight();
41989             if(this.lastQuery == this.allQuery){
41990                 if(this.editable){
41991                     this.el.dom.select();
41992                 }
41993                 if(!this.selectByValue(this.value, true)){
41994                     this.select(0, true);
41995                 }
41996             }else{
41997                 this.selectNext();
41998                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41999                     this.taTask.delay(this.typeAheadDelay);
42000                 }
42001             }
42002         }else{
42003             this.onEmptyResults();
42004         }
42005         //this.el.focus();
42006     },
42007     // private
42008     onLoadException : function()
42009     {
42010         this.collapse();
42011         Roo.log(this.store.reader.jsonData);
42012         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42013             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42014         }
42015         
42016         
42017     },
42018     // private
42019     onTypeAhead : function(){
42020         if(this.store.getCount() > 0){
42021             var r = this.store.getAt(0);
42022             var newValue = r.data[this.displayField];
42023             var len = newValue.length;
42024             var selStart = this.getRawValue().length;
42025             if(selStart != len){
42026                 this.setRawValue(newValue);
42027                 this.selectText(selStart, newValue.length);
42028             }
42029         }
42030     },
42031
42032     // private
42033     onSelect : function(record, index){
42034         if(this.fireEvent('beforeselect', this, record, index) !== false){
42035             this.setFromData(index > -1 ? record.data : false);
42036             this.collapse();
42037             this.fireEvent('select', this, record, index);
42038         }
42039     },
42040
42041     /**
42042      * Returns the currently selected field value or empty string if no value is set.
42043      * @return {String} value The selected value
42044      */
42045     getValue : function(){
42046         if(this.valueField){
42047             return typeof this.value != 'undefined' ? this.value : '';
42048         }
42049         return Roo.form.ComboBox.superclass.getValue.call(this);
42050     },
42051
42052     /**
42053      * Clears any text/value currently set in the field
42054      */
42055     clearValue : function(){
42056         if(this.hiddenField){
42057             this.hiddenField.value = '';
42058         }
42059         this.value = '';
42060         this.setRawValue('');
42061         this.lastSelectionText = '';
42062         
42063     },
42064
42065     /**
42066      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42067      * will be displayed in the field.  If the value does not match the data value of an existing item,
42068      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42069      * Otherwise the field will be blank (although the value will still be set).
42070      * @param {String} value The value to match
42071      */
42072     setValue : function(v){
42073         var text = v;
42074         if(this.valueField){
42075             var r = this.findRecord(this.valueField, v);
42076             if(r){
42077                 text = r.data[this.displayField];
42078             }else if(this.valueNotFoundText !== undefined){
42079                 text = this.valueNotFoundText;
42080             }
42081         }
42082         this.lastSelectionText = text;
42083         if(this.hiddenField){
42084             this.hiddenField.value = v;
42085         }
42086         Roo.form.ComboBox.superclass.setValue.call(this, text);
42087         this.value = v;
42088     },
42089     /**
42090      * @property {Object} the last set data for the element
42091      */
42092     
42093     lastData : false,
42094     /**
42095      * Sets the value of the field based on a object which is related to the record format for the store.
42096      * @param {Object} value the value to set as. or false on reset?
42097      */
42098     setFromData : function(o){
42099         var dv = ''; // display value
42100         var vv = ''; // value value..
42101         this.lastData = o;
42102         if (this.displayField) {
42103             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42104         } else {
42105             // this is an error condition!!!
42106             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42107         }
42108         
42109         if(this.valueField){
42110             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42111         }
42112         if(this.hiddenField){
42113             this.hiddenField.value = vv;
42114             
42115             this.lastSelectionText = dv;
42116             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42117             this.value = vv;
42118             return;
42119         }
42120         // no hidden field.. - we store the value in 'value', but still display
42121         // display field!!!!
42122         this.lastSelectionText = dv;
42123         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42124         this.value = vv;
42125         
42126         
42127     },
42128     // private
42129     reset : function(){
42130         // overridden so that last data is reset..
42131         this.setValue(this.resetValue);
42132         this.originalValue = this.getValue();
42133         this.clearInvalid();
42134         this.lastData = false;
42135         if (this.view) {
42136             this.view.clearSelections();
42137         }
42138     },
42139     // private
42140     findRecord : function(prop, value){
42141         var record;
42142         if(this.store.getCount() > 0){
42143             this.store.each(function(r){
42144                 if(r.data[prop] == value){
42145                     record = r;
42146                     return false;
42147                 }
42148                 return true;
42149             });
42150         }
42151         return record;
42152     },
42153     
42154     getName: function()
42155     {
42156         // returns hidden if it's set..
42157         if (!this.rendered) {return ''};
42158         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42159         
42160     },
42161     // private
42162     onViewMove : function(e, t){
42163         this.inKeyMode = false;
42164     },
42165
42166     // private
42167     onViewOver : function(e, t){
42168         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42169             return;
42170         }
42171         var item = this.view.findItemFromChild(t);
42172         if(item){
42173             var index = this.view.indexOf(item);
42174             this.select(index, false);
42175         }
42176     },
42177
42178     // private
42179     onViewClick : function(doFocus)
42180     {
42181         var index = this.view.getSelectedIndexes()[0];
42182         var r = this.store.getAt(index);
42183         if(r){
42184             this.onSelect(r, index);
42185         }
42186         if(doFocus !== false && !this.blockFocus){
42187             this.el.focus();
42188         }
42189     },
42190
42191     // private
42192     restrictHeight : function(){
42193         this.innerList.dom.style.height = '';
42194         var inner = this.innerList.dom;
42195         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42196         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42197         this.list.beginUpdate();
42198         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42199         this.list.alignTo(this.el, this.listAlign);
42200         this.list.endUpdate();
42201     },
42202
42203     // private
42204     onEmptyResults : function(){
42205         this.collapse();
42206     },
42207
42208     /**
42209      * Returns true if the dropdown list is expanded, else false.
42210      */
42211     isExpanded : function(){
42212         return this.list.isVisible();
42213     },
42214
42215     /**
42216      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42217      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42218      * @param {String} value The data value of the item to select
42219      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42220      * selected item if it is not currently in view (defaults to true)
42221      * @return {Boolean} True if the value matched an item in the list, else false
42222      */
42223     selectByValue : function(v, scrollIntoView){
42224         if(v !== undefined && v !== null){
42225             var r = this.findRecord(this.valueField || this.displayField, v);
42226             if(r){
42227                 this.select(this.store.indexOf(r), scrollIntoView);
42228                 return true;
42229             }
42230         }
42231         return false;
42232     },
42233
42234     /**
42235      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42236      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42237      * @param {Number} index The zero-based index of the list item to select
42238      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42239      * selected item if it is not currently in view (defaults to true)
42240      */
42241     select : function(index, scrollIntoView){
42242         this.selectedIndex = index;
42243         this.view.select(index);
42244         if(scrollIntoView !== false){
42245             var el = this.view.getNode(index);
42246             if(el){
42247                 this.innerList.scrollChildIntoView(el, false);
42248             }
42249         }
42250     },
42251
42252     // private
42253     selectNext : function(){
42254         var ct = this.store.getCount();
42255         if(ct > 0){
42256             if(this.selectedIndex == -1){
42257                 this.select(0);
42258             }else if(this.selectedIndex < ct-1){
42259                 this.select(this.selectedIndex+1);
42260             }
42261         }
42262     },
42263
42264     // private
42265     selectPrev : function(){
42266         var ct = this.store.getCount();
42267         if(ct > 0){
42268             if(this.selectedIndex == -1){
42269                 this.select(0);
42270             }else if(this.selectedIndex != 0){
42271                 this.select(this.selectedIndex-1);
42272             }
42273         }
42274     },
42275
42276     // private
42277     onKeyUp : function(e){
42278         if(this.editable !== false && !e.isSpecialKey()){
42279             this.lastKey = e.getKey();
42280             this.dqTask.delay(this.queryDelay);
42281         }
42282     },
42283
42284     // private
42285     validateBlur : function(){
42286         return !this.list || !this.list.isVisible();   
42287     },
42288
42289     // private
42290     initQuery : function(){
42291         this.doQuery(this.getRawValue());
42292     },
42293
42294     // private
42295     doForce : function(){
42296         if(this.el.dom.value.length > 0){
42297             this.el.dom.value =
42298                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42299              
42300         }
42301     },
42302
42303     /**
42304      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42305      * query allowing the query action to be canceled if needed.
42306      * @param {String} query The SQL query to execute
42307      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42308      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42309      * saved in the current store (defaults to false)
42310      */
42311     doQuery : function(q, forceAll){
42312         if(q === undefined || q === null){
42313             q = '';
42314         }
42315         var qe = {
42316             query: q,
42317             forceAll: forceAll,
42318             combo: this,
42319             cancel:false
42320         };
42321         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42322             return false;
42323         }
42324         q = qe.query;
42325         forceAll = qe.forceAll;
42326         if(forceAll === true || (q.length >= this.minChars)){
42327             if(this.lastQuery != q || this.alwaysQuery){
42328                 this.lastQuery = q;
42329                 if(this.mode == 'local'){
42330                     this.selectedIndex = -1;
42331                     if(forceAll){
42332                         this.store.clearFilter();
42333                     }else{
42334                         this.store.filter(this.displayField, q);
42335                     }
42336                     this.onLoad();
42337                 }else{
42338                     this.store.baseParams[this.queryParam] = q;
42339                     this.store.load({
42340                         params: this.getParams(q)
42341                     });
42342                     this.expand();
42343                 }
42344             }else{
42345                 this.selectedIndex = -1;
42346                 this.onLoad();   
42347             }
42348         }
42349     },
42350
42351     // private
42352     getParams : function(q){
42353         var p = {};
42354         //p[this.queryParam] = q;
42355         if(this.pageSize){
42356             p.start = 0;
42357             p.limit = this.pageSize;
42358         }
42359         return p;
42360     },
42361
42362     /**
42363      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42364      */
42365     collapse : function(){
42366         if(!this.isExpanded()){
42367             return;
42368         }
42369         this.list.hide();
42370         Roo.get(document).un('mousedown', this.collapseIf, this);
42371         Roo.get(document).un('mousewheel', this.collapseIf, this);
42372         if (!this.editable) {
42373             Roo.get(document).un('keydown', this.listKeyPress, this);
42374         }
42375         this.fireEvent('collapse', this);
42376     },
42377
42378     // private
42379     collapseIf : function(e){
42380         if(!e.within(this.wrap) && !e.within(this.list)){
42381             this.collapse();
42382         }
42383     },
42384
42385     /**
42386      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42387      */
42388     expand : function(){
42389         if(this.isExpanded() || !this.hasFocus){
42390             return;
42391         }
42392         this.list.alignTo(this.el, this.listAlign);
42393         this.list.show();
42394         Roo.get(document).on('mousedown', this.collapseIf, this);
42395         Roo.get(document).on('mousewheel', this.collapseIf, this);
42396         if (!this.editable) {
42397             Roo.get(document).on('keydown', this.listKeyPress, this);
42398         }
42399         
42400         this.fireEvent('expand', this);
42401     },
42402
42403     // private
42404     // Implements the default empty TriggerField.onTriggerClick function
42405     onTriggerClick : function(){
42406         if(this.disabled){
42407             return;
42408         }
42409         if(this.isExpanded()){
42410             this.collapse();
42411             if (!this.blockFocus) {
42412                 this.el.focus();
42413             }
42414             
42415         }else {
42416             this.hasFocus = true;
42417             if(this.triggerAction == 'all') {
42418                 this.doQuery(this.allQuery, true);
42419             } else {
42420                 this.doQuery(this.getRawValue());
42421             }
42422             if (!this.blockFocus) {
42423                 this.el.focus();
42424             }
42425         }
42426     },
42427     listKeyPress : function(e)
42428     {
42429         //Roo.log('listkeypress');
42430         // scroll to first matching element based on key pres..
42431         if (e.isSpecialKey()) {
42432             return false;
42433         }
42434         var k = String.fromCharCode(e.getKey()).toUpperCase();
42435         //Roo.log(k);
42436         var match  = false;
42437         var csel = this.view.getSelectedNodes();
42438         var cselitem = false;
42439         if (csel.length) {
42440             var ix = this.view.indexOf(csel[0]);
42441             cselitem  = this.store.getAt(ix);
42442             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42443                 cselitem = false;
42444             }
42445             
42446         }
42447         
42448         this.store.each(function(v) { 
42449             if (cselitem) {
42450                 // start at existing selection.
42451                 if (cselitem.id == v.id) {
42452                     cselitem = false;
42453                 }
42454                 return;
42455             }
42456                 
42457             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42458                 match = this.store.indexOf(v);
42459                 return false;
42460             }
42461         }, this);
42462         
42463         if (match === false) {
42464             return true; // no more action?
42465         }
42466         // scroll to?
42467         this.view.select(match);
42468         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42469         sn.scrollIntoView(sn.dom.parentNode, false);
42470     } 
42471
42472     /** 
42473     * @cfg {Boolean} grow 
42474     * @hide 
42475     */
42476     /** 
42477     * @cfg {Number} growMin 
42478     * @hide 
42479     */
42480     /** 
42481     * @cfg {Number} growMax 
42482     * @hide 
42483     */
42484     /**
42485      * @hide
42486      * @method autoSize
42487      */
42488 });/*
42489  * Copyright(c) 2010-2012, Roo J Solutions Limited
42490  *
42491  * Licence LGPL
42492  *
42493  */
42494
42495 /**
42496  * @class Roo.form.ComboBoxArray
42497  * @extends Roo.form.TextField
42498  * A facebook style adder... for lists of email / people / countries  etc...
42499  * pick multiple items from a combo box, and shows each one.
42500  *
42501  *  Fred [x]  Brian [x]  [Pick another |v]
42502  *
42503  *
42504  *  For this to work: it needs various extra information
42505  *    - normal combo problay has
42506  *      name, hiddenName
42507  *    + displayField, valueField
42508  *
42509  *    For our purpose...
42510  *
42511  *
42512  *   If we change from 'extends' to wrapping...
42513  *   
42514  *  
42515  *
42516  
42517  
42518  * @constructor
42519  * Create a new ComboBoxArray.
42520  * @param {Object} config Configuration options
42521  */
42522  
42523
42524 Roo.form.ComboBoxArray = function(config)
42525 {
42526     this.addEvents({
42527         /**
42528          * @event beforeremove
42529          * Fires before remove the value from the list
42530              * @param {Roo.form.ComboBoxArray} _self This combo box array
42531              * @param {Roo.form.ComboBoxArray.Item} item removed item
42532              */
42533         'beforeremove' : true,
42534         /**
42535          * @event remove
42536          * Fires when remove the value from the list
42537              * @param {Roo.form.ComboBoxArray} _self This combo box array
42538              * @param {Roo.form.ComboBoxArray.Item} item removed item
42539              */
42540         'remove' : true
42541         
42542         
42543     });
42544     
42545     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42546     
42547     this.items = new Roo.util.MixedCollection(false);
42548     
42549     // construct the child combo...
42550     
42551     
42552     
42553     
42554    
42555     
42556 }
42557
42558  
42559 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42560
42561     /**
42562      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42563      */
42564     
42565     lastData : false,
42566     
42567     // behavies liek a hiddne field
42568     inputType:      'hidden',
42569     /**
42570      * @cfg {Number} width The width of the box that displays the selected element
42571      */ 
42572     width:          300,
42573
42574     
42575     
42576     /**
42577      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42578      */
42579     name : false,
42580     /**
42581      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42582      */
42583     hiddenName : false,
42584       /**
42585      * @cfg {String} seperator    The value seperator normally ',' 
42586      */
42587     seperator : ',',
42588     
42589     // private the array of items that are displayed..
42590     items  : false,
42591     // private - the hidden field el.
42592     hiddenEl : false,
42593     // private - the filed el..
42594     el : false,
42595     
42596     //validateValue : function() { return true; }, // all values are ok!
42597     //onAddClick: function() { },
42598     
42599     onRender : function(ct, position) 
42600     {
42601         
42602         // create the standard hidden element
42603         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42604         
42605         
42606         // give fake names to child combo;
42607         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42608         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42609         
42610         this.combo = Roo.factory(this.combo, Roo.form);
42611         this.combo.onRender(ct, position);
42612         if (typeof(this.combo.width) != 'undefined') {
42613             this.combo.onResize(this.combo.width,0);
42614         }
42615         
42616         this.combo.initEvents();
42617         
42618         // assigned so form know we need to do this..
42619         this.store          = this.combo.store;
42620         this.valueField     = this.combo.valueField;
42621         this.displayField   = this.combo.displayField ;
42622         
42623         
42624         this.combo.wrap.addClass('x-cbarray-grp');
42625         
42626         var cbwrap = this.combo.wrap.createChild(
42627             {tag: 'div', cls: 'x-cbarray-cb'},
42628             this.combo.el.dom
42629         );
42630         
42631              
42632         this.hiddenEl = this.combo.wrap.createChild({
42633             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42634         });
42635         this.el = this.combo.wrap.createChild({
42636             tag: 'input',  type:'hidden' , name: this.name, value : ''
42637         });
42638          //   this.el.dom.removeAttribute("name");
42639         
42640         
42641         this.outerWrap = this.combo.wrap;
42642         this.wrap = cbwrap;
42643         
42644         this.outerWrap.setWidth(this.width);
42645         this.outerWrap.dom.removeChild(this.el.dom);
42646         
42647         this.wrap.dom.appendChild(this.el.dom);
42648         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42649         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42650         
42651         this.combo.trigger.setStyle('position','relative');
42652         this.combo.trigger.setStyle('left', '0px');
42653         this.combo.trigger.setStyle('top', '2px');
42654         
42655         this.combo.el.setStyle('vertical-align', 'text-bottom');
42656         
42657         //this.trigger.setStyle('vertical-align', 'top');
42658         
42659         // this should use the code from combo really... on('add' ....)
42660         if (this.adder) {
42661             
42662         
42663             this.adder = this.outerWrap.createChild(
42664                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42665             var _t = this;
42666             this.adder.on('click', function(e) {
42667                 _t.fireEvent('adderclick', this, e);
42668             }, _t);
42669         }
42670         //var _t = this;
42671         //this.adder.on('click', this.onAddClick, _t);
42672         
42673         
42674         this.combo.on('select', function(cb, rec, ix) {
42675             this.addItem(rec.data);
42676             
42677             cb.setValue('');
42678             cb.el.dom.value = '';
42679             //cb.lastData = rec.data;
42680             // add to list
42681             
42682         }, this);
42683         
42684         
42685     },
42686     
42687     
42688     getName: function()
42689     {
42690         // returns hidden if it's set..
42691         if (!this.rendered) {return ''};
42692         return  this.hiddenName ? this.hiddenName : this.name;
42693         
42694     },
42695     
42696     
42697     onResize: function(w, h){
42698         
42699         return;
42700         // not sure if this is needed..
42701         //this.combo.onResize(w,h);
42702         
42703         if(typeof w != 'number'){
42704             // we do not handle it!?!?
42705             return;
42706         }
42707         var tw = this.combo.trigger.getWidth();
42708         tw += this.addicon ? this.addicon.getWidth() : 0;
42709         tw += this.editicon ? this.editicon.getWidth() : 0;
42710         var x = w - tw;
42711         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42712             
42713         this.combo.trigger.setStyle('left', '0px');
42714         
42715         if(this.list && this.listWidth === undefined){
42716             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42717             this.list.setWidth(lw);
42718             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42719         }
42720         
42721     
42722         
42723     },
42724     
42725     addItem: function(rec)
42726     {
42727         var valueField = this.combo.valueField;
42728         var displayField = this.combo.displayField;
42729         
42730         if (this.items.indexOfKey(rec[valueField]) > -1) {
42731             //console.log("GOT " + rec.data.id);
42732             return;
42733         }
42734         
42735         var x = new Roo.form.ComboBoxArray.Item({
42736             //id : rec[this.idField],
42737             data : rec,
42738             displayField : displayField ,
42739             tipField : displayField ,
42740             cb : this
42741         });
42742         // use the 
42743         this.items.add(rec[valueField],x);
42744         // add it before the element..
42745         this.updateHiddenEl();
42746         x.render(this.outerWrap, this.wrap.dom);
42747         // add the image handler..
42748     },
42749     
42750     updateHiddenEl : function()
42751     {
42752         this.validate();
42753         if (!this.hiddenEl) {
42754             return;
42755         }
42756         var ar = [];
42757         var idField = this.combo.valueField;
42758         
42759         this.items.each(function(f) {
42760             ar.push(f.data[idField]);
42761         });
42762         this.hiddenEl.dom.value = ar.join(this.seperator);
42763         this.validate();
42764     },
42765     
42766     reset : function()
42767     {
42768         this.items.clear();
42769         
42770         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42771            el.remove();
42772         });
42773         
42774         this.el.dom.value = '';
42775         if (this.hiddenEl) {
42776             this.hiddenEl.dom.value = '';
42777         }
42778         
42779     },
42780     getValue: function()
42781     {
42782         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42783     },
42784     setValue: function(v) // not a valid action - must use addItems..
42785     {
42786         
42787         this.reset();
42788          
42789         if (this.store.isLocal && (typeof(v) == 'string')) {
42790             // then we can use the store to find the values..
42791             // comma seperated at present.. this needs to allow JSON based encoding..
42792             this.hiddenEl.value  = v;
42793             var v_ar = [];
42794             Roo.each(v.split(this.seperator), function(k) {
42795                 Roo.log("CHECK " + this.valueField + ',' + k);
42796                 var li = this.store.query(this.valueField, k);
42797                 if (!li.length) {
42798                     return;
42799                 }
42800                 var add = {};
42801                 add[this.valueField] = k;
42802                 add[this.displayField] = li.item(0).data[this.displayField];
42803                 
42804                 this.addItem(add);
42805             }, this) 
42806              
42807         }
42808         if (typeof(v) == 'object' ) {
42809             // then let's assume it's an array of objects..
42810             Roo.each(v, function(l) {
42811                 var add = l;
42812                 if (typeof(l) == 'string') {
42813                     add = {};
42814                     add[this.valueField] = l;
42815                     add[this.displayField] = l
42816                 }
42817                 this.addItem(add);
42818             }, this);
42819              
42820         }
42821         
42822         
42823     },
42824     setFromData: function(v)
42825     {
42826         // this recieves an object, if setValues is called.
42827         this.reset();
42828         this.el.dom.value = v[this.displayField];
42829         this.hiddenEl.dom.value = v[this.valueField];
42830         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42831             return;
42832         }
42833         var kv = v[this.valueField];
42834         var dv = v[this.displayField];
42835         kv = typeof(kv) != 'string' ? '' : kv;
42836         dv = typeof(dv) != 'string' ? '' : dv;
42837         
42838         
42839         var keys = kv.split(this.seperator);
42840         var display = dv.split(this.seperator);
42841         for (var i = 0 ; i < keys.length; i++) {
42842             add = {};
42843             add[this.valueField] = keys[i];
42844             add[this.displayField] = display[i];
42845             this.addItem(add);
42846         }
42847       
42848         
42849     },
42850     
42851     /**
42852      * Validates the combox array value
42853      * @return {Boolean} True if the value is valid, else false
42854      */
42855     validate : function(){
42856         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42857             this.clearInvalid();
42858             return true;
42859         }
42860         return false;
42861     },
42862     
42863     validateValue : function(value){
42864         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42865         
42866     },
42867     
42868     /*@
42869      * overide
42870      * 
42871      */
42872     isDirty : function() {
42873         if(this.disabled) {
42874             return false;
42875         }
42876         
42877         try {
42878             var d = Roo.decode(String(this.originalValue));
42879         } catch (e) {
42880             return String(this.getValue()) !== String(this.originalValue);
42881         }
42882         
42883         var originalValue = [];
42884         
42885         for (var i = 0; i < d.length; i++){
42886             originalValue.push(d[i][this.valueField]);
42887         }
42888         
42889         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42890         
42891     }
42892     
42893 });
42894
42895
42896
42897 /**
42898  * @class Roo.form.ComboBoxArray.Item
42899  * @extends Roo.BoxComponent
42900  * A selected item in the list
42901  *  Fred [x]  Brian [x]  [Pick another |v]
42902  * 
42903  * @constructor
42904  * Create a new item.
42905  * @param {Object} config Configuration options
42906  */
42907  
42908 Roo.form.ComboBoxArray.Item = function(config) {
42909     config.id = Roo.id();
42910     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42911 }
42912
42913 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42914     data : {},
42915     cb: false,
42916     displayField : false,
42917     tipField : false,
42918     
42919     
42920     defaultAutoCreate : {
42921         tag: 'div',
42922         cls: 'x-cbarray-item',
42923         cn : [ 
42924             { tag: 'div' },
42925             {
42926                 tag: 'img',
42927                 width:16,
42928                 height : 16,
42929                 src : Roo.BLANK_IMAGE_URL ,
42930                 align: 'center'
42931             }
42932         ]
42933         
42934     },
42935     
42936  
42937     onRender : function(ct, position)
42938     {
42939         Roo.form.Field.superclass.onRender.call(this, ct, position);
42940         
42941         if(!this.el){
42942             var cfg = this.getAutoCreate();
42943             this.el = ct.createChild(cfg, position);
42944         }
42945         
42946         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42947         
42948         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42949             this.cb.renderer(this.data) :
42950             String.format('{0}',this.data[this.displayField]);
42951         
42952             
42953         this.el.child('div').dom.setAttribute('qtip',
42954                         String.format('{0}',this.data[this.tipField])
42955         );
42956         
42957         this.el.child('img').on('click', this.remove, this);
42958         
42959     },
42960    
42961     remove : function()
42962     {
42963         if(this.cb.disabled){
42964             return;
42965         }
42966         
42967         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42968             this.cb.items.remove(this);
42969             this.el.child('img').un('click', this.remove, this);
42970             this.el.remove();
42971             this.cb.updateHiddenEl();
42972
42973             this.cb.fireEvent('remove', this.cb, this);
42974         }
42975         
42976     }
42977 });/*
42978  * RooJS Library 1.1.1
42979  * Copyright(c) 2008-2011  Alan Knowles
42980  *
42981  * License - LGPL
42982  */
42983  
42984
42985 /**
42986  * @class Roo.form.ComboNested
42987  * @extends Roo.form.ComboBox
42988  * A combobox for that allows selection of nested items in a list,
42989  * eg.
42990  *
42991  *  Book
42992  *    -> red
42993  *    -> green
42994  *  Table
42995  *    -> square
42996  *      ->red
42997  *      ->green
42998  *    -> rectangle
42999  *      ->green
43000  *      
43001  * 
43002  * @constructor
43003  * Create a new ComboNested
43004  * @param {Object} config Configuration options
43005  */
43006 Roo.form.ComboNested = function(config){
43007     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43008     // should verify some data...
43009     // like
43010     // hiddenName = required..
43011     // displayField = required
43012     // valudField == required
43013     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43014     var _t = this;
43015     Roo.each(req, function(e) {
43016         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43017             throw "Roo.form.ComboNested : missing value for: " + e;
43018         }
43019     });
43020      
43021     
43022 };
43023
43024 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43025    
43026     /*
43027      * @config {Number} max Number of columns to show
43028      */
43029     
43030     maxColumns : 3,
43031    
43032     list : null, // the outermost div..
43033     innerLists : null, // the
43034     views : null,
43035     stores : null,
43036     // private
43037     loadingChildren : false,
43038     
43039     onRender : function(ct, position)
43040     {
43041         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43042         
43043         if(this.hiddenName){
43044             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43045                     'before', true);
43046             this.hiddenField.value =
43047                 this.hiddenValue !== undefined ? this.hiddenValue :
43048                 this.value !== undefined ? this.value : '';
43049
43050             // prevent input submission
43051             this.el.dom.removeAttribute('name');
43052              
43053              
43054         }
43055         
43056         if(Roo.isGecko){
43057             this.el.dom.setAttribute('autocomplete', 'off');
43058         }
43059
43060         var cls = 'x-combo-list';
43061
43062         this.list = new Roo.Layer({
43063             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43064         });
43065
43066         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43067         this.list.setWidth(lw);
43068         this.list.swallowEvent('mousewheel');
43069         this.assetHeight = 0;
43070
43071         if(this.title){
43072             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43073             this.assetHeight += this.header.getHeight();
43074         }
43075         this.innerLists = [];
43076         this.views = [];
43077         this.stores = [];
43078         for (var i =0 ; i < this.maxColumns; i++) {
43079             this.onRenderList( cls, i);
43080         }
43081         
43082         // always needs footer, as we are going to have an 'OK' button.
43083         this.footer = this.list.createChild({cls:cls+'-ft'});
43084         this.pageTb = new Roo.Toolbar(this.footer);  
43085         var _this = this;
43086         this.pageTb.add(  {
43087             
43088             text: 'Done',
43089             handler: function()
43090             {
43091                 _this.collapse();
43092             }
43093         });
43094         
43095         if ( this.allowBlank && !this.disableClear) {
43096             
43097             this.pageTb.add(new Roo.Toolbar.Fill(), {
43098                 cls: 'x-btn-icon x-btn-clear',
43099                 text: '&#160;',
43100                 handler: function()
43101                 {
43102                     _this.collapse();
43103                     _this.clearValue();
43104                     _this.onSelect(false, -1);
43105                 }
43106             });
43107         }
43108         if (this.footer) {
43109             this.assetHeight += this.footer.getHeight();
43110         }
43111         
43112     },
43113     onRenderList : function (  cls, i)
43114     {
43115         
43116         var lw = Math.floor(
43117                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43118         );
43119         
43120         this.list.setWidth(lw); // default to '1'
43121
43122         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43123         //il.on('mouseover', this.onViewOver, this, { list:  i });
43124         //il.on('mousemove', this.onViewMove, this, { list:  i });
43125         il.setWidth(lw);
43126         il.setStyle({ 'overflow-x' : 'hidden'});
43127
43128         if(!this.tpl){
43129             this.tpl = new Roo.Template({
43130                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43131                 isEmpty: function (value, allValues) {
43132                     //Roo.log(value);
43133                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43134                     return dl ? 'has-children' : 'no-children'
43135                 }
43136             });
43137         }
43138         
43139         var store  = this.store;
43140         if (i > 0) {
43141             store  = new Roo.data.SimpleStore({
43142                 //fields : this.store.reader.meta.fields,
43143                 reader : this.store.reader,
43144                 data : [ ]
43145             });
43146         }
43147         this.stores[i]  = store;
43148                   
43149         var view = this.views[i] = new Roo.View(
43150             il,
43151             this.tpl,
43152             {
43153                 singleSelect:true,
43154                 store: store,
43155                 selectedClass: this.selectedClass
43156             }
43157         );
43158         view.getEl().setWidth(lw);
43159         view.getEl().setStyle({
43160             position: i < 1 ? 'relative' : 'absolute',
43161             top: 0,
43162             left: (i * lw ) + 'px',
43163             display : i > 0 ? 'none' : 'block'
43164         });
43165         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43166         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43167         //view.on('click', this.onViewClick, this, { list : i });
43168
43169         store.on('beforeload', this.onBeforeLoad, this);
43170         store.on('load',  this.onLoad, this, { list  : i});
43171         store.on('loadexception', this.onLoadException, this);
43172
43173         // hide the other vies..
43174         
43175         
43176         
43177     },
43178       
43179     restrictHeight : function()
43180     {
43181         var mh = 0;
43182         Roo.each(this.innerLists, function(il,i) {
43183             var el = this.views[i].getEl();
43184             el.dom.style.height = '';
43185             var inner = el.dom;
43186             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43187             // only adjust heights on other ones..
43188             mh = Math.max(h, mh);
43189             if (i < 1) {
43190                 
43191                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43192                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43193                
43194             }
43195             
43196             
43197         }, this);
43198         
43199         this.list.beginUpdate();
43200         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43201         this.list.alignTo(this.el, this.listAlign);
43202         this.list.endUpdate();
43203         
43204     },
43205      
43206     
43207     // -- store handlers..
43208     // private
43209     onBeforeLoad : function()
43210     {
43211         if(!this.hasFocus){
43212             return;
43213         }
43214         this.innerLists[0].update(this.loadingText ?
43215                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43216         this.restrictHeight();
43217         this.selectedIndex = -1;
43218     },
43219     // private
43220     onLoad : function(a,b,c,d)
43221     {
43222         if (!this.loadingChildren) {
43223             // then we are loading the top level. - hide the children
43224             for (var i = 1;i < this.views.length; i++) {
43225                 this.views[i].getEl().setStyle({ display : 'none' });
43226             }
43227             var lw = Math.floor(
43228                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43229             );
43230         
43231              this.list.setWidth(lw); // default to '1'
43232
43233             
43234         }
43235         if(!this.hasFocus){
43236             return;
43237         }
43238         
43239         if(this.store.getCount() > 0) {
43240             this.expand();
43241             this.restrictHeight();   
43242         } else {
43243             this.onEmptyResults();
43244         }
43245         
43246         if (!this.loadingChildren) {
43247             this.selectActive();
43248         }
43249         /*
43250         this.stores[1].loadData([]);
43251         this.stores[2].loadData([]);
43252         this.views
43253         */    
43254     
43255         //this.el.focus();
43256     },
43257     
43258     
43259     // private
43260     onLoadException : function()
43261     {
43262         this.collapse();
43263         Roo.log(this.store.reader.jsonData);
43264         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43265             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43266         }
43267         
43268         
43269     },
43270     // no cleaning of leading spaces on blur here.
43271     cleanLeadingSpace : function(e) { },
43272     
43273
43274     onSelectChange : function (view, sels, opts )
43275     {
43276         var ix = view.getSelectedIndexes();
43277          
43278         if (opts.list > this.maxColumns - 2) {
43279             if (view.store.getCount()<  1) {
43280                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43281
43282             } else  {
43283                 if (ix.length) {
43284                     // used to clear ?? but if we are loading unselected 
43285                     this.setFromData(view.store.getAt(ix[0]).data);
43286                 }
43287                 
43288             }
43289             
43290             return;
43291         }
43292         
43293         if (!ix.length) {
43294             // this get's fired when trigger opens..
43295            // this.setFromData({});
43296             var str = this.stores[opts.list+1];
43297             str.data.clear(); // removeall wihtout the fire events..
43298             return;
43299         }
43300         
43301         var rec = view.store.getAt(ix[0]);
43302          
43303         this.setFromData(rec.data);
43304         this.fireEvent('select', this, rec, ix[0]);
43305         
43306         var lw = Math.floor(
43307              (
43308                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43309              ) / this.maxColumns
43310         );
43311         this.loadingChildren = true;
43312         this.stores[opts.list+1].loadDataFromChildren( rec );
43313         this.loadingChildren = false;
43314         var dl = this.stores[opts.list+1]. getTotalCount();
43315         
43316         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43317         
43318         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43319         for (var i = opts.list+2; i < this.views.length;i++) {
43320             this.views[i].getEl().setStyle({ display : 'none' });
43321         }
43322         
43323         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43324         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43325         
43326         if (this.isLoading) {
43327            // this.selectActive(opts.list);
43328         }
43329          
43330     },
43331     
43332     
43333     
43334     
43335     onDoubleClick : function()
43336     {
43337         this.collapse(); //??
43338     },
43339     
43340      
43341     
43342     
43343     
43344     // private
43345     recordToStack : function(store, prop, value, stack)
43346     {
43347         var cstore = new Roo.data.SimpleStore({
43348             //fields : this.store.reader.meta.fields, // we need array reader.. for
43349             reader : this.store.reader,
43350             data : [ ]
43351         });
43352         var _this = this;
43353         var record  = false;
43354         var srec = false;
43355         if(store.getCount() < 1){
43356             return false;
43357         }
43358         store.each(function(r){
43359             if(r.data[prop] == value){
43360                 record = r;
43361             srec = r;
43362                 return false;
43363             }
43364             if (r.data.cn && r.data.cn.length) {
43365                 cstore.loadDataFromChildren( r);
43366                 var cret = _this.recordToStack(cstore, prop, value, stack);
43367                 if (cret !== false) {
43368                     record = cret;
43369                     srec = r;
43370                     return false;
43371                 }
43372             }
43373              
43374             return true;
43375         });
43376         if (record == false) {
43377             return false
43378         }
43379         stack.unshift(srec);
43380         return record;
43381     },
43382     
43383     /*
43384      * find the stack of stores that match our value.
43385      *
43386      * 
43387      */
43388     
43389     selectActive : function ()
43390     {
43391         // if store is not loaded, then we will need to wait for that to happen first.
43392         var stack = [];
43393         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43394         for (var i = 0; i < stack.length; i++ ) {
43395             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43396         }
43397         
43398     }
43399         
43400          
43401     
43402     
43403     
43404     
43405 });/*
43406  * Based on:
43407  * Ext JS Library 1.1.1
43408  * Copyright(c) 2006-2007, Ext JS, LLC.
43409  *
43410  * Originally Released Under LGPL - original licence link has changed is not relivant.
43411  *
43412  * Fork - LGPL
43413  * <script type="text/javascript">
43414  */
43415 /**
43416  * @class Roo.form.Checkbox
43417  * @extends Roo.form.Field
43418  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43419  * @constructor
43420  * Creates a new Checkbox
43421  * @param {Object} config Configuration options
43422  */
43423 Roo.form.Checkbox = function(config){
43424     Roo.form.Checkbox.superclass.constructor.call(this, config);
43425     this.addEvents({
43426         /**
43427          * @event check
43428          * Fires when the checkbox is checked or unchecked.
43429              * @param {Roo.form.Checkbox} this This checkbox
43430              * @param {Boolean} checked The new checked value
43431              */
43432         check : true
43433     });
43434 };
43435
43436 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43437     /**
43438      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43439      */
43440     focusClass : undefined,
43441     /**
43442      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43443      */
43444     fieldClass: "x-form-field",
43445     /**
43446      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43447      */
43448     checked: false,
43449     /**
43450      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43451      * {tag: "input", type: "checkbox", autocomplete: "off"})
43452      */
43453     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43454     /**
43455      * @cfg {String} boxLabel The text that appears beside the checkbox
43456      */
43457     boxLabel : "",
43458     /**
43459      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43460      */  
43461     inputValue : '1',
43462     /**
43463      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43464      */
43465      valueOff: '0', // value when not checked..
43466
43467     actionMode : 'viewEl', 
43468     //
43469     // private
43470     itemCls : 'x-menu-check-item x-form-item',
43471     groupClass : 'x-menu-group-item',
43472     inputType : 'hidden',
43473     
43474     
43475     inSetChecked: false, // check that we are not calling self...
43476     
43477     inputElement: false, // real input element?
43478     basedOn: false, // ????
43479     
43480     isFormField: true, // not sure where this is needed!!!!
43481
43482     onResize : function(){
43483         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43484         if(!this.boxLabel){
43485             this.el.alignTo(this.wrap, 'c-c');
43486         }
43487     },
43488
43489     initEvents : function(){
43490         Roo.form.Checkbox.superclass.initEvents.call(this);
43491         this.el.on("click", this.onClick,  this);
43492         this.el.on("change", this.onClick,  this);
43493     },
43494
43495
43496     getResizeEl : function(){
43497         return this.wrap;
43498     },
43499
43500     getPositionEl : function(){
43501         return this.wrap;
43502     },
43503
43504     // private
43505     onRender : function(ct, position){
43506         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43507         /*
43508         if(this.inputValue !== undefined){
43509             this.el.dom.value = this.inputValue;
43510         }
43511         */
43512         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43513         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43514         var viewEl = this.wrap.createChild({ 
43515             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43516         this.viewEl = viewEl;   
43517         this.wrap.on('click', this.onClick,  this); 
43518         
43519         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43520         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43521         
43522         
43523         
43524         if(this.boxLabel){
43525             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43526         //    viewEl.on('click', this.onClick,  this); 
43527         }
43528         //if(this.checked){
43529             this.setChecked(this.checked);
43530         //}else{
43531             //this.checked = this.el.dom;
43532         //}
43533
43534     },
43535
43536     // private
43537     initValue : Roo.emptyFn,
43538
43539     /**
43540      * Returns the checked state of the checkbox.
43541      * @return {Boolean} True if checked, else false
43542      */
43543     getValue : function(){
43544         if(this.el){
43545             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43546         }
43547         return this.valueOff;
43548         
43549     },
43550
43551         // private
43552     onClick : function(){ 
43553         if (this.disabled) {
43554             return;
43555         }
43556         this.setChecked(!this.checked);
43557
43558         //if(this.el.dom.checked != this.checked){
43559         //    this.setValue(this.el.dom.checked);
43560        // }
43561     },
43562
43563     /**
43564      * Sets the checked state of the checkbox.
43565      * On is always based on a string comparison between inputValue and the param.
43566      * @param {Boolean/String} value - the value to set 
43567      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43568      */
43569     setValue : function(v,suppressEvent){
43570         
43571         
43572         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43573         //if(this.el && this.el.dom){
43574         //    this.el.dom.checked = this.checked;
43575         //    this.el.dom.defaultChecked = this.checked;
43576         //}
43577         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43578         //this.fireEvent("check", this, this.checked);
43579     },
43580     // private..
43581     setChecked : function(state,suppressEvent)
43582     {
43583         if (this.inSetChecked) {
43584             this.checked = state;
43585             return;
43586         }
43587         
43588     
43589         if(this.wrap){
43590             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43591         }
43592         this.checked = state;
43593         if(suppressEvent !== true){
43594             this.fireEvent('check', this, state);
43595         }
43596         this.inSetChecked = true;
43597         this.el.dom.value = state ? this.inputValue : this.valueOff;
43598         this.inSetChecked = false;
43599         
43600     },
43601     // handle setting of hidden value by some other method!!?!?
43602     setFromHidden: function()
43603     {
43604         if(!this.el){
43605             return;
43606         }
43607         //console.log("SET FROM HIDDEN");
43608         //alert('setFrom hidden');
43609         this.setValue(this.el.dom.value);
43610     },
43611     
43612     onDestroy : function()
43613     {
43614         if(this.viewEl){
43615             Roo.get(this.viewEl).remove();
43616         }
43617          
43618         Roo.form.Checkbox.superclass.onDestroy.call(this);
43619     },
43620     
43621     setBoxLabel : function(str)
43622     {
43623         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43624     }
43625
43626 });/*
43627  * Based on:
43628  * Ext JS Library 1.1.1
43629  * Copyright(c) 2006-2007, Ext JS, LLC.
43630  *
43631  * Originally Released Under LGPL - original licence link has changed is not relivant.
43632  *
43633  * Fork - LGPL
43634  * <script type="text/javascript">
43635  */
43636  
43637 /**
43638  * @class Roo.form.Radio
43639  * @extends Roo.form.Checkbox
43640  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43641  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43642  * @constructor
43643  * Creates a new Radio
43644  * @param {Object} config Configuration options
43645  */
43646 Roo.form.Radio = function(){
43647     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43648 };
43649 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43650     inputType: 'radio',
43651
43652     /**
43653      * If this radio is part of a group, it will return the selected value
43654      * @return {String}
43655      */
43656     getGroupValue : function(){
43657         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43658     },
43659     
43660     
43661     onRender : function(ct, position){
43662         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43663         
43664         if(this.inputValue !== undefined){
43665             this.el.dom.value = this.inputValue;
43666         }
43667          
43668         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43669         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43670         //var viewEl = this.wrap.createChild({ 
43671         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43672         //this.viewEl = viewEl;   
43673         //this.wrap.on('click', this.onClick,  this); 
43674         
43675         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43676         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43677         
43678         
43679         
43680         if(this.boxLabel){
43681             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43682         //    viewEl.on('click', this.onClick,  this); 
43683         }
43684          if(this.checked){
43685             this.el.dom.checked =   'checked' ;
43686         }
43687          
43688     } 
43689     
43690     
43691 });//<script type="text/javascript">
43692
43693 /*
43694  * Based  Ext JS Library 1.1.1
43695  * Copyright(c) 2006-2007, Ext JS, LLC.
43696  * LGPL
43697  *
43698  */
43699  
43700 /**
43701  * @class Roo.HtmlEditorCore
43702  * @extends Roo.Component
43703  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43704  *
43705  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43706  */
43707
43708 Roo.HtmlEditorCore = function(config){
43709     
43710     
43711     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43712     
43713     
43714     this.addEvents({
43715         /**
43716          * @event initialize
43717          * Fires when the editor is fully initialized (including the iframe)
43718          * @param {Roo.HtmlEditorCore} this
43719          */
43720         initialize: true,
43721         /**
43722          * @event activate
43723          * Fires when the editor is first receives the focus. Any insertion must wait
43724          * until after this event.
43725          * @param {Roo.HtmlEditorCore} this
43726          */
43727         activate: true,
43728          /**
43729          * @event beforesync
43730          * Fires before the textarea is updated with content from the editor iframe. Return false
43731          * to cancel the sync.
43732          * @param {Roo.HtmlEditorCore} this
43733          * @param {String} html
43734          */
43735         beforesync: true,
43736          /**
43737          * @event beforepush
43738          * Fires before the iframe editor is updated with content from the textarea. Return false
43739          * to cancel the push.
43740          * @param {Roo.HtmlEditorCore} this
43741          * @param {String} html
43742          */
43743         beforepush: true,
43744          /**
43745          * @event sync
43746          * Fires when the textarea is updated with content from the editor iframe.
43747          * @param {Roo.HtmlEditorCore} this
43748          * @param {String} html
43749          */
43750         sync: true,
43751          /**
43752          * @event push
43753          * Fires when the iframe editor is updated with content from the textarea.
43754          * @param {Roo.HtmlEditorCore} this
43755          * @param {String} html
43756          */
43757         push: true,
43758         
43759         /**
43760          * @event editorevent
43761          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43762          * @param {Roo.HtmlEditorCore} this
43763          */
43764         editorevent: true
43765         
43766     });
43767     
43768     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43769     
43770     // defaults : white / black...
43771     this.applyBlacklists();
43772     
43773     
43774     
43775 };
43776
43777
43778 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43779
43780
43781      /**
43782      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43783      */
43784     
43785     owner : false,
43786     
43787      /**
43788      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43789      *                        Roo.resizable.
43790      */
43791     resizable : false,
43792      /**
43793      * @cfg {Number} height (in pixels)
43794      */   
43795     height: 300,
43796    /**
43797      * @cfg {Number} width (in pixels)
43798      */   
43799     width: 500,
43800     
43801     /**
43802      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43803      * 
43804      */
43805     stylesheets: false,
43806     
43807     /**
43808      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
43809      */
43810     allowComments: false,
43811     // id of frame..
43812     frameId: false,
43813     
43814     // private properties
43815     validationEvent : false,
43816     deferHeight: true,
43817     initialized : false,
43818     activated : false,
43819     sourceEditMode : false,
43820     onFocus : Roo.emptyFn,
43821     iframePad:3,
43822     hideMode:'offsets',
43823     
43824     clearUp: true,
43825     
43826     // blacklist + whitelisted elements..
43827     black: false,
43828     white: false,
43829      
43830     bodyCls : '',
43831
43832     /**
43833      * Protected method that will not generally be called directly. It
43834      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43835      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43836      */
43837     getDocMarkup : function(){
43838         // body styles..
43839         var st = '';
43840         
43841         // inherit styels from page...?? 
43842         if (this.stylesheets === false) {
43843             
43844             Roo.get(document.head).select('style').each(function(node) {
43845                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43846             });
43847             
43848             Roo.get(document.head).select('link').each(function(node) { 
43849                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43850             });
43851             
43852         } else if (!this.stylesheets.length) {
43853                 // simple..
43854                 st = '<style type="text/css">' +
43855                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43856                    '</style>';
43857         } else {
43858             for (var i in this.stylesheets) { 
43859                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43860             }
43861             
43862         }
43863         
43864         st +=  '<style type="text/css">' +
43865             'IMG { cursor: pointer } ' +
43866         '</style>';
43867
43868         var cls = 'roo-htmleditor-body';
43869         
43870         if(this.bodyCls.length){
43871             cls += ' ' + this.bodyCls;
43872         }
43873         
43874         return '<html><head>' + st  +
43875             //<style type="text/css">' +
43876             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43877             //'</style>' +
43878             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43879     },
43880
43881     // private
43882     onRender : function(ct, position)
43883     {
43884         var _t = this;
43885         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43886         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43887         
43888         
43889         this.el.dom.style.border = '0 none';
43890         this.el.dom.setAttribute('tabIndex', -1);
43891         this.el.addClass('x-hidden hide');
43892         
43893         
43894         
43895         if(Roo.isIE){ // fix IE 1px bogus margin
43896             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43897         }
43898        
43899         
43900         this.frameId = Roo.id();
43901         
43902          
43903         
43904         var iframe = this.owner.wrap.createChild({
43905             tag: 'iframe',
43906             cls: 'form-control', // bootstrap..
43907             id: this.frameId,
43908             name: this.frameId,
43909             frameBorder : 'no',
43910             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43911         }, this.el
43912         );
43913         
43914         
43915         this.iframe = iframe.dom;
43916
43917          this.assignDocWin();
43918         
43919         this.doc.designMode = 'on';
43920        
43921         this.doc.open();
43922         this.doc.write(this.getDocMarkup());
43923         this.doc.close();
43924
43925         
43926         var task = { // must defer to wait for browser to be ready
43927             run : function(){
43928                 //console.log("run task?" + this.doc.readyState);
43929                 this.assignDocWin();
43930                 if(this.doc.body || this.doc.readyState == 'complete'){
43931                     try {
43932                         this.doc.designMode="on";
43933                     } catch (e) {
43934                         return;
43935                     }
43936                     Roo.TaskMgr.stop(task);
43937                     this.initEditor.defer(10, this);
43938                 }
43939             },
43940             interval : 10,
43941             duration: 10000,
43942             scope: this
43943         };
43944         Roo.TaskMgr.start(task);
43945
43946     },
43947
43948     // private
43949     onResize : function(w, h)
43950     {
43951          Roo.log('resize: ' +w + ',' + h );
43952         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43953         if(!this.iframe){
43954             return;
43955         }
43956         if(typeof w == 'number'){
43957             
43958             this.iframe.style.width = w + 'px';
43959         }
43960         if(typeof h == 'number'){
43961             
43962             this.iframe.style.height = h + 'px';
43963             if(this.doc){
43964                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43965             }
43966         }
43967         
43968     },
43969
43970     /**
43971      * Toggles the editor between standard and source edit mode.
43972      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43973      */
43974     toggleSourceEdit : function(sourceEditMode){
43975         
43976         this.sourceEditMode = sourceEditMode === true;
43977         
43978         if(this.sourceEditMode){
43979  
43980             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43981             
43982         }else{
43983             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43984             //this.iframe.className = '';
43985             this.deferFocus();
43986         }
43987         //this.setSize(this.owner.wrap.getSize());
43988         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43989     },
43990
43991     
43992   
43993
43994     /**
43995      * Protected method that will not generally be called directly. If you need/want
43996      * custom HTML cleanup, this is the method you should override.
43997      * @param {String} html The HTML to be cleaned
43998      * return {String} The cleaned HTML
43999      */
44000     cleanHtml : function(html){
44001         html = String(html);
44002         if(html.length > 5){
44003             if(Roo.isSafari){ // strip safari nonsense
44004                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44005             }
44006         }
44007         if(html == '&nbsp;'){
44008             html = '';
44009         }
44010         return html;
44011     },
44012
44013     /**
44014      * HTML Editor -> Textarea
44015      * Protected method that will not generally be called directly. Syncs the contents
44016      * of the editor iframe with the textarea.
44017      */
44018     syncValue : function(){
44019         if(this.initialized){
44020             var bd = (this.doc.body || this.doc.documentElement);
44021             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44022             var html = bd.innerHTML;
44023             if(Roo.isSafari){
44024                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44025                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44026                 if(m && m[1]){
44027                     html = '<div style="'+m[0]+'">' + html + '</div>';
44028                 }
44029             }
44030             html = this.cleanHtml(html);
44031             // fix up the special chars.. normaly like back quotes in word...
44032             // however we do not want to do this with chinese..
44033             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44034                 
44035                 var cc = match.charCodeAt();
44036
44037                 // Get the character value, handling surrogate pairs
44038                 if (match.length == 2) {
44039                     // It's a surrogate pair, calculate the Unicode code point
44040                     var high = match.charCodeAt(0) - 0xD800;
44041                     var low  = match.charCodeAt(1) - 0xDC00;
44042                     cc = (high * 0x400) + low + 0x10000;
44043                 }  else if (
44044                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44045                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44046                     (cc >= 0xf900 && cc < 0xfb00 )
44047                 ) {
44048                         return match;
44049                 }  
44050          
44051                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44052                 return "&#" + cc + ";";
44053                 
44054                 
44055             });
44056             
44057             
44058              
44059             if(this.owner.fireEvent('beforesync', this, html) !== false){
44060                 this.el.dom.value = html;
44061                 this.owner.fireEvent('sync', this, html);
44062             }
44063         }
44064     },
44065
44066     /**
44067      * Protected method that will not generally be called directly. Pushes the value of the textarea
44068      * into the iframe editor.
44069      */
44070     pushValue : function(){
44071         if(this.initialized){
44072             var v = this.el.dom.value.trim();
44073             
44074 //            if(v.length < 1){
44075 //                v = '&#160;';
44076 //            }
44077             
44078             if(this.owner.fireEvent('beforepush', this, v) !== false){
44079                 var d = (this.doc.body || this.doc.documentElement);
44080                 d.innerHTML = v;
44081                 this.cleanUpPaste();
44082                 this.el.dom.value = d.innerHTML;
44083                 this.owner.fireEvent('push', this, v);
44084             }
44085         }
44086     },
44087
44088     // private
44089     deferFocus : function(){
44090         this.focus.defer(10, this);
44091     },
44092
44093     // doc'ed in Field
44094     focus : function(){
44095         if(this.win && !this.sourceEditMode){
44096             this.win.focus();
44097         }else{
44098             this.el.focus();
44099         }
44100     },
44101     
44102     assignDocWin: function()
44103     {
44104         var iframe = this.iframe;
44105         
44106          if(Roo.isIE){
44107             this.doc = iframe.contentWindow.document;
44108             this.win = iframe.contentWindow;
44109         } else {
44110 //            if (!Roo.get(this.frameId)) {
44111 //                return;
44112 //            }
44113 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44114 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44115             
44116             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44117                 return;
44118             }
44119             
44120             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44121             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44122         }
44123     },
44124     
44125     // private
44126     initEditor : function(){
44127         //console.log("INIT EDITOR");
44128         this.assignDocWin();
44129         
44130         
44131         
44132         this.doc.designMode="on";
44133         this.doc.open();
44134         this.doc.write(this.getDocMarkup());
44135         this.doc.close();
44136         
44137         var dbody = (this.doc.body || this.doc.documentElement);
44138         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44139         // this copies styles from the containing element into thsi one..
44140         // not sure why we need all of this..
44141         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44142         
44143         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44144         //ss['background-attachment'] = 'fixed'; // w3c
44145         dbody.bgProperties = 'fixed'; // ie
44146         //Roo.DomHelper.applyStyles(dbody, ss);
44147         Roo.EventManager.on(this.doc, {
44148             //'mousedown': this.onEditorEvent,
44149             'mouseup': this.onEditorEvent,
44150             'dblclick': this.onEditorEvent,
44151             'click': this.onEditorEvent,
44152             'keyup': this.onEditorEvent,
44153             buffer:100,
44154             scope: this
44155         });
44156         if(Roo.isGecko){
44157             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44158         }
44159         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44160             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44161         }
44162         this.initialized = true;
44163
44164         this.owner.fireEvent('initialize', this);
44165         this.pushValue();
44166     },
44167
44168     // private
44169     onDestroy : function(){
44170         
44171         
44172         
44173         if(this.rendered){
44174             
44175             //for (var i =0; i < this.toolbars.length;i++) {
44176             //    // fixme - ask toolbars for heights?
44177             //    this.toolbars[i].onDestroy();
44178            // }
44179             
44180             //this.wrap.dom.innerHTML = '';
44181             //this.wrap.remove();
44182         }
44183     },
44184
44185     // private
44186     onFirstFocus : function(){
44187         
44188         this.assignDocWin();
44189         
44190         
44191         this.activated = true;
44192          
44193     
44194         if(Roo.isGecko){ // prevent silly gecko errors
44195             this.win.focus();
44196             var s = this.win.getSelection();
44197             if(!s.focusNode || s.focusNode.nodeType != 3){
44198                 var r = s.getRangeAt(0);
44199                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44200                 r.collapse(true);
44201                 this.deferFocus();
44202             }
44203             try{
44204                 this.execCmd('useCSS', true);
44205                 this.execCmd('styleWithCSS', false);
44206             }catch(e){}
44207         }
44208         this.owner.fireEvent('activate', this);
44209     },
44210
44211     // private
44212     adjustFont: function(btn){
44213         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44214         //if(Roo.isSafari){ // safari
44215         //    adjust *= 2;
44216        // }
44217         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44218         if(Roo.isSafari){ // safari
44219             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44220             v =  (v < 10) ? 10 : v;
44221             v =  (v > 48) ? 48 : v;
44222             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44223             
44224         }
44225         
44226         
44227         v = Math.max(1, v+adjust);
44228         
44229         this.execCmd('FontSize', v  );
44230     },
44231
44232     onEditorEvent : function(e)
44233     {
44234         this.owner.fireEvent('editorevent', this, e);
44235       //  this.updateToolbar();
44236         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44237     },
44238
44239     insertTag : function(tg)
44240     {
44241         // could be a bit smarter... -> wrap the current selected tRoo..
44242         if (tg.toLowerCase() == 'span' ||
44243             tg.toLowerCase() == 'code' ||
44244             tg.toLowerCase() == 'sup' ||
44245             tg.toLowerCase() == 'sub' 
44246             ) {
44247             
44248             range = this.createRange(this.getSelection());
44249             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44250             wrappingNode.appendChild(range.extractContents());
44251             range.insertNode(wrappingNode);
44252
44253             return;
44254             
44255             
44256             
44257         }
44258         this.execCmd("formatblock",   tg);
44259         
44260     },
44261     
44262     insertText : function(txt)
44263     {
44264         
44265         
44266         var range = this.createRange();
44267         range.deleteContents();
44268                //alert(Sender.getAttribute('label'));
44269                
44270         range.insertNode(this.doc.createTextNode(txt));
44271     } ,
44272     
44273      
44274
44275     /**
44276      * Executes a Midas editor command on the editor document and performs necessary focus and
44277      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44278      * @param {String} cmd The Midas command
44279      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44280      */
44281     relayCmd : function(cmd, value){
44282         this.win.focus();
44283         this.execCmd(cmd, value);
44284         this.owner.fireEvent('editorevent', this);
44285         //this.updateToolbar();
44286         this.owner.deferFocus();
44287     },
44288
44289     /**
44290      * Executes a Midas editor command directly on the editor document.
44291      * For visual commands, you should use {@link #relayCmd} instead.
44292      * <b>This should only be called after the editor is initialized.</b>
44293      * @param {String} cmd The Midas command
44294      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44295      */
44296     execCmd : function(cmd, value){
44297         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44298         this.syncValue();
44299     },
44300  
44301  
44302    
44303     /**
44304      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44305      * to insert tRoo.
44306      * @param {String} text | dom node.. 
44307      */
44308     insertAtCursor : function(text)
44309     {
44310         
44311         if(!this.activated){
44312             return;
44313         }
44314         /*
44315         if(Roo.isIE){
44316             this.win.focus();
44317             var r = this.doc.selection.createRange();
44318             if(r){
44319                 r.collapse(true);
44320                 r.pasteHTML(text);
44321                 this.syncValue();
44322                 this.deferFocus();
44323             
44324             }
44325             return;
44326         }
44327         */
44328         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44329             this.win.focus();
44330             
44331             
44332             // from jquery ui (MIT licenced)
44333             var range, node;
44334             var win = this.win;
44335             
44336             if (win.getSelection && win.getSelection().getRangeAt) {
44337                 range = win.getSelection().getRangeAt(0);
44338                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44339                 range.insertNode(node);
44340             } else if (win.document.selection && win.document.selection.createRange) {
44341                 // no firefox support
44342                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44343                 win.document.selection.createRange().pasteHTML(txt);
44344             } else {
44345                 // no firefox support
44346                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44347                 this.execCmd('InsertHTML', txt);
44348             } 
44349             
44350             this.syncValue();
44351             
44352             this.deferFocus();
44353         }
44354     },
44355  // private
44356     mozKeyPress : function(e){
44357         if(e.ctrlKey){
44358             var c = e.getCharCode(), cmd;
44359           
44360             if(c > 0){
44361                 c = String.fromCharCode(c).toLowerCase();
44362                 switch(c){
44363                     case 'b':
44364                         cmd = 'bold';
44365                         break;
44366                     case 'i':
44367                         cmd = 'italic';
44368                         break;
44369                     
44370                     case 'u':
44371                         cmd = 'underline';
44372                         break;
44373                     
44374                     case 'v':
44375                         this.cleanUpPaste.defer(100, this);
44376                         return;
44377                         
44378                 }
44379                 if(cmd){
44380                     this.win.focus();
44381                     this.execCmd(cmd);
44382                     this.deferFocus();
44383                     e.preventDefault();
44384                 }
44385                 
44386             }
44387         }
44388     },
44389
44390     // private
44391     fixKeys : function(){ // load time branching for fastest keydown performance
44392         if(Roo.isIE){
44393             return function(e){
44394                 var k = e.getKey(), r;
44395                 if(k == e.TAB){
44396                     e.stopEvent();
44397                     r = this.doc.selection.createRange();
44398                     if(r){
44399                         r.collapse(true);
44400                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44401                         this.deferFocus();
44402                     }
44403                     return;
44404                 }
44405                 
44406                 if(k == e.ENTER){
44407                     r = this.doc.selection.createRange();
44408                     if(r){
44409                         var target = r.parentElement();
44410                         if(!target || target.tagName.toLowerCase() != 'li'){
44411                             e.stopEvent();
44412                             r.pasteHTML('<br />');
44413                             r.collapse(false);
44414                             r.select();
44415                         }
44416                     }
44417                 }
44418                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44419                     this.cleanUpPaste.defer(100, this);
44420                     return;
44421                 }
44422                 
44423                 
44424             };
44425         }else if(Roo.isOpera){
44426             return function(e){
44427                 var k = e.getKey();
44428                 if(k == e.TAB){
44429                     e.stopEvent();
44430                     this.win.focus();
44431                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44432                     this.deferFocus();
44433                 }
44434                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44435                     this.cleanUpPaste.defer(100, this);
44436                     return;
44437                 }
44438                 
44439             };
44440         }else if(Roo.isSafari){
44441             return function(e){
44442                 var k = e.getKey();
44443                 
44444                 if(k == e.TAB){
44445                     e.stopEvent();
44446                     this.execCmd('InsertText','\t');
44447                     this.deferFocus();
44448                     return;
44449                 }
44450                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44451                     this.cleanUpPaste.defer(100, this);
44452                     return;
44453                 }
44454                 
44455              };
44456         }
44457     }(),
44458     
44459     getAllAncestors: function()
44460     {
44461         var p = this.getSelectedNode();
44462         var a = [];
44463         if (!p) {
44464             a.push(p); // push blank onto stack..
44465             p = this.getParentElement();
44466         }
44467         
44468         
44469         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44470             a.push(p);
44471             p = p.parentNode;
44472         }
44473         a.push(this.doc.body);
44474         return a;
44475     },
44476     lastSel : false,
44477     lastSelNode : false,
44478     
44479     
44480     getSelection : function() 
44481     {
44482         this.assignDocWin();
44483         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44484     },
44485     
44486     getSelectedNode: function() 
44487     {
44488         // this may only work on Gecko!!!
44489         
44490         // should we cache this!!!!
44491         
44492         
44493         
44494          
44495         var range = this.createRange(this.getSelection()).cloneRange();
44496         
44497         if (Roo.isIE) {
44498             var parent = range.parentElement();
44499             while (true) {
44500                 var testRange = range.duplicate();
44501                 testRange.moveToElementText(parent);
44502                 if (testRange.inRange(range)) {
44503                     break;
44504                 }
44505                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44506                     break;
44507                 }
44508                 parent = parent.parentElement;
44509             }
44510             return parent;
44511         }
44512         
44513         // is ancestor a text element.
44514         var ac =  range.commonAncestorContainer;
44515         if (ac.nodeType == 3) {
44516             ac = ac.parentNode;
44517         }
44518         
44519         var ar = ac.childNodes;
44520          
44521         var nodes = [];
44522         var other_nodes = [];
44523         var has_other_nodes = false;
44524         for (var i=0;i<ar.length;i++) {
44525             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44526                 continue;
44527             }
44528             // fullly contained node.
44529             
44530             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44531                 nodes.push(ar[i]);
44532                 continue;
44533             }
44534             
44535             // probably selected..
44536             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44537                 other_nodes.push(ar[i]);
44538                 continue;
44539             }
44540             // outer..
44541             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44542                 continue;
44543             }
44544             
44545             
44546             has_other_nodes = true;
44547         }
44548         if (!nodes.length && other_nodes.length) {
44549             nodes= other_nodes;
44550         }
44551         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44552             return false;
44553         }
44554         
44555         return nodes[0];
44556     },
44557     createRange: function(sel)
44558     {
44559         // this has strange effects when using with 
44560         // top toolbar - not sure if it's a great idea.
44561         //this.editor.contentWindow.focus();
44562         if (typeof sel != "undefined") {
44563             try {
44564                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44565             } catch(e) {
44566                 return this.doc.createRange();
44567             }
44568         } else {
44569             return this.doc.createRange();
44570         }
44571     },
44572     getParentElement: function()
44573     {
44574         
44575         this.assignDocWin();
44576         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44577         
44578         var range = this.createRange(sel);
44579          
44580         try {
44581             var p = range.commonAncestorContainer;
44582             while (p.nodeType == 3) { // text node
44583                 p = p.parentNode;
44584             }
44585             return p;
44586         } catch (e) {
44587             return null;
44588         }
44589     
44590     },
44591     /***
44592      *
44593      * Range intersection.. the hard stuff...
44594      *  '-1' = before
44595      *  '0' = hits..
44596      *  '1' = after.
44597      *         [ -- selected range --- ]
44598      *   [fail]                        [fail]
44599      *
44600      *    basically..
44601      *      if end is before start or  hits it. fail.
44602      *      if start is after end or hits it fail.
44603      *
44604      *   if either hits (but other is outside. - then it's not 
44605      *   
44606      *    
44607      **/
44608     
44609     
44610     // @see http://www.thismuchiknow.co.uk/?p=64.
44611     rangeIntersectsNode : function(range, node)
44612     {
44613         var nodeRange = node.ownerDocument.createRange();
44614         try {
44615             nodeRange.selectNode(node);
44616         } catch (e) {
44617             nodeRange.selectNodeContents(node);
44618         }
44619     
44620         var rangeStartRange = range.cloneRange();
44621         rangeStartRange.collapse(true);
44622     
44623         var rangeEndRange = range.cloneRange();
44624         rangeEndRange.collapse(false);
44625     
44626         var nodeStartRange = nodeRange.cloneRange();
44627         nodeStartRange.collapse(true);
44628     
44629         var nodeEndRange = nodeRange.cloneRange();
44630         nodeEndRange.collapse(false);
44631     
44632         return rangeStartRange.compareBoundaryPoints(
44633                  Range.START_TO_START, nodeEndRange) == -1 &&
44634                rangeEndRange.compareBoundaryPoints(
44635                  Range.START_TO_START, nodeStartRange) == 1;
44636         
44637          
44638     },
44639     rangeCompareNode : function(range, node)
44640     {
44641         var nodeRange = node.ownerDocument.createRange();
44642         try {
44643             nodeRange.selectNode(node);
44644         } catch (e) {
44645             nodeRange.selectNodeContents(node);
44646         }
44647         
44648         
44649         range.collapse(true);
44650     
44651         nodeRange.collapse(true);
44652      
44653         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44654         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44655          
44656         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44657         
44658         var nodeIsBefore   =  ss == 1;
44659         var nodeIsAfter    = ee == -1;
44660         
44661         if (nodeIsBefore && nodeIsAfter) {
44662             return 0; // outer
44663         }
44664         if (!nodeIsBefore && nodeIsAfter) {
44665             return 1; //right trailed.
44666         }
44667         
44668         if (nodeIsBefore && !nodeIsAfter) {
44669             return 2;  // left trailed.
44670         }
44671         // fully contined.
44672         return 3;
44673     },
44674
44675     // private? - in a new class?
44676     cleanUpPaste :  function()
44677     {
44678         // cleans up the whole document..
44679         Roo.log('cleanuppaste');
44680         
44681         this.cleanUpChildren(this.doc.body);
44682         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44683         if (clean != this.doc.body.innerHTML) {
44684             this.doc.body.innerHTML = clean;
44685         }
44686         
44687     },
44688     
44689     cleanWordChars : function(input) {// change the chars to hex code
44690         var he = Roo.HtmlEditorCore;
44691         
44692         var output = input;
44693         Roo.each(he.swapCodes, function(sw) { 
44694             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44695             
44696             output = output.replace(swapper, sw[1]);
44697         });
44698         
44699         return output;
44700     },
44701     
44702     
44703     cleanUpChildren : function (n)
44704     {
44705         if (!n.childNodes.length) {
44706             return;
44707         }
44708         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44709            this.cleanUpChild(n.childNodes[i]);
44710         }
44711     },
44712     
44713     
44714         
44715     
44716     cleanUpChild : function (node)
44717     {
44718         var ed = this;
44719         //console.log(node);
44720         if (node.nodeName == "#text") {
44721             // clean up silly Windows -- stuff?
44722             return; 
44723         }
44724         if (node.nodeName == "#comment") {
44725             if (!this.allowComments) {
44726                 node.parentNode.removeChild(node);
44727             }
44728             // clean up silly Windows -- stuff?
44729             return; 
44730         }
44731         var lcname = node.tagName.toLowerCase();
44732         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44733         // whitelist of tags..
44734         
44735         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44736             // remove node.
44737             node.parentNode.removeChild(node);
44738             return;
44739             
44740         }
44741         
44742         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44743         
44744         // spans with no attributes - just remove them..
44745         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44746             remove_keep_children = true;
44747         }
44748         
44749         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44750         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44751         
44752         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44753         //    remove_keep_children = true;
44754         //}
44755         
44756         if (remove_keep_children) {
44757             this.cleanUpChildren(node);
44758             // inserts everything just before this node...
44759             while (node.childNodes.length) {
44760                 var cn = node.childNodes[0];
44761                 node.removeChild(cn);
44762                 node.parentNode.insertBefore(cn, node);
44763             }
44764             node.parentNode.removeChild(node);
44765             return;
44766         }
44767         
44768         if (!node.attributes || !node.attributes.length) {
44769             
44770           
44771             
44772             
44773             this.cleanUpChildren(node);
44774             return;
44775         }
44776         
44777         function cleanAttr(n,v)
44778         {
44779             
44780             if (v.match(/^\./) || v.match(/^\//)) {
44781                 return;
44782             }
44783             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44784                 return;
44785             }
44786             if (v.match(/^#/)) {
44787                 return;
44788             }
44789             if (v.match(/^\{/)) { // allow template editing.
44790                 return;
44791             }
44792 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44793             node.removeAttribute(n);
44794             
44795         }
44796         
44797         var cwhite = this.cwhite;
44798         var cblack = this.cblack;
44799             
44800         function cleanStyle(n,v)
44801         {
44802             if (v.match(/expression/)) { //XSS?? should we even bother..
44803                 node.removeAttribute(n);
44804                 return;
44805             }
44806             
44807             var parts = v.split(/;/);
44808             var clean = [];
44809             
44810             Roo.each(parts, function(p) {
44811                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44812                 if (!p.length) {
44813                     return true;
44814                 }
44815                 var l = p.split(':').shift().replace(/\s+/g,'');
44816                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44817                 
44818                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44819 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44820                     //node.removeAttribute(n);
44821                     return true;
44822                 }
44823                 //Roo.log()
44824                 // only allow 'c whitelisted system attributes'
44825                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44826 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44827                     //node.removeAttribute(n);
44828                     return true;
44829                 }
44830                 
44831                 
44832                  
44833                 
44834                 clean.push(p);
44835                 return true;
44836             });
44837             if (clean.length) { 
44838                 node.setAttribute(n, clean.join(';'));
44839             } else {
44840                 node.removeAttribute(n);
44841             }
44842             
44843         }
44844         
44845         
44846         for (var i = node.attributes.length-1; i > -1 ; i--) {
44847             var a = node.attributes[i];
44848             //console.log(a);
44849             
44850             if (a.name.toLowerCase().substr(0,2)=='on')  {
44851                 node.removeAttribute(a.name);
44852                 continue;
44853             }
44854             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44855                 node.removeAttribute(a.name);
44856                 continue;
44857             }
44858             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44859                 cleanAttr(a.name,a.value); // fixme..
44860                 continue;
44861             }
44862             if (a.name == 'style') {
44863                 cleanStyle(a.name,a.value);
44864                 continue;
44865             }
44866             /// clean up MS crap..
44867             // tecnically this should be a list of valid class'es..
44868             
44869             
44870             if (a.name == 'class') {
44871                 if (a.value.match(/^Mso/)) {
44872                     node.removeAttribute('class');
44873                 }
44874                 
44875                 if (a.value.match(/^body$/)) {
44876                     node.removeAttribute('class');
44877                 }
44878                 continue;
44879             }
44880             
44881             // style cleanup!?
44882             // class cleanup?
44883             
44884         }
44885         
44886         
44887         this.cleanUpChildren(node);
44888         
44889         
44890     },
44891     
44892     /**
44893      * Clean up MS wordisms...
44894      */
44895     cleanWord : function(node)
44896     {
44897         if (!node) {
44898             this.cleanWord(this.doc.body);
44899             return;
44900         }
44901         
44902         if(
44903                 node.nodeName == 'SPAN' &&
44904                 !node.hasAttributes() &&
44905                 node.childNodes.length == 1 &&
44906                 node.firstChild.nodeName == "#text"  
44907         ) {
44908             var textNode = node.firstChild;
44909             node.removeChild(textNode);
44910             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44911                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44912             }
44913             node.parentNode.insertBefore(textNode, node);
44914             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44915                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44916             }
44917             node.parentNode.removeChild(node);
44918         }
44919         
44920         if (node.nodeName == "#text") {
44921             // clean up silly Windows -- stuff?
44922             return; 
44923         }
44924         if (node.nodeName == "#comment") {
44925             node.parentNode.removeChild(node);
44926             // clean up silly Windows -- stuff?
44927             return; 
44928         }
44929         
44930         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44931             node.parentNode.removeChild(node);
44932             return;
44933         }
44934         //Roo.log(node.tagName);
44935         // remove - but keep children..
44936         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44937             //Roo.log('-- removed');
44938             while (node.childNodes.length) {
44939                 var cn = node.childNodes[0];
44940                 node.removeChild(cn);
44941                 node.parentNode.insertBefore(cn, node);
44942                 // move node to parent - and clean it..
44943                 this.cleanWord(cn);
44944             }
44945             node.parentNode.removeChild(node);
44946             /// no need to iterate chidlren = it's got none..
44947             //this.iterateChildren(node, this.cleanWord);
44948             return;
44949         }
44950         // clean styles
44951         if (node.className.length) {
44952             
44953             var cn = node.className.split(/\W+/);
44954             var cna = [];
44955             Roo.each(cn, function(cls) {
44956                 if (cls.match(/Mso[a-zA-Z]+/)) {
44957                     return;
44958                 }
44959                 cna.push(cls);
44960             });
44961             node.className = cna.length ? cna.join(' ') : '';
44962             if (!cna.length) {
44963                 node.removeAttribute("class");
44964             }
44965         }
44966         
44967         if (node.hasAttribute("lang")) {
44968             node.removeAttribute("lang");
44969         }
44970         
44971         if (node.hasAttribute("style")) {
44972             
44973             var styles = node.getAttribute("style").split(";");
44974             var nstyle = [];
44975             Roo.each(styles, function(s) {
44976                 if (!s.match(/:/)) {
44977                     return;
44978                 }
44979                 var kv = s.split(":");
44980                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44981                     return;
44982                 }
44983                 // what ever is left... we allow.
44984                 nstyle.push(s);
44985             });
44986             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44987             if (!nstyle.length) {
44988                 node.removeAttribute('style');
44989             }
44990         }
44991         this.iterateChildren(node, this.cleanWord);
44992         
44993         
44994         
44995     },
44996     /**
44997      * iterateChildren of a Node, calling fn each time, using this as the scole..
44998      * @param {DomNode} node node to iterate children of.
44999      * @param {Function} fn method of this class to call on each item.
45000      */
45001     iterateChildren : function(node, fn)
45002     {
45003         if (!node.childNodes.length) {
45004                 return;
45005         }
45006         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45007            fn.call(this, node.childNodes[i])
45008         }
45009     },
45010     
45011     
45012     /**
45013      * cleanTableWidths.
45014      *
45015      * Quite often pasting from word etc.. results in tables with column and widths.
45016      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45017      *
45018      */
45019     cleanTableWidths : function(node)
45020     {
45021          
45022          
45023         if (!node) {
45024             this.cleanTableWidths(this.doc.body);
45025             return;
45026         }
45027         
45028         // ignore list...
45029         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45030             return; 
45031         }
45032         Roo.log(node.tagName);
45033         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45034             this.iterateChildren(node, this.cleanTableWidths);
45035             return;
45036         }
45037         if (node.hasAttribute('width')) {
45038             node.removeAttribute('width');
45039         }
45040         
45041          
45042         if (node.hasAttribute("style")) {
45043             // pretty basic...
45044             
45045             var styles = node.getAttribute("style").split(";");
45046             var nstyle = [];
45047             Roo.each(styles, function(s) {
45048                 if (!s.match(/:/)) {
45049                     return;
45050                 }
45051                 var kv = s.split(":");
45052                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45053                     return;
45054                 }
45055                 // what ever is left... we allow.
45056                 nstyle.push(s);
45057             });
45058             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45059             if (!nstyle.length) {
45060                 node.removeAttribute('style');
45061             }
45062         }
45063         
45064         this.iterateChildren(node, this.cleanTableWidths);
45065         
45066         
45067     },
45068     
45069     
45070     
45071     
45072     domToHTML : function(currentElement, depth, nopadtext) {
45073         
45074         depth = depth || 0;
45075         nopadtext = nopadtext || false;
45076     
45077         if (!currentElement) {
45078             return this.domToHTML(this.doc.body);
45079         }
45080         
45081         //Roo.log(currentElement);
45082         var j;
45083         var allText = false;
45084         var nodeName = currentElement.nodeName;
45085         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45086         
45087         if  (nodeName == '#text') {
45088             
45089             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45090         }
45091         
45092         
45093         var ret = '';
45094         if (nodeName != 'BODY') {
45095              
45096             var i = 0;
45097             // Prints the node tagName, such as <A>, <IMG>, etc
45098             if (tagName) {
45099                 var attr = [];
45100                 for(i = 0; i < currentElement.attributes.length;i++) {
45101                     // quoting?
45102                     var aname = currentElement.attributes.item(i).name;
45103                     if (!currentElement.attributes.item(i).value.length) {
45104                         continue;
45105                     }
45106                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45107                 }
45108                 
45109                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45110             } 
45111             else {
45112                 
45113                 // eack
45114             }
45115         } else {
45116             tagName = false;
45117         }
45118         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45119             return ret;
45120         }
45121         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45122             nopadtext = true;
45123         }
45124         
45125         
45126         // Traverse the tree
45127         i = 0;
45128         var currentElementChild = currentElement.childNodes.item(i);
45129         var allText = true;
45130         var innerHTML  = '';
45131         lastnode = '';
45132         while (currentElementChild) {
45133             // Formatting code (indent the tree so it looks nice on the screen)
45134             var nopad = nopadtext;
45135             if (lastnode == 'SPAN') {
45136                 nopad  = true;
45137             }
45138             // text
45139             if  (currentElementChild.nodeName == '#text') {
45140                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45141                 toadd = nopadtext ? toadd : toadd.trim();
45142                 if (!nopad && toadd.length > 80) {
45143                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45144                 }
45145                 innerHTML  += toadd;
45146                 
45147                 i++;
45148                 currentElementChild = currentElement.childNodes.item(i);
45149                 lastNode = '';
45150                 continue;
45151             }
45152             allText = false;
45153             
45154             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45155                 
45156             // Recursively traverse the tree structure of the child node
45157             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45158             lastnode = currentElementChild.nodeName;
45159             i++;
45160             currentElementChild=currentElement.childNodes.item(i);
45161         }
45162         
45163         ret += innerHTML;
45164         
45165         if (!allText) {
45166                 // The remaining code is mostly for formatting the tree
45167             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45168         }
45169         
45170         
45171         if (tagName) {
45172             ret+= "</"+tagName+">";
45173         }
45174         return ret;
45175         
45176     },
45177         
45178     applyBlacklists : function()
45179     {
45180         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45181         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45182         
45183         this.white = [];
45184         this.black = [];
45185         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45186             if (b.indexOf(tag) > -1) {
45187                 return;
45188             }
45189             this.white.push(tag);
45190             
45191         }, this);
45192         
45193         Roo.each(w, function(tag) {
45194             if (b.indexOf(tag) > -1) {
45195                 return;
45196             }
45197             if (this.white.indexOf(tag) > -1) {
45198                 return;
45199             }
45200             this.white.push(tag);
45201             
45202         }, this);
45203         
45204         
45205         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45206             if (w.indexOf(tag) > -1) {
45207                 return;
45208             }
45209             this.black.push(tag);
45210             
45211         }, this);
45212         
45213         Roo.each(b, function(tag) {
45214             if (w.indexOf(tag) > -1) {
45215                 return;
45216             }
45217             if (this.black.indexOf(tag) > -1) {
45218                 return;
45219             }
45220             this.black.push(tag);
45221             
45222         }, this);
45223         
45224         
45225         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45226         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45227         
45228         this.cwhite = [];
45229         this.cblack = [];
45230         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45231             if (b.indexOf(tag) > -1) {
45232                 return;
45233             }
45234             this.cwhite.push(tag);
45235             
45236         }, this);
45237         
45238         Roo.each(w, function(tag) {
45239             if (b.indexOf(tag) > -1) {
45240                 return;
45241             }
45242             if (this.cwhite.indexOf(tag) > -1) {
45243                 return;
45244             }
45245             this.cwhite.push(tag);
45246             
45247         }, this);
45248         
45249         
45250         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45251             if (w.indexOf(tag) > -1) {
45252                 return;
45253             }
45254             this.cblack.push(tag);
45255             
45256         }, this);
45257         
45258         Roo.each(b, function(tag) {
45259             if (w.indexOf(tag) > -1) {
45260                 return;
45261             }
45262             if (this.cblack.indexOf(tag) > -1) {
45263                 return;
45264             }
45265             this.cblack.push(tag);
45266             
45267         }, this);
45268     },
45269     
45270     setStylesheets : function(stylesheets)
45271     {
45272         if(typeof(stylesheets) == 'string'){
45273             Roo.get(this.iframe.contentDocument.head).createChild({
45274                 tag : 'link',
45275                 rel : 'stylesheet',
45276                 type : 'text/css',
45277                 href : stylesheets
45278             });
45279             
45280             return;
45281         }
45282         var _this = this;
45283      
45284         Roo.each(stylesheets, function(s) {
45285             if(!s.length){
45286                 return;
45287             }
45288             
45289             Roo.get(_this.iframe.contentDocument.head).createChild({
45290                 tag : 'link',
45291                 rel : 'stylesheet',
45292                 type : 'text/css',
45293                 href : s
45294             });
45295         });
45296
45297         
45298     },
45299     
45300     removeStylesheets : function()
45301     {
45302         var _this = this;
45303         
45304         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45305             s.remove();
45306         });
45307     },
45308     
45309     setStyle : function(style)
45310     {
45311         Roo.get(this.iframe.contentDocument.head).createChild({
45312             tag : 'style',
45313             type : 'text/css',
45314             html : style
45315         });
45316
45317         return;
45318     }
45319     
45320     // hide stuff that is not compatible
45321     /**
45322      * @event blur
45323      * @hide
45324      */
45325     /**
45326      * @event change
45327      * @hide
45328      */
45329     /**
45330      * @event focus
45331      * @hide
45332      */
45333     /**
45334      * @event specialkey
45335      * @hide
45336      */
45337     /**
45338      * @cfg {String} fieldClass @hide
45339      */
45340     /**
45341      * @cfg {String} focusClass @hide
45342      */
45343     /**
45344      * @cfg {String} autoCreate @hide
45345      */
45346     /**
45347      * @cfg {String} inputType @hide
45348      */
45349     /**
45350      * @cfg {String} invalidClass @hide
45351      */
45352     /**
45353      * @cfg {String} invalidText @hide
45354      */
45355     /**
45356      * @cfg {String} msgFx @hide
45357      */
45358     /**
45359      * @cfg {String} validateOnBlur @hide
45360      */
45361 });
45362
45363 Roo.HtmlEditorCore.white = [
45364         'area', 'br', 'img', 'input', 'hr', 'wbr',
45365         
45366        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45367        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45368        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45369        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45370        'table',   'ul',         'xmp', 
45371        
45372        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45373       'thead',   'tr', 
45374      
45375       'dir', 'menu', 'ol', 'ul', 'dl',
45376        
45377       'embed',  'object'
45378 ];
45379
45380
45381 Roo.HtmlEditorCore.black = [
45382     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45383         'applet', // 
45384         'base',   'basefont', 'bgsound', 'blink',  'body', 
45385         'frame',  'frameset', 'head',    'html',   'ilayer', 
45386         'iframe', 'layer',  'link',     'meta',    'object',   
45387         'script', 'style' ,'title',  'xml' // clean later..
45388 ];
45389 Roo.HtmlEditorCore.clean = [
45390     'script', 'style', 'title', 'xml'
45391 ];
45392 Roo.HtmlEditorCore.remove = [
45393     'font'
45394 ];
45395 // attributes..
45396
45397 Roo.HtmlEditorCore.ablack = [
45398     'on'
45399 ];
45400     
45401 Roo.HtmlEditorCore.aclean = [ 
45402     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45403 ];
45404
45405 // protocols..
45406 Roo.HtmlEditorCore.pwhite= [
45407         'http',  'https',  'mailto'
45408 ];
45409
45410 // white listed style attributes.
45411 Roo.HtmlEditorCore.cwhite= [
45412       //  'text-align', /// default is to allow most things..
45413       
45414          
45415 //        'font-size'//??
45416 ];
45417
45418 // black listed style attributes.
45419 Roo.HtmlEditorCore.cblack= [
45420       //  'font-size' -- this can be set by the project 
45421 ];
45422
45423
45424 Roo.HtmlEditorCore.swapCodes   =[ 
45425     [    8211, "&#8211;" ], 
45426     [    8212, "&#8212;" ], 
45427     [    8216,  "'" ],  
45428     [    8217, "'" ],  
45429     [    8220, '"' ],  
45430     [    8221, '"' ],  
45431     [    8226, "*" ],  
45432     [    8230, "..." ]
45433 ]; 
45434
45435     //<script type="text/javascript">
45436
45437 /*
45438  * Ext JS Library 1.1.1
45439  * Copyright(c) 2006-2007, Ext JS, LLC.
45440  * Licence LGPL
45441  * 
45442  */
45443  
45444  
45445 Roo.form.HtmlEditor = function(config){
45446     
45447     
45448     
45449     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45450     
45451     if (!this.toolbars) {
45452         this.toolbars = [];
45453     }
45454     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45455     
45456     
45457 };
45458
45459 /**
45460  * @class Roo.form.HtmlEditor
45461  * @extends Roo.form.Field
45462  * Provides a lightweight HTML Editor component.
45463  *
45464  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45465  * 
45466  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45467  * supported by this editor.</b><br/><br/>
45468  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45469  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45470  */
45471 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45472     /**
45473      * @cfg {Boolean} clearUp
45474      */
45475     clearUp : true,
45476       /**
45477      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45478      */
45479     toolbars : false,
45480    
45481      /**
45482      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45483      *                        Roo.resizable.
45484      */
45485     resizable : false,
45486      /**
45487      * @cfg {Number} height (in pixels)
45488      */   
45489     height: 300,
45490    /**
45491      * @cfg {Number} width (in pixels)
45492      */   
45493     width: 500,
45494     
45495     /**
45496      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45497      * 
45498      */
45499     stylesheets: false,
45500     
45501     
45502      /**
45503      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45504      * 
45505      */
45506     cblack: false,
45507     /**
45508      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45509      * 
45510      */
45511     cwhite: false,
45512     
45513      /**
45514      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45515      * 
45516      */
45517     black: false,
45518     /**
45519      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45520      * 
45521      */
45522     white: false,
45523     /**
45524      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45525      */
45526     allowComments: false,
45527     
45528     // id of frame..
45529     frameId: false,
45530     
45531     // private properties
45532     validationEvent : false,
45533     deferHeight: true,
45534     initialized : false,
45535     activated : false,
45536     
45537     onFocus : Roo.emptyFn,
45538     iframePad:3,
45539     hideMode:'offsets',
45540     
45541     actionMode : 'container', // defaults to hiding it...
45542     
45543     defaultAutoCreate : { // modified by initCompnoent..
45544         tag: "textarea",
45545         style:"width:500px;height:300px;",
45546         autocomplete: "new-password"
45547     },
45548
45549     // private
45550     initComponent : function(){
45551         this.addEvents({
45552             /**
45553              * @event initialize
45554              * Fires when the editor is fully initialized (including the iframe)
45555              * @param {HtmlEditor} this
45556              */
45557             initialize: true,
45558             /**
45559              * @event activate
45560              * Fires when the editor is first receives the focus. Any insertion must wait
45561              * until after this event.
45562              * @param {HtmlEditor} this
45563              */
45564             activate: true,
45565              /**
45566              * @event beforesync
45567              * Fires before the textarea is updated with content from the editor iframe. Return false
45568              * to cancel the sync.
45569              * @param {HtmlEditor} this
45570              * @param {String} html
45571              */
45572             beforesync: true,
45573              /**
45574              * @event beforepush
45575              * Fires before the iframe editor is updated with content from the textarea. Return false
45576              * to cancel the push.
45577              * @param {HtmlEditor} this
45578              * @param {String} html
45579              */
45580             beforepush: true,
45581              /**
45582              * @event sync
45583              * Fires when the textarea is updated with content from the editor iframe.
45584              * @param {HtmlEditor} this
45585              * @param {String} html
45586              */
45587             sync: true,
45588              /**
45589              * @event push
45590              * Fires when the iframe editor is updated with content from the textarea.
45591              * @param {HtmlEditor} this
45592              * @param {String} html
45593              */
45594             push: true,
45595              /**
45596              * @event editmodechange
45597              * Fires when the editor switches edit modes
45598              * @param {HtmlEditor} this
45599              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45600              */
45601             editmodechange: true,
45602             /**
45603              * @event editorevent
45604              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45605              * @param {HtmlEditor} this
45606              */
45607             editorevent: true,
45608             /**
45609              * @event firstfocus
45610              * Fires when on first focus - needed by toolbars..
45611              * @param {HtmlEditor} this
45612              */
45613             firstfocus: true,
45614             /**
45615              * @event autosave
45616              * Auto save the htmlEditor value as a file into Events
45617              * @param {HtmlEditor} this
45618              */
45619             autosave: true,
45620             /**
45621              * @event savedpreview
45622              * preview the saved version of htmlEditor
45623              * @param {HtmlEditor} this
45624              */
45625             savedpreview: true,
45626             
45627             /**
45628             * @event stylesheetsclick
45629             * Fires when press the Sytlesheets button
45630             * @param {Roo.HtmlEditorCore} this
45631             */
45632             stylesheetsclick: true
45633         });
45634         this.defaultAutoCreate =  {
45635             tag: "textarea",
45636             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45637             autocomplete: "new-password"
45638         };
45639     },
45640
45641     /**
45642      * Protected method that will not generally be called directly. It
45643      * is called when the editor creates its toolbar. Override this method if you need to
45644      * add custom toolbar buttons.
45645      * @param {HtmlEditor} editor
45646      */
45647     createToolbar : function(editor){
45648         Roo.log("create toolbars");
45649         if (!editor.toolbars || !editor.toolbars.length) {
45650             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45651         }
45652         
45653         for (var i =0 ; i < editor.toolbars.length;i++) {
45654             editor.toolbars[i] = Roo.factory(
45655                     typeof(editor.toolbars[i]) == 'string' ?
45656                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45657                 Roo.form.HtmlEditor);
45658             editor.toolbars[i].init(editor);
45659         }
45660          
45661         
45662     },
45663
45664      
45665     // private
45666     onRender : function(ct, position)
45667     {
45668         var _t = this;
45669         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45670         
45671         this.wrap = this.el.wrap({
45672             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45673         });
45674         
45675         this.editorcore.onRender(ct, position);
45676          
45677         if (this.resizable) {
45678             this.resizeEl = new Roo.Resizable(this.wrap, {
45679                 pinned : true,
45680                 wrap: true,
45681                 dynamic : true,
45682                 minHeight : this.height,
45683                 height: this.height,
45684                 handles : this.resizable,
45685                 width: this.width,
45686                 listeners : {
45687                     resize : function(r, w, h) {
45688                         _t.onResize(w,h); // -something
45689                     }
45690                 }
45691             });
45692             
45693         }
45694         this.createToolbar(this);
45695        
45696         
45697         if(!this.width){
45698             this.setSize(this.wrap.getSize());
45699         }
45700         if (this.resizeEl) {
45701             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45702             // should trigger onReize..
45703         }
45704         
45705         this.keyNav = new Roo.KeyNav(this.el, {
45706             
45707             "tab" : function(e){
45708                 e.preventDefault();
45709                 
45710                 var value = this.getValue();
45711                 
45712                 var start = this.el.dom.selectionStart;
45713                 var end = this.el.dom.selectionEnd;
45714                 
45715                 if(!e.shiftKey){
45716                     
45717                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45718                     this.el.dom.setSelectionRange(end + 1, end + 1);
45719                     return;
45720                 }
45721                 
45722                 var f = value.substring(0, start).split("\t");
45723                 
45724                 if(f.pop().length != 0){
45725                     return;
45726                 }
45727                 
45728                 this.setValue(f.join("\t") + value.substring(end));
45729                 this.el.dom.setSelectionRange(start - 1, start - 1);
45730                 
45731             },
45732             
45733             "home" : function(e){
45734                 e.preventDefault();
45735                 
45736                 var curr = this.el.dom.selectionStart;
45737                 var lines = this.getValue().split("\n");
45738                 
45739                 if(!lines.length){
45740                     return;
45741                 }
45742                 
45743                 if(e.ctrlKey){
45744                     this.el.dom.setSelectionRange(0, 0);
45745                     return;
45746                 }
45747                 
45748                 var pos = 0;
45749                 
45750                 for (var i = 0; i < lines.length;i++) {
45751                     pos += lines[i].length;
45752                     
45753                     if(i != 0){
45754                         pos += 1;
45755                     }
45756                     
45757                     if(pos < curr){
45758                         continue;
45759                     }
45760                     
45761                     pos -= lines[i].length;
45762                     
45763                     break;
45764                 }
45765                 
45766                 if(!e.shiftKey){
45767                     this.el.dom.setSelectionRange(pos, pos);
45768                     return;
45769                 }
45770                 
45771                 this.el.dom.selectionStart = pos;
45772                 this.el.dom.selectionEnd = curr;
45773             },
45774             
45775             "end" : function(e){
45776                 e.preventDefault();
45777                 
45778                 var curr = this.el.dom.selectionStart;
45779                 var lines = this.getValue().split("\n");
45780                 
45781                 if(!lines.length){
45782                     return;
45783                 }
45784                 
45785                 if(e.ctrlKey){
45786                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45787                     return;
45788                 }
45789                 
45790                 var pos = 0;
45791                 
45792                 for (var i = 0; i < lines.length;i++) {
45793                     
45794                     pos += lines[i].length;
45795                     
45796                     if(i != 0){
45797                         pos += 1;
45798                     }
45799                     
45800                     if(pos < curr){
45801                         continue;
45802                     }
45803                     
45804                     break;
45805                 }
45806                 
45807                 if(!e.shiftKey){
45808                     this.el.dom.setSelectionRange(pos, pos);
45809                     return;
45810                 }
45811                 
45812                 this.el.dom.selectionStart = curr;
45813                 this.el.dom.selectionEnd = pos;
45814             },
45815
45816             scope : this,
45817
45818             doRelay : function(foo, bar, hname){
45819                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45820             },
45821
45822             forceKeyDown: true
45823         });
45824         
45825 //        if(this.autosave && this.w){
45826 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45827 //        }
45828     },
45829
45830     // private
45831     onResize : function(w, h)
45832     {
45833         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45834         var ew = false;
45835         var eh = false;
45836         
45837         if(this.el ){
45838             if(typeof w == 'number'){
45839                 var aw = w - this.wrap.getFrameWidth('lr');
45840                 this.el.setWidth(this.adjustWidth('textarea', aw));
45841                 ew = aw;
45842             }
45843             if(typeof h == 'number'){
45844                 var tbh = 0;
45845                 for (var i =0; i < this.toolbars.length;i++) {
45846                     // fixme - ask toolbars for heights?
45847                     tbh += this.toolbars[i].tb.el.getHeight();
45848                     if (this.toolbars[i].footer) {
45849                         tbh += this.toolbars[i].footer.el.getHeight();
45850                     }
45851                 }
45852                 
45853                 
45854                 
45855                 
45856                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45857                 ah -= 5; // knock a few pixes off for look..
45858 //                Roo.log(ah);
45859                 this.el.setHeight(this.adjustWidth('textarea', ah));
45860                 var eh = ah;
45861             }
45862         }
45863         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45864         this.editorcore.onResize(ew,eh);
45865         
45866     },
45867
45868     /**
45869      * Toggles the editor between standard and source edit mode.
45870      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45871      */
45872     toggleSourceEdit : function(sourceEditMode)
45873     {
45874         this.editorcore.toggleSourceEdit(sourceEditMode);
45875         
45876         if(this.editorcore.sourceEditMode){
45877             Roo.log('editor - showing textarea');
45878             
45879 //            Roo.log('in');
45880 //            Roo.log(this.syncValue());
45881             this.editorcore.syncValue();
45882             this.el.removeClass('x-hidden');
45883             this.el.dom.removeAttribute('tabIndex');
45884             this.el.focus();
45885             
45886             for (var i = 0; i < this.toolbars.length; i++) {
45887                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45888                     this.toolbars[i].tb.hide();
45889                     this.toolbars[i].footer.hide();
45890                 }
45891             }
45892             
45893         }else{
45894             Roo.log('editor - hiding textarea');
45895 //            Roo.log('out')
45896 //            Roo.log(this.pushValue()); 
45897             this.editorcore.pushValue();
45898             
45899             this.el.addClass('x-hidden');
45900             this.el.dom.setAttribute('tabIndex', -1);
45901             
45902             for (var i = 0; i < this.toolbars.length; i++) {
45903                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45904                     this.toolbars[i].tb.show();
45905                     this.toolbars[i].footer.show();
45906                 }
45907             }
45908             
45909             //this.deferFocus();
45910         }
45911         
45912         this.setSize(this.wrap.getSize());
45913         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45914         
45915         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45916     },
45917  
45918     // private (for BoxComponent)
45919     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45920
45921     // private (for BoxComponent)
45922     getResizeEl : function(){
45923         return this.wrap;
45924     },
45925
45926     // private (for BoxComponent)
45927     getPositionEl : function(){
45928         return this.wrap;
45929     },
45930
45931     // private
45932     initEvents : function(){
45933         this.originalValue = this.getValue();
45934     },
45935
45936     /**
45937      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45938      * @method
45939      */
45940     markInvalid : Roo.emptyFn,
45941     /**
45942      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45943      * @method
45944      */
45945     clearInvalid : Roo.emptyFn,
45946
45947     setValue : function(v){
45948         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45949         this.editorcore.pushValue();
45950     },
45951
45952      
45953     // private
45954     deferFocus : function(){
45955         this.focus.defer(10, this);
45956     },
45957
45958     // doc'ed in Field
45959     focus : function(){
45960         this.editorcore.focus();
45961         
45962     },
45963       
45964
45965     // private
45966     onDestroy : function(){
45967         
45968         
45969         
45970         if(this.rendered){
45971             
45972             for (var i =0; i < this.toolbars.length;i++) {
45973                 // fixme - ask toolbars for heights?
45974                 this.toolbars[i].onDestroy();
45975             }
45976             
45977             this.wrap.dom.innerHTML = '';
45978             this.wrap.remove();
45979         }
45980     },
45981
45982     // private
45983     onFirstFocus : function(){
45984         //Roo.log("onFirstFocus");
45985         this.editorcore.onFirstFocus();
45986          for (var i =0; i < this.toolbars.length;i++) {
45987             this.toolbars[i].onFirstFocus();
45988         }
45989         
45990     },
45991     
45992     // private
45993     syncValue : function()
45994     {
45995         this.editorcore.syncValue();
45996     },
45997     
45998     pushValue : function()
45999     {
46000         this.editorcore.pushValue();
46001     },
46002     
46003     setStylesheets : function(stylesheets)
46004     {
46005         this.editorcore.setStylesheets(stylesheets);
46006     },
46007     
46008     removeStylesheets : function()
46009     {
46010         this.editorcore.removeStylesheets();
46011     }
46012      
46013     
46014     // hide stuff that is not compatible
46015     /**
46016      * @event blur
46017      * @hide
46018      */
46019     /**
46020      * @event change
46021      * @hide
46022      */
46023     /**
46024      * @event focus
46025      * @hide
46026      */
46027     /**
46028      * @event specialkey
46029      * @hide
46030      */
46031     /**
46032      * @cfg {String} fieldClass @hide
46033      */
46034     /**
46035      * @cfg {String} focusClass @hide
46036      */
46037     /**
46038      * @cfg {String} autoCreate @hide
46039      */
46040     /**
46041      * @cfg {String} inputType @hide
46042      */
46043     /**
46044      * @cfg {String} invalidClass @hide
46045      */
46046     /**
46047      * @cfg {String} invalidText @hide
46048      */
46049     /**
46050      * @cfg {String} msgFx @hide
46051      */
46052     /**
46053      * @cfg {String} validateOnBlur @hide
46054      */
46055 });
46056  
46057     // <script type="text/javascript">
46058 /*
46059  * Based on
46060  * Ext JS Library 1.1.1
46061  * Copyright(c) 2006-2007, Ext JS, LLC.
46062  *  
46063  
46064  */
46065
46066 /**
46067  * @class Roo.form.HtmlEditorToolbar1
46068  * Basic Toolbar
46069  * 
46070  * Usage:
46071  *
46072  new Roo.form.HtmlEditor({
46073     ....
46074     toolbars : [
46075         new Roo.form.HtmlEditorToolbar1({
46076             disable : { fonts: 1 , format: 1, ..., ... , ...],
46077             btns : [ .... ]
46078         })
46079     }
46080      
46081  * 
46082  * @cfg {Object} disable List of elements to disable..
46083  * @cfg {Array} btns List of additional buttons.
46084  * 
46085  * 
46086  * NEEDS Extra CSS? 
46087  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46088  */
46089  
46090 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46091 {
46092     
46093     Roo.apply(this, config);
46094     
46095     // default disabled, based on 'good practice'..
46096     this.disable = this.disable || {};
46097     Roo.applyIf(this.disable, {
46098         fontSize : true,
46099         colors : true,
46100         specialElements : true
46101     });
46102     
46103     
46104     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46105     // dont call parent... till later.
46106 }
46107
46108 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46109     
46110     tb: false,
46111     
46112     rendered: false,
46113     
46114     editor : false,
46115     editorcore : false,
46116     /**
46117      * @cfg {Object} disable  List of toolbar elements to disable
46118          
46119      */
46120     disable : false,
46121     
46122     
46123      /**
46124      * @cfg {String} createLinkText The default text for the create link prompt
46125      */
46126     createLinkText : 'Please enter the URL for the link:',
46127     /**
46128      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46129      */
46130     defaultLinkValue : 'http:/'+'/',
46131    
46132     
46133       /**
46134      * @cfg {Array} fontFamilies An array of available font families
46135      */
46136     fontFamilies : [
46137         'Arial',
46138         'Courier New',
46139         'Tahoma',
46140         'Times New Roman',
46141         'Verdana'
46142     ],
46143     
46144     specialChars : [
46145            "&#169;",
46146           "&#174;",     
46147           "&#8482;",    
46148           "&#163;" ,    
46149          // "&#8212;",    
46150           "&#8230;",    
46151           "&#247;" ,    
46152         //  "&#225;" ,     ?? a acute?
46153            "&#8364;"    , //Euro
46154        //   "&#8220;"    ,
46155         //  "&#8221;"    ,
46156         //  "&#8226;"    ,
46157           "&#176;"  //   , // degrees
46158
46159          // "&#233;"     , // e ecute
46160          // "&#250;"     , // u ecute?
46161     ],
46162     
46163     specialElements : [
46164         {
46165             text: "Insert Table",
46166             xtype: 'MenuItem',
46167             xns : Roo.Menu,
46168             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46169                 
46170         },
46171         {    
46172             text: "Insert Image",
46173             xtype: 'MenuItem',
46174             xns : Roo.Menu,
46175             ihtml : '<img src="about:blank"/>'
46176             
46177         }
46178         
46179          
46180     ],
46181     
46182     
46183     inputElements : [ 
46184             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46185             "input:submit", "input:button", "select", "textarea", "label" ],
46186     formats : [
46187         ["p"] ,  
46188         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46189         ["pre"],[ "code"], 
46190         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46191         ['div'],['span'],
46192         ['sup'],['sub']
46193     ],
46194     
46195     cleanStyles : [
46196         "font-size"
46197     ],
46198      /**
46199      * @cfg {String} defaultFont default font to use.
46200      */
46201     defaultFont: 'tahoma',
46202    
46203     fontSelect : false,
46204     
46205     
46206     formatCombo : false,
46207     
46208     init : function(editor)
46209     {
46210         this.editor = editor;
46211         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46212         var editorcore = this.editorcore;
46213         
46214         var _t = this;
46215         
46216         var fid = editorcore.frameId;
46217         var etb = this;
46218         function btn(id, toggle, handler){
46219             var xid = fid + '-'+ id ;
46220             return {
46221                 id : xid,
46222                 cmd : id,
46223                 cls : 'x-btn-icon x-edit-'+id,
46224                 enableToggle:toggle !== false,
46225                 scope: _t, // was editor...
46226                 handler:handler||_t.relayBtnCmd,
46227                 clickEvent:'mousedown',
46228                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46229                 tabIndex:-1
46230             };
46231         }
46232         
46233         
46234         
46235         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46236         this.tb = tb;
46237          // stop form submits
46238         tb.el.on('click', function(e){
46239             e.preventDefault(); // what does this do?
46240         });
46241
46242         if(!this.disable.font) { // && !Roo.isSafari){
46243             /* why no safari for fonts 
46244             editor.fontSelect = tb.el.createChild({
46245                 tag:'select',
46246                 tabIndex: -1,
46247                 cls:'x-font-select',
46248                 html: this.createFontOptions()
46249             });
46250             
46251             editor.fontSelect.on('change', function(){
46252                 var font = editor.fontSelect.dom.value;
46253                 editor.relayCmd('fontname', font);
46254                 editor.deferFocus();
46255             }, editor);
46256             
46257             tb.add(
46258                 editor.fontSelect.dom,
46259                 '-'
46260             );
46261             */
46262             
46263         };
46264         if(!this.disable.formats){
46265             this.formatCombo = new Roo.form.ComboBox({
46266                 store: new Roo.data.SimpleStore({
46267                     id : 'tag',
46268                     fields: ['tag'],
46269                     data : this.formats // from states.js
46270                 }),
46271                 blockFocus : true,
46272                 name : '',
46273                 //autoCreate : {tag: "div",  size: "20"},
46274                 displayField:'tag',
46275                 typeAhead: false,
46276                 mode: 'local',
46277                 editable : false,
46278                 triggerAction: 'all',
46279                 emptyText:'Add tag',
46280                 selectOnFocus:true,
46281                 width:135,
46282                 listeners : {
46283                     'select': function(c, r, i) {
46284                         editorcore.insertTag(r.get('tag'));
46285                         editor.focus();
46286                     }
46287                 }
46288
46289             });
46290             tb.addField(this.formatCombo);
46291             
46292         }
46293         
46294         if(!this.disable.format){
46295             tb.add(
46296                 btn('bold'),
46297                 btn('italic'),
46298                 btn('underline'),
46299                 btn('strikethrough')
46300             );
46301         };
46302         if(!this.disable.fontSize){
46303             tb.add(
46304                 '-',
46305                 
46306                 
46307                 btn('increasefontsize', false, editorcore.adjustFont),
46308                 btn('decreasefontsize', false, editorcore.adjustFont)
46309             );
46310         };
46311         
46312         
46313         if(!this.disable.colors){
46314             tb.add(
46315                 '-', {
46316                     id:editorcore.frameId +'-forecolor',
46317                     cls:'x-btn-icon x-edit-forecolor',
46318                     clickEvent:'mousedown',
46319                     tooltip: this.buttonTips['forecolor'] || undefined,
46320                     tabIndex:-1,
46321                     menu : new Roo.menu.ColorMenu({
46322                         allowReselect: true,
46323                         focus: Roo.emptyFn,
46324                         value:'000000',
46325                         plain:true,
46326                         selectHandler: function(cp, color){
46327                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46328                             editor.deferFocus();
46329                         },
46330                         scope: editorcore,
46331                         clickEvent:'mousedown'
46332                     })
46333                 }, {
46334                     id:editorcore.frameId +'backcolor',
46335                     cls:'x-btn-icon x-edit-backcolor',
46336                     clickEvent:'mousedown',
46337                     tooltip: this.buttonTips['backcolor'] || undefined,
46338                     tabIndex:-1,
46339                     menu : new Roo.menu.ColorMenu({
46340                         focus: Roo.emptyFn,
46341                         value:'FFFFFF',
46342                         plain:true,
46343                         allowReselect: true,
46344                         selectHandler: function(cp, color){
46345                             if(Roo.isGecko){
46346                                 editorcore.execCmd('useCSS', false);
46347                                 editorcore.execCmd('hilitecolor', color);
46348                                 editorcore.execCmd('useCSS', true);
46349                                 editor.deferFocus();
46350                             }else{
46351                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46352                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46353                                 editor.deferFocus();
46354                             }
46355                         },
46356                         scope:editorcore,
46357                         clickEvent:'mousedown'
46358                     })
46359                 }
46360             );
46361         };
46362         // now add all the items...
46363         
46364
46365         if(!this.disable.alignments){
46366             tb.add(
46367                 '-',
46368                 btn('justifyleft'),
46369                 btn('justifycenter'),
46370                 btn('justifyright')
46371             );
46372         };
46373
46374         //if(!Roo.isSafari){
46375             if(!this.disable.links){
46376                 tb.add(
46377                     '-',
46378                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46379                 );
46380             };
46381
46382             if(!this.disable.lists){
46383                 tb.add(
46384                     '-',
46385                     btn('insertorderedlist'),
46386                     btn('insertunorderedlist')
46387                 );
46388             }
46389             if(!this.disable.sourceEdit){
46390                 tb.add(
46391                     '-',
46392                     btn('sourceedit', true, function(btn){
46393                         this.toggleSourceEdit(btn.pressed);
46394                     })
46395                 );
46396             }
46397         //}
46398         
46399         var smenu = { };
46400         // special menu.. - needs to be tidied up..
46401         if (!this.disable.special) {
46402             smenu = {
46403                 text: "&#169;",
46404                 cls: 'x-edit-none',
46405                 
46406                 menu : {
46407                     items : []
46408                 }
46409             };
46410             for (var i =0; i < this.specialChars.length; i++) {
46411                 smenu.menu.items.push({
46412                     
46413                     html: this.specialChars[i],
46414                     handler: function(a,b) {
46415                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46416                         //editor.insertAtCursor(a.html);
46417                         
46418                     },
46419                     tabIndex:-1
46420                 });
46421             }
46422             
46423             
46424             tb.add(smenu);
46425             
46426             
46427         }
46428         
46429         var cmenu = { };
46430         if (!this.disable.cleanStyles) {
46431             cmenu = {
46432                 cls: 'x-btn-icon x-btn-clear',
46433                 
46434                 menu : {
46435                     items : []
46436                 }
46437             };
46438             for (var i =0; i < this.cleanStyles.length; i++) {
46439                 cmenu.menu.items.push({
46440                     actiontype : this.cleanStyles[i],
46441                     html: 'Remove ' + this.cleanStyles[i],
46442                     handler: function(a,b) {
46443 //                        Roo.log(a);
46444 //                        Roo.log(b);
46445                         var c = Roo.get(editorcore.doc.body);
46446                         c.select('[style]').each(function(s) {
46447                             s.dom.style.removeProperty(a.actiontype);
46448                         });
46449                         editorcore.syncValue();
46450                     },
46451                     tabIndex:-1
46452                 });
46453             }
46454              cmenu.menu.items.push({
46455                 actiontype : 'tablewidths',
46456                 html: 'Remove Table Widths',
46457                 handler: function(a,b) {
46458                     editorcore.cleanTableWidths();
46459                     editorcore.syncValue();
46460                 },
46461                 tabIndex:-1
46462             });
46463             cmenu.menu.items.push({
46464                 actiontype : 'word',
46465                 html: 'Remove MS Word Formating',
46466                 handler: function(a,b) {
46467                     editorcore.cleanWord();
46468                     editorcore.syncValue();
46469                 },
46470                 tabIndex:-1
46471             });
46472             
46473             cmenu.menu.items.push({
46474                 actiontype : 'all',
46475                 html: 'Remove All Styles',
46476                 handler: function(a,b) {
46477                     
46478                     var c = Roo.get(editorcore.doc.body);
46479                     c.select('[style]').each(function(s) {
46480                         s.dom.removeAttribute('style');
46481                     });
46482                     editorcore.syncValue();
46483                 },
46484                 tabIndex:-1
46485             });
46486             
46487             cmenu.menu.items.push({
46488                 actiontype : 'all',
46489                 html: 'Remove All CSS Classes',
46490                 handler: function(a,b) {
46491                     
46492                     var c = Roo.get(editorcore.doc.body);
46493                     c.select('[class]').each(function(s) {
46494                         s.dom.removeAttribute('class');
46495                     });
46496                     editorcore.cleanWord();
46497                     editorcore.syncValue();
46498                 },
46499                 tabIndex:-1
46500             });
46501             
46502              cmenu.menu.items.push({
46503                 actiontype : 'tidy',
46504                 html: 'Tidy HTML Source',
46505                 handler: function(a,b) {
46506                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46507                     editorcore.syncValue();
46508                 },
46509                 tabIndex:-1
46510             });
46511             
46512             
46513             tb.add(cmenu);
46514         }
46515          
46516         if (!this.disable.specialElements) {
46517             var semenu = {
46518                 text: "Other;",
46519                 cls: 'x-edit-none',
46520                 menu : {
46521                     items : []
46522                 }
46523             };
46524             for (var i =0; i < this.specialElements.length; i++) {
46525                 semenu.menu.items.push(
46526                     Roo.apply({ 
46527                         handler: function(a,b) {
46528                             editor.insertAtCursor(this.ihtml);
46529                         }
46530                     }, this.specialElements[i])
46531                 );
46532                     
46533             }
46534             
46535             tb.add(semenu);
46536             
46537             
46538         }
46539          
46540         
46541         if (this.btns) {
46542             for(var i =0; i< this.btns.length;i++) {
46543                 var b = Roo.factory(this.btns[i],Roo.form);
46544                 b.cls =  'x-edit-none';
46545                 
46546                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46547                     b.cls += ' x-init-enable';
46548                 }
46549                 
46550                 b.scope = editorcore;
46551                 tb.add(b);
46552             }
46553         
46554         }
46555         
46556         
46557         
46558         // disable everything...
46559         
46560         this.tb.items.each(function(item){
46561             
46562            if(
46563                 item.id != editorcore.frameId+ '-sourceedit' && 
46564                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46565             ){
46566                 
46567                 item.disable();
46568             }
46569         });
46570         this.rendered = true;
46571         
46572         // the all the btns;
46573         editor.on('editorevent', this.updateToolbar, this);
46574         // other toolbars need to implement this..
46575         //editor.on('editmodechange', this.updateToolbar, this);
46576     },
46577     
46578     
46579     relayBtnCmd : function(btn) {
46580         this.editorcore.relayCmd(btn.cmd);
46581     },
46582     // private used internally
46583     createLink : function(){
46584         Roo.log("create link?");
46585         var url = prompt(this.createLinkText, this.defaultLinkValue);
46586         if(url && url != 'http:/'+'/'){
46587             this.editorcore.relayCmd('createlink', url);
46588         }
46589     },
46590
46591     
46592     /**
46593      * Protected method that will not generally be called directly. It triggers
46594      * a toolbar update by reading the markup state of the current selection in the editor.
46595      */
46596     updateToolbar: function(){
46597
46598         if(!this.editorcore.activated){
46599             this.editor.onFirstFocus();
46600             return;
46601         }
46602
46603         var btns = this.tb.items.map, 
46604             doc = this.editorcore.doc,
46605             frameId = this.editorcore.frameId;
46606
46607         if(!this.disable.font && !Roo.isSafari){
46608             /*
46609             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46610             if(name != this.fontSelect.dom.value){
46611                 this.fontSelect.dom.value = name;
46612             }
46613             */
46614         }
46615         if(!this.disable.format){
46616             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46617             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46618             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46619             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46620         }
46621         if(!this.disable.alignments){
46622             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46623             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46624             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46625         }
46626         if(!Roo.isSafari && !this.disable.lists){
46627             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46628             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46629         }
46630         
46631         var ans = this.editorcore.getAllAncestors();
46632         if (this.formatCombo) {
46633             
46634             
46635             var store = this.formatCombo.store;
46636             this.formatCombo.setValue("");
46637             for (var i =0; i < ans.length;i++) {
46638                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46639                     // select it..
46640                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46641                     break;
46642                 }
46643             }
46644         }
46645         
46646         
46647         
46648         // hides menus... - so this cant be on a menu...
46649         Roo.menu.MenuMgr.hideAll();
46650
46651         //this.editorsyncValue();
46652     },
46653    
46654     
46655     createFontOptions : function(){
46656         var buf = [], fs = this.fontFamilies, ff, lc;
46657         
46658         
46659         
46660         for(var i = 0, len = fs.length; i< len; i++){
46661             ff = fs[i];
46662             lc = ff.toLowerCase();
46663             buf.push(
46664                 '<option value="',lc,'" style="font-family:',ff,';"',
46665                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46666                     ff,
46667                 '</option>'
46668             );
46669         }
46670         return buf.join('');
46671     },
46672     
46673     toggleSourceEdit : function(sourceEditMode){
46674         
46675         Roo.log("toolbar toogle");
46676         if(sourceEditMode === undefined){
46677             sourceEditMode = !this.sourceEditMode;
46678         }
46679         this.sourceEditMode = sourceEditMode === true;
46680         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46681         // just toggle the button?
46682         if(btn.pressed !== this.sourceEditMode){
46683             btn.toggle(this.sourceEditMode);
46684             return;
46685         }
46686         
46687         if(sourceEditMode){
46688             Roo.log("disabling buttons");
46689             this.tb.items.each(function(item){
46690                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46691                     item.disable();
46692                 }
46693             });
46694           
46695         }else{
46696             Roo.log("enabling buttons");
46697             if(this.editorcore.initialized){
46698                 this.tb.items.each(function(item){
46699                     item.enable();
46700                 });
46701             }
46702             
46703         }
46704         Roo.log("calling toggole on editor");
46705         // tell the editor that it's been pressed..
46706         this.editor.toggleSourceEdit(sourceEditMode);
46707        
46708     },
46709      /**
46710      * Object collection of toolbar tooltips for the buttons in the editor. The key
46711      * is the command id associated with that button and the value is a valid QuickTips object.
46712      * For example:
46713 <pre><code>
46714 {
46715     bold : {
46716         title: 'Bold (Ctrl+B)',
46717         text: 'Make the selected text bold.',
46718         cls: 'x-html-editor-tip'
46719     },
46720     italic : {
46721         title: 'Italic (Ctrl+I)',
46722         text: 'Make the selected text italic.',
46723         cls: 'x-html-editor-tip'
46724     },
46725     ...
46726 </code></pre>
46727     * @type Object
46728      */
46729     buttonTips : {
46730         bold : {
46731             title: 'Bold (Ctrl+B)',
46732             text: 'Make the selected text bold.',
46733             cls: 'x-html-editor-tip'
46734         },
46735         italic : {
46736             title: 'Italic (Ctrl+I)',
46737             text: 'Make the selected text italic.',
46738             cls: 'x-html-editor-tip'
46739         },
46740         underline : {
46741             title: 'Underline (Ctrl+U)',
46742             text: 'Underline the selected text.',
46743             cls: 'x-html-editor-tip'
46744         },
46745         strikethrough : {
46746             title: 'Strikethrough',
46747             text: 'Strikethrough the selected text.',
46748             cls: 'x-html-editor-tip'
46749         },
46750         increasefontsize : {
46751             title: 'Grow Text',
46752             text: 'Increase the font size.',
46753             cls: 'x-html-editor-tip'
46754         },
46755         decreasefontsize : {
46756             title: 'Shrink Text',
46757             text: 'Decrease the font size.',
46758             cls: 'x-html-editor-tip'
46759         },
46760         backcolor : {
46761             title: 'Text Highlight Color',
46762             text: 'Change the background color of the selected text.',
46763             cls: 'x-html-editor-tip'
46764         },
46765         forecolor : {
46766             title: 'Font Color',
46767             text: 'Change the color of the selected text.',
46768             cls: 'x-html-editor-tip'
46769         },
46770         justifyleft : {
46771             title: 'Align Text Left',
46772             text: 'Align text to the left.',
46773             cls: 'x-html-editor-tip'
46774         },
46775         justifycenter : {
46776             title: 'Center Text',
46777             text: 'Center text in the editor.',
46778             cls: 'x-html-editor-tip'
46779         },
46780         justifyright : {
46781             title: 'Align Text Right',
46782             text: 'Align text to the right.',
46783             cls: 'x-html-editor-tip'
46784         },
46785         insertunorderedlist : {
46786             title: 'Bullet List',
46787             text: 'Start a bulleted list.',
46788             cls: 'x-html-editor-tip'
46789         },
46790         insertorderedlist : {
46791             title: 'Numbered List',
46792             text: 'Start a numbered list.',
46793             cls: 'x-html-editor-tip'
46794         },
46795         createlink : {
46796             title: 'Hyperlink',
46797             text: 'Make the selected text a hyperlink.',
46798             cls: 'x-html-editor-tip'
46799         },
46800         sourceedit : {
46801             title: 'Source Edit',
46802             text: 'Switch to source editing mode.',
46803             cls: 'x-html-editor-tip'
46804         }
46805     },
46806     // private
46807     onDestroy : function(){
46808         if(this.rendered){
46809             
46810             this.tb.items.each(function(item){
46811                 if(item.menu){
46812                     item.menu.removeAll();
46813                     if(item.menu.el){
46814                         item.menu.el.destroy();
46815                     }
46816                 }
46817                 item.destroy();
46818             });
46819              
46820         }
46821     },
46822     onFirstFocus: function() {
46823         this.tb.items.each(function(item){
46824            item.enable();
46825         });
46826     }
46827 });
46828
46829
46830
46831
46832 // <script type="text/javascript">
46833 /*
46834  * Based on
46835  * Ext JS Library 1.1.1
46836  * Copyright(c) 2006-2007, Ext JS, LLC.
46837  *  
46838  
46839  */
46840
46841  
46842 /**
46843  * @class Roo.form.HtmlEditor.ToolbarContext
46844  * Context Toolbar
46845  * 
46846  * Usage:
46847  *
46848  new Roo.form.HtmlEditor({
46849     ....
46850     toolbars : [
46851         { xtype: 'ToolbarStandard', styles : {} }
46852         { xtype: 'ToolbarContext', disable : {} }
46853     ]
46854 })
46855
46856      
46857  * 
46858  * @config : {Object} disable List of elements to disable.. (not done yet.)
46859  * @config : {Object} styles  Map of styles available.
46860  * 
46861  */
46862
46863 Roo.form.HtmlEditor.ToolbarContext = function(config)
46864 {
46865     
46866     Roo.apply(this, config);
46867     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46868     // dont call parent... till later.
46869     this.styles = this.styles || {};
46870 }
46871
46872  
46873
46874 Roo.form.HtmlEditor.ToolbarContext.types = {
46875     'IMG' : {
46876         width : {
46877             title: "Width",
46878             width: 40
46879         },
46880         height:  {
46881             title: "Height",
46882             width: 40
46883         },
46884         align: {
46885             title: "Align",
46886             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46887             width : 80
46888             
46889         },
46890         border: {
46891             title: "Border",
46892             width: 40
46893         },
46894         alt: {
46895             title: "Alt",
46896             width: 120
46897         },
46898         src : {
46899             title: "Src",
46900             width: 220
46901         }
46902         
46903     },
46904     'A' : {
46905         name : {
46906             title: "Name",
46907             width: 50
46908         },
46909         target:  {
46910             title: "Target",
46911             width: 120
46912         },
46913         href:  {
46914             title: "Href",
46915             width: 220
46916         } // border?
46917         
46918     },
46919     'TABLE' : {
46920         rows : {
46921             title: "Rows",
46922             width: 20
46923         },
46924         cols : {
46925             title: "Cols",
46926             width: 20
46927         },
46928         width : {
46929             title: "Width",
46930             width: 40
46931         },
46932         height : {
46933             title: "Height",
46934             width: 40
46935         },
46936         border : {
46937             title: "Border",
46938             width: 20
46939         }
46940     },
46941     'TD' : {
46942         width : {
46943             title: "Width",
46944             width: 40
46945         },
46946         height : {
46947             title: "Height",
46948             width: 40
46949         },   
46950         align: {
46951             title: "Align",
46952             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46953             width: 80
46954         },
46955         valign: {
46956             title: "Valign",
46957             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46958             width: 80
46959         },
46960         colspan: {
46961             title: "Colspan",
46962             width: 20
46963             
46964         },
46965          'font-family'  : {
46966             title : "Font",
46967             style : 'fontFamily',
46968             displayField: 'display',
46969             optname : 'font-family',
46970             width: 140
46971         }
46972     },
46973     'INPUT' : {
46974         name : {
46975             title: "name",
46976             width: 120
46977         },
46978         value : {
46979             title: "Value",
46980             width: 120
46981         },
46982         width : {
46983             title: "Width",
46984             width: 40
46985         }
46986     },
46987     'LABEL' : {
46988         'for' : {
46989             title: "For",
46990             width: 120
46991         }
46992     },
46993     'TEXTAREA' : {
46994           name : {
46995             title: "name",
46996             width: 120
46997         },
46998         rows : {
46999             title: "Rows",
47000             width: 20
47001         },
47002         cols : {
47003             title: "Cols",
47004             width: 20
47005         }
47006     },
47007     'SELECT' : {
47008         name : {
47009             title: "name",
47010             width: 120
47011         },
47012         selectoptions : {
47013             title: "Options",
47014             width: 200
47015         }
47016     },
47017     
47018     // should we really allow this??
47019     // should this just be 
47020     'BODY' : {
47021         title : {
47022             title: "Title",
47023             width: 200,
47024             disabled : true
47025         }
47026     },
47027     'SPAN' : {
47028         'font-family'  : {
47029             title : "Font",
47030             style : 'fontFamily',
47031             displayField: 'display',
47032             optname : 'font-family',
47033             width: 140
47034         }
47035     },
47036     'DIV' : {
47037         'font-family'  : {
47038             title : "Font",
47039             style : 'fontFamily',
47040             displayField: 'display',
47041             optname : 'font-family',
47042             width: 140
47043         }
47044     },
47045      'P' : {
47046         'font-family'  : {
47047             title : "Font",
47048             style : 'fontFamily',
47049             displayField: 'display',
47050             optname : 'font-family',
47051             width: 140
47052         }
47053     },
47054     
47055     '*' : {
47056         // empty..
47057     }
47058
47059 };
47060
47061 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47062 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47063
47064 Roo.form.HtmlEditor.ToolbarContext.options = {
47065         'font-family'  : [ 
47066                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47067                 [ 'Courier New', 'Courier New'],
47068                 [ 'Tahoma', 'Tahoma'],
47069                 [ 'Times New Roman,serif', 'Times'],
47070                 [ 'Verdana','Verdana' ]
47071         ]
47072 };
47073
47074 // fixme - these need to be configurable..
47075  
47076
47077 //Roo.form.HtmlEditor.ToolbarContext.types
47078
47079
47080 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47081     
47082     tb: false,
47083     
47084     rendered: false,
47085     
47086     editor : false,
47087     editorcore : false,
47088     /**
47089      * @cfg {Object} disable  List of toolbar elements to disable
47090          
47091      */
47092     disable : false,
47093     /**
47094      * @cfg {Object} styles List of styles 
47095      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47096      *
47097      * These must be defined in the page, so they get rendered correctly..
47098      * .headline { }
47099      * TD.underline { }
47100      * 
47101      */
47102     styles : false,
47103     
47104     options: false,
47105     
47106     toolbars : false,
47107     
47108     init : function(editor)
47109     {
47110         this.editor = editor;
47111         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47112         var editorcore = this.editorcore;
47113         
47114         var fid = editorcore.frameId;
47115         var etb = this;
47116         function btn(id, toggle, handler){
47117             var xid = fid + '-'+ id ;
47118             return {
47119                 id : xid,
47120                 cmd : id,
47121                 cls : 'x-btn-icon x-edit-'+id,
47122                 enableToggle:toggle !== false,
47123                 scope: editorcore, // was editor...
47124                 handler:handler||editorcore.relayBtnCmd,
47125                 clickEvent:'mousedown',
47126                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47127                 tabIndex:-1
47128             };
47129         }
47130         // create a new element.
47131         var wdiv = editor.wrap.createChild({
47132                 tag: 'div'
47133             }, editor.wrap.dom.firstChild.nextSibling, true);
47134         
47135         // can we do this more than once??
47136         
47137          // stop form submits
47138       
47139  
47140         // disable everything...
47141         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47142         this.toolbars = {};
47143            
47144         for (var i in  ty) {
47145           
47146             this.toolbars[i] = this.buildToolbar(ty[i],i);
47147         }
47148         this.tb = this.toolbars.BODY;
47149         this.tb.el.show();
47150         this.buildFooter();
47151         this.footer.show();
47152         editor.on('hide', function( ) { this.footer.hide() }, this);
47153         editor.on('show', function( ) { this.footer.show() }, this);
47154         
47155          
47156         this.rendered = true;
47157         
47158         // the all the btns;
47159         editor.on('editorevent', this.updateToolbar, this);
47160         // other toolbars need to implement this..
47161         //editor.on('editmodechange', this.updateToolbar, this);
47162     },
47163     
47164     
47165     
47166     /**
47167      * Protected method that will not generally be called directly. It triggers
47168      * a toolbar update by reading the markup state of the current selection in the editor.
47169      *
47170      * Note you can force an update by calling on('editorevent', scope, false)
47171      */
47172     updateToolbar: function(editor,ev,sel){
47173
47174         //Roo.log(ev);
47175         // capture mouse up - this is handy for selecting images..
47176         // perhaps should go somewhere else...
47177         if(!this.editorcore.activated){
47178              this.editor.onFirstFocus();
47179             return;
47180         }
47181         
47182         
47183         
47184         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47185         // selectNode - might want to handle IE?
47186         if (ev &&
47187             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47188             ev.target && ev.target.tagName == 'IMG') {
47189             // they have click on an image...
47190             // let's see if we can change the selection...
47191             sel = ev.target;
47192          
47193               var nodeRange = sel.ownerDocument.createRange();
47194             try {
47195                 nodeRange.selectNode(sel);
47196             } catch (e) {
47197                 nodeRange.selectNodeContents(sel);
47198             }
47199             //nodeRange.collapse(true);
47200             var s = this.editorcore.win.getSelection();
47201             s.removeAllRanges();
47202             s.addRange(nodeRange);
47203         }  
47204         
47205       
47206         var updateFooter = sel ? false : true;
47207         
47208         
47209         var ans = this.editorcore.getAllAncestors();
47210         
47211         // pick
47212         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47213         
47214         if (!sel) { 
47215             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47216             sel = sel ? sel : this.editorcore.doc.body;
47217             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47218             
47219         }
47220         // pick a menu that exists..
47221         var tn = sel.tagName.toUpperCase();
47222         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47223         
47224         tn = sel.tagName.toUpperCase();
47225         
47226         var lastSel = this.tb.selectedNode;
47227         
47228         this.tb.selectedNode = sel;
47229         
47230         // if current menu does not match..
47231         
47232         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47233                 
47234             this.tb.el.hide();
47235             ///console.log("show: " + tn);
47236             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47237             this.tb.el.show();
47238             // update name
47239             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47240             
47241             
47242             // update attributes
47243             if (this.tb.fields) {
47244                 this.tb.fields.each(function(e) {
47245                     if (e.stylename) {
47246                         e.setValue(sel.style[e.stylename]);
47247                         return;
47248                     } 
47249                    e.setValue(sel.getAttribute(e.attrname));
47250                 });
47251             }
47252             
47253             var hasStyles = false;
47254             for(var i in this.styles) {
47255                 hasStyles = true;
47256                 break;
47257             }
47258             
47259             // update styles
47260             if (hasStyles) { 
47261                 var st = this.tb.fields.item(0);
47262                 
47263                 st.store.removeAll();
47264                
47265                 
47266                 var cn = sel.className.split(/\s+/);
47267                 
47268                 var avs = [];
47269                 if (this.styles['*']) {
47270                     
47271                     Roo.each(this.styles['*'], function(v) {
47272                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47273                     });
47274                 }
47275                 if (this.styles[tn]) { 
47276                     Roo.each(this.styles[tn], function(v) {
47277                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47278                     });
47279                 }
47280                 
47281                 st.store.loadData(avs);
47282                 st.collapse();
47283                 st.setValue(cn);
47284             }
47285             // flag our selected Node.
47286             this.tb.selectedNode = sel;
47287            
47288            
47289             Roo.menu.MenuMgr.hideAll();
47290
47291         }
47292         
47293         if (!updateFooter) {
47294             //this.footDisp.dom.innerHTML = ''; 
47295             return;
47296         }
47297         // update the footer
47298         //
47299         var html = '';
47300         
47301         this.footerEls = ans.reverse();
47302         Roo.each(this.footerEls, function(a,i) {
47303             if (!a) { return; }
47304             html += html.length ? ' &gt; '  :  '';
47305             
47306             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47307             
47308         });
47309        
47310         // 
47311         var sz = this.footDisp.up('td').getSize();
47312         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47313         this.footDisp.dom.style.marginLeft = '5px';
47314         
47315         this.footDisp.dom.style.overflow = 'hidden';
47316         
47317         this.footDisp.dom.innerHTML = html;
47318             
47319         //this.editorsyncValue();
47320     },
47321      
47322     
47323    
47324        
47325     // private
47326     onDestroy : function(){
47327         if(this.rendered){
47328             
47329             this.tb.items.each(function(item){
47330                 if(item.menu){
47331                     item.menu.removeAll();
47332                     if(item.menu.el){
47333                         item.menu.el.destroy();
47334                     }
47335                 }
47336                 item.destroy();
47337             });
47338              
47339         }
47340     },
47341     onFirstFocus: function() {
47342         // need to do this for all the toolbars..
47343         this.tb.items.each(function(item){
47344            item.enable();
47345         });
47346     },
47347     buildToolbar: function(tlist, nm)
47348     {
47349         var editor = this.editor;
47350         var editorcore = this.editorcore;
47351          // create a new element.
47352         var wdiv = editor.wrap.createChild({
47353                 tag: 'div'
47354             }, editor.wrap.dom.firstChild.nextSibling, true);
47355         
47356        
47357         var tb = new Roo.Toolbar(wdiv);
47358         // add the name..
47359         
47360         tb.add(nm+ ":&nbsp;");
47361         
47362         var styles = [];
47363         for(var i in this.styles) {
47364             styles.push(i);
47365         }
47366         
47367         // styles...
47368         if (styles && styles.length) {
47369             
47370             // this needs a multi-select checkbox...
47371             tb.addField( new Roo.form.ComboBox({
47372                 store: new Roo.data.SimpleStore({
47373                     id : 'val',
47374                     fields: ['val', 'selected'],
47375                     data : [] 
47376                 }),
47377                 name : '-roo-edit-className',
47378                 attrname : 'className',
47379                 displayField: 'val',
47380                 typeAhead: false,
47381                 mode: 'local',
47382                 editable : false,
47383                 triggerAction: 'all',
47384                 emptyText:'Select Style',
47385                 selectOnFocus:true,
47386                 width: 130,
47387                 listeners : {
47388                     'select': function(c, r, i) {
47389                         // initial support only for on class per el..
47390                         tb.selectedNode.className =  r ? r.get('val') : '';
47391                         editorcore.syncValue();
47392                     }
47393                 }
47394     
47395             }));
47396         }
47397         
47398         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47399         var tbops = tbc.options;
47400         
47401         for (var i in tlist) {
47402             
47403             var item = tlist[i];
47404             tb.add(item.title + ":&nbsp;");
47405             
47406             
47407             //optname == used so you can configure the options available..
47408             var opts = item.opts ? item.opts : false;
47409             if (item.optname) {
47410                 opts = tbops[item.optname];
47411            
47412             }
47413             
47414             if (opts) {
47415                 // opts == pulldown..
47416                 tb.addField( new Roo.form.ComboBox({
47417                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47418                         id : 'val',
47419                         fields: ['val', 'display'],
47420                         data : opts  
47421                     }),
47422                     name : '-roo-edit-' + i,
47423                     attrname : i,
47424                     stylename : item.style ? item.style : false,
47425                     displayField: item.displayField ? item.displayField : 'val',
47426                     valueField :  'val',
47427                     typeAhead: false,
47428                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47429                     editable : false,
47430                     triggerAction: 'all',
47431                     emptyText:'Select',
47432                     selectOnFocus:true,
47433                     width: item.width ? item.width  : 130,
47434                     listeners : {
47435                         'select': function(c, r, i) {
47436                             if (c.stylename) {
47437                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47438                                 return;
47439                             }
47440                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47441                         }
47442                     }
47443
47444                 }));
47445                 continue;
47446                     
47447                  
47448                 
47449                 tb.addField( new Roo.form.TextField({
47450                     name: i,
47451                     width: 100,
47452                     //allowBlank:false,
47453                     value: ''
47454                 }));
47455                 continue;
47456             }
47457             tb.addField( new Roo.form.TextField({
47458                 name: '-roo-edit-' + i,
47459                 attrname : i,
47460                 
47461                 width: item.width,
47462                 //allowBlank:true,
47463                 value: '',
47464                 listeners: {
47465                     'change' : function(f, nv, ov) {
47466                         tb.selectedNode.setAttribute(f.attrname, nv);
47467                         editorcore.syncValue();
47468                     }
47469                 }
47470             }));
47471              
47472         }
47473         
47474         var _this = this;
47475         
47476         if(nm == 'BODY'){
47477             tb.addSeparator();
47478         
47479             tb.addButton( {
47480                 text: 'Stylesheets',
47481
47482                 listeners : {
47483                     click : function ()
47484                     {
47485                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47486                     }
47487                 }
47488             });
47489         }
47490         
47491         tb.addFill();
47492         tb.addButton( {
47493             text: 'Remove Tag',
47494     
47495             listeners : {
47496                 click : function ()
47497                 {
47498                     // remove
47499                     // undo does not work.
47500                      
47501                     var sn = tb.selectedNode;
47502                     
47503                     var pn = sn.parentNode;
47504                     
47505                     var stn =  sn.childNodes[0];
47506                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47507                     while (sn.childNodes.length) {
47508                         var node = sn.childNodes[0];
47509                         sn.removeChild(node);
47510                         //Roo.log(node);
47511                         pn.insertBefore(node, sn);
47512                         
47513                     }
47514                     pn.removeChild(sn);
47515                     var range = editorcore.createRange();
47516         
47517                     range.setStart(stn,0);
47518                     range.setEnd(en,0); //????
47519                     //range.selectNode(sel);
47520                     
47521                     
47522                     var selection = editorcore.getSelection();
47523                     selection.removeAllRanges();
47524                     selection.addRange(range);
47525                     
47526                     
47527                     
47528                     //_this.updateToolbar(null, null, pn);
47529                     _this.updateToolbar(null, null, null);
47530                     _this.footDisp.dom.innerHTML = ''; 
47531                 }
47532             }
47533             
47534                     
47535                 
47536             
47537         });
47538         
47539         
47540         tb.el.on('click', function(e){
47541             e.preventDefault(); // what does this do?
47542         });
47543         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47544         tb.el.hide();
47545         tb.name = nm;
47546         // dont need to disable them... as they will get hidden
47547         return tb;
47548          
47549         
47550     },
47551     buildFooter : function()
47552     {
47553         
47554         var fel = this.editor.wrap.createChild();
47555         this.footer = new Roo.Toolbar(fel);
47556         // toolbar has scrolly on left / right?
47557         var footDisp= new Roo.Toolbar.Fill();
47558         var _t = this;
47559         this.footer.add(
47560             {
47561                 text : '&lt;',
47562                 xtype: 'Button',
47563                 handler : function() {
47564                     _t.footDisp.scrollTo('left',0,true)
47565                 }
47566             }
47567         );
47568         this.footer.add( footDisp );
47569         this.footer.add( 
47570             {
47571                 text : '&gt;',
47572                 xtype: 'Button',
47573                 handler : function() {
47574                     // no animation..
47575                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47576                 }
47577             }
47578         );
47579         var fel = Roo.get(footDisp.el);
47580         fel.addClass('x-editor-context');
47581         this.footDispWrap = fel; 
47582         this.footDispWrap.overflow  = 'hidden';
47583         
47584         this.footDisp = fel.createChild();
47585         this.footDispWrap.on('click', this.onContextClick, this)
47586         
47587         
47588     },
47589     onContextClick : function (ev,dom)
47590     {
47591         ev.preventDefault();
47592         var  cn = dom.className;
47593         //Roo.log(cn);
47594         if (!cn.match(/x-ed-loc-/)) {
47595             return;
47596         }
47597         var n = cn.split('-').pop();
47598         var ans = this.footerEls;
47599         var sel = ans[n];
47600         
47601          // pick
47602         var range = this.editorcore.createRange();
47603         
47604         range.selectNodeContents(sel);
47605         //range.selectNode(sel);
47606         
47607         
47608         var selection = this.editorcore.getSelection();
47609         selection.removeAllRanges();
47610         selection.addRange(range);
47611         
47612         
47613         
47614         this.updateToolbar(null, null, sel);
47615         
47616         
47617     }
47618     
47619     
47620     
47621     
47622     
47623 });
47624
47625
47626
47627
47628
47629 /*
47630  * Based on:
47631  * Ext JS Library 1.1.1
47632  * Copyright(c) 2006-2007, Ext JS, LLC.
47633  *
47634  * Originally Released Under LGPL - original licence link has changed is not relivant.
47635  *
47636  * Fork - LGPL
47637  * <script type="text/javascript">
47638  */
47639  
47640 /**
47641  * @class Roo.form.BasicForm
47642  * @extends Roo.util.Observable
47643  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47644  * @constructor
47645  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47646  * @param {Object} config Configuration options
47647  */
47648 Roo.form.BasicForm = function(el, config){
47649     this.allItems = [];
47650     this.childForms = [];
47651     Roo.apply(this, config);
47652     /*
47653      * The Roo.form.Field items in this form.
47654      * @type MixedCollection
47655      */
47656      
47657      
47658     this.items = new Roo.util.MixedCollection(false, function(o){
47659         return o.id || (o.id = Roo.id());
47660     });
47661     this.addEvents({
47662         /**
47663          * @event beforeaction
47664          * Fires before any action is performed. Return false to cancel the action.
47665          * @param {Form} this
47666          * @param {Action} action The action to be performed
47667          */
47668         beforeaction: true,
47669         /**
47670          * @event actionfailed
47671          * Fires when an action fails.
47672          * @param {Form} this
47673          * @param {Action} action The action that failed
47674          */
47675         actionfailed : true,
47676         /**
47677          * @event actioncomplete
47678          * Fires when an action is completed.
47679          * @param {Form} this
47680          * @param {Action} action The action that completed
47681          */
47682         actioncomplete : true
47683     });
47684     if(el){
47685         this.initEl(el);
47686     }
47687     Roo.form.BasicForm.superclass.constructor.call(this);
47688     
47689     Roo.form.BasicForm.popover.apply();
47690 };
47691
47692 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47693     /**
47694      * @cfg {String} method
47695      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47696      */
47697     /**
47698      * @cfg {DataReader} reader
47699      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47700      * This is optional as there is built-in support for processing JSON.
47701      */
47702     /**
47703      * @cfg {DataReader} errorReader
47704      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47705      * This is completely optional as there is built-in support for processing JSON.
47706      */
47707     /**
47708      * @cfg {String} url
47709      * The URL to use for form actions if one isn't supplied in the action options.
47710      */
47711     /**
47712      * @cfg {Boolean} fileUpload
47713      * Set to true if this form is a file upload.
47714      */
47715      
47716     /**
47717      * @cfg {Object} baseParams
47718      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47719      */
47720      /**
47721      
47722     /**
47723      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47724      */
47725     timeout: 30,
47726
47727     // private
47728     activeAction : null,
47729
47730     /**
47731      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47732      * or setValues() data instead of when the form was first created.
47733      */
47734     trackResetOnLoad : false,
47735     
47736     
47737     /**
47738      * childForms - used for multi-tab forms
47739      * @type {Array}
47740      */
47741     childForms : false,
47742     
47743     /**
47744      * allItems - full list of fields.
47745      * @type {Array}
47746      */
47747     allItems : false,
47748     
47749     /**
47750      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47751      * element by passing it or its id or mask the form itself by passing in true.
47752      * @type Mixed
47753      */
47754     waitMsgTarget : false,
47755     
47756     /**
47757      * @type Boolean
47758      */
47759     disableMask : false,
47760     
47761     /**
47762      * @cfg {Boolean} errorMask (true|false) default false
47763      */
47764     errorMask : false,
47765     
47766     /**
47767      * @cfg {Number} maskOffset Default 100
47768      */
47769     maskOffset : 100,
47770
47771     // private
47772     initEl : function(el){
47773         this.el = Roo.get(el);
47774         this.id = this.el.id || Roo.id();
47775         this.el.on('submit', this.onSubmit, this);
47776         this.el.addClass('x-form');
47777     },
47778
47779     // private
47780     onSubmit : function(e){
47781         e.stopEvent();
47782     },
47783
47784     /**
47785      * Returns true if client-side validation on the form is successful.
47786      * @return Boolean
47787      */
47788     isValid : function(){
47789         var valid = true;
47790         var target = false;
47791         this.items.each(function(f){
47792             if(f.validate()){
47793                 return;
47794             }
47795             
47796             valid = false;
47797                 
47798             if(!target && f.el.isVisible(true)){
47799                 target = f;
47800             }
47801         });
47802         
47803         if(this.errorMask && !valid){
47804             Roo.form.BasicForm.popover.mask(this, target);
47805         }
47806         
47807         return valid;
47808     },
47809     /**
47810      * Returns array of invalid form fields.
47811      * @return Array
47812      */
47813     
47814     invalidFields : function()
47815     {
47816         var ret = [];
47817         this.items.each(function(f){
47818             if(f.validate()){
47819                 return;
47820             }
47821             ret.push(f);
47822             
47823         });
47824         
47825         return ret;
47826     },
47827     
47828     
47829     /**
47830      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47831      * @return Boolean
47832      */
47833     isDirty : function(){
47834         var dirty = false;
47835         this.items.each(function(f){
47836            if(f.isDirty()){
47837                dirty = true;
47838                return false;
47839            }
47840         });
47841         return dirty;
47842     },
47843     
47844     /**
47845      * Returns true if any fields in this form have changed since their original load. (New version)
47846      * @return Boolean
47847      */
47848     
47849     hasChanged : function()
47850     {
47851         var dirty = false;
47852         this.items.each(function(f){
47853            if(f.hasChanged()){
47854                dirty = true;
47855                return false;
47856            }
47857         });
47858         return dirty;
47859         
47860     },
47861     /**
47862      * Resets all hasChanged to 'false' -
47863      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47864      * So hasChanged storage is only to be used for this purpose
47865      * @return Boolean
47866      */
47867     resetHasChanged : function()
47868     {
47869         this.items.each(function(f){
47870            f.resetHasChanged();
47871         });
47872         
47873     },
47874     
47875     
47876     /**
47877      * Performs a predefined action (submit or load) or custom actions you define on this form.
47878      * @param {String} actionName The name of the action type
47879      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47880      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47881      * accept other config options):
47882      * <pre>
47883 Property          Type             Description
47884 ----------------  ---------------  ----------------------------------------------------------------------------------
47885 url               String           The url for the action (defaults to the form's url)
47886 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47887 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47888 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47889                                    validate the form on the client (defaults to false)
47890      * </pre>
47891      * @return {BasicForm} this
47892      */
47893     doAction : function(action, options){
47894         if(typeof action == 'string'){
47895             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47896         }
47897         if(this.fireEvent('beforeaction', this, action) !== false){
47898             this.beforeAction(action);
47899             action.run.defer(100, action);
47900         }
47901         return this;
47902     },
47903
47904     /**
47905      * Shortcut to do a submit action.
47906      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47907      * @return {BasicForm} this
47908      */
47909     submit : function(options){
47910         this.doAction('submit', options);
47911         return this;
47912     },
47913
47914     /**
47915      * Shortcut to do a load action.
47916      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47917      * @return {BasicForm} this
47918      */
47919     load : function(options){
47920         this.doAction('load', options);
47921         return this;
47922     },
47923
47924     /**
47925      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47926      * @param {Record} record The record to edit
47927      * @return {BasicForm} this
47928      */
47929     updateRecord : function(record){
47930         record.beginEdit();
47931         var fs = record.fields;
47932         fs.each(function(f){
47933             var field = this.findField(f.name);
47934             if(field){
47935                 record.set(f.name, field.getValue());
47936             }
47937         }, this);
47938         record.endEdit();
47939         return this;
47940     },
47941
47942     /**
47943      * Loads an Roo.data.Record into this form.
47944      * @param {Record} record The record to load
47945      * @return {BasicForm} this
47946      */
47947     loadRecord : function(record){
47948         this.setValues(record.data);
47949         return this;
47950     },
47951
47952     // private
47953     beforeAction : function(action){
47954         var o = action.options;
47955         
47956         if(!this.disableMask) {
47957             if(this.waitMsgTarget === true){
47958                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47959             }else if(this.waitMsgTarget){
47960                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47961                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47962             }else {
47963                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47964             }
47965         }
47966         
47967          
47968     },
47969
47970     // private
47971     afterAction : function(action, success){
47972         this.activeAction = null;
47973         var o = action.options;
47974         
47975         if(!this.disableMask) {
47976             if(this.waitMsgTarget === true){
47977                 this.el.unmask();
47978             }else if(this.waitMsgTarget){
47979                 this.waitMsgTarget.unmask();
47980             }else{
47981                 Roo.MessageBox.updateProgress(1);
47982                 Roo.MessageBox.hide();
47983             }
47984         }
47985         
47986         if(success){
47987             if(o.reset){
47988                 this.reset();
47989             }
47990             Roo.callback(o.success, o.scope, [this, action]);
47991             this.fireEvent('actioncomplete', this, action);
47992             
47993         }else{
47994             
47995             // failure condition..
47996             // we have a scenario where updates need confirming.
47997             // eg. if a locking scenario exists..
47998             // we look for { errors : { needs_confirm : true }} in the response.
47999             if (
48000                 (typeof(action.result) != 'undefined')  &&
48001                 (typeof(action.result.errors) != 'undefined')  &&
48002                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48003            ){
48004                 var _t = this;
48005                 Roo.MessageBox.confirm(
48006                     "Change requires confirmation",
48007                     action.result.errorMsg,
48008                     function(r) {
48009                         if (r != 'yes') {
48010                             return;
48011                         }
48012                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48013                     }
48014                     
48015                 );
48016                 
48017                 
48018                 
48019                 return;
48020             }
48021             
48022             Roo.callback(o.failure, o.scope, [this, action]);
48023             // show an error message if no failed handler is set..
48024             if (!this.hasListener('actionfailed')) {
48025                 Roo.MessageBox.alert("Error",
48026                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48027                         action.result.errorMsg :
48028                         "Saving Failed, please check your entries or try again"
48029                 );
48030             }
48031             
48032             this.fireEvent('actionfailed', this, action);
48033         }
48034         
48035     },
48036
48037     /**
48038      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48039      * @param {String} id The value to search for
48040      * @return Field
48041      */
48042     findField : function(id){
48043         var field = this.items.get(id);
48044         if(!field){
48045             this.items.each(function(f){
48046                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48047                     field = f;
48048                     return false;
48049                 }
48050             });
48051         }
48052         return field || null;
48053     },
48054
48055     /**
48056      * Add a secondary form to this one, 
48057      * Used to provide tabbed forms. One form is primary, with hidden values 
48058      * which mirror the elements from the other forms.
48059      * 
48060      * @param {Roo.form.Form} form to add.
48061      * 
48062      */
48063     addForm : function(form)
48064     {
48065        
48066         if (this.childForms.indexOf(form) > -1) {
48067             // already added..
48068             return;
48069         }
48070         this.childForms.push(form);
48071         var n = '';
48072         Roo.each(form.allItems, function (fe) {
48073             
48074             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48075             if (this.findField(n)) { // already added..
48076                 return;
48077             }
48078             var add = new Roo.form.Hidden({
48079                 name : n
48080             });
48081             add.render(this.el);
48082             
48083             this.add( add );
48084         }, this);
48085         
48086     },
48087     /**
48088      * Mark fields in this form invalid in bulk.
48089      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48090      * @return {BasicForm} this
48091      */
48092     markInvalid : function(errors){
48093         if(errors instanceof Array){
48094             for(var i = 0, len = errors.length; i < len; i++){
48095                 var fieldError = errors[i];
48096                 var f = this.findField(fieldError.id);
48097                 if(f){
48098                     f.markInvalid(fieldError.msg);
48099                 }
48100             }
48101         }else{
48102             var field, id;
48103             for(id in errors){
48104                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48105                     field.markInvalid(errors[id]);
48106                 }
48107             }
48108         }
48109         Roo.each(this.childForms || [], function (f) {
48110             f.markInvalid(errors);
48111         });
48112         
48113         return this;
48114     },
48115
48116     /**
48117      * Set values for fields in this form in bulk.
48118      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48119      * @return {BasicForm} this
48120      */
48121     setValues : function(values){
48122         if(values instanceof Array){ // array of objects
48123             for(var i = 0, len = values.length; i < len; i++){
48124                 var v = values[i];
48125                 var f = this.findField(v.id);
48126                 if(f){
48127                     f.setValue(v.value);
48128                     if(this.trackResetOnLoad){
48129                         f.originalValue = f.getValue();
48130                     }
48131                 }
48132             }
48133         }else{ // object hash
48134             var field, id;
48135             for(id in values){
48136                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48137                     
48138                     if (field.setFromData && 
48139                         field.valueField && 
48140                         field.displayField &&
48141                         // combos' with local stores can 
48142                         // be queried via setValue()
48143                         // to set their value..
48144                         (field.store && !field.store.isLocal)
48145                         ) {
48146                         // it's a combo
48147                         var sd = { };
48148                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48149                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48150                         field.setFromData(sd);
48151                         
48152                     } else {
48153                         field.setValue(values[id]);
48154                     }
48155                     
48156                     
48157                     if(this.trackResetOnLoad){
48158                         field.originalValue = field.getValue();
48159                     }
48160                 }
48161             }
48162         }
48163         this.resetHasChanged();
48164         
48165         
48166         Roo.each(this.childForms || [], function (f) {
48167             f.setValues(values);
48168             f.resetHasChanged();
48169         });
48170                 
48171         return this;
48172     },
48173  
48174     /**
48175      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48176      * they are returned as an array.
48177      * @param {Boolean} asString
48178      * @return {Object}
48179      */
48180     getValues : function(asString){
48181         if (this.childForms) {
48182             // copy values from the child forms
48183             Roo.each(this.childForms, function (f) {
48184                 this.setValues(f.getValues());
48185             }, this);
48186         }
48187         
48188         // use formdata
48189         if (typeof(FormData) != 'undefined' && asString !== true) {
48190             // this relies on a 'recent' version of chrome apparently...
48191             try {
48192                 var fd = (new FormData(this.el.dom)).entries();
48193                 var ret = {};
48194                 var ent = fd.next();
48195                 while (!ent.done) {
48196                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48197                     ent = fd.next();
48198                 };
48199                 return ret;
48200             } catch(e) {
48201                 
48202             }
48203             
48204         }
48205         
48206         
48207         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48208         if(asString === true){
48209             return fs;
48210         }
48211         return Roo.urlDecode(fs);
48212     },
48213     
48214     /**
48215      * Returns the fields in this form as an object with key/value pairs. 
48216      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48217      * @return {Object}
48218      */
48219     getFieldValues : function(with_hidden)
48220     {
48221         if (this.childForms) {
48222             // copy values from the child forms
48223             // should this call getFieldValues - probably not as we do not currently copy
48224             // hidden fields when we generate..
48225             Roo.each(this.childForms, function (f) {
48226                 this.setValues(f.getValues());
48227             }, this);
48228         }
48229         
48230         var ret = {};
48231         this.items.each(function(f){
48232             if (!f.getName()) {
48233                 return;
48234             }
48235             var v = f.getValue();
48236             if (f.inputType =='radio') {
48237                 if (typeof(ret[f.getName()]) == 'undefined') {
48238                     ret[f.getName()] = ''; // empty..
48239                 }
48240                 
48241                 if (!f.el.dom.checked) {
48242                     return;
48243                     
48244                 }
48245                 v = f.el.dom.value;
48246                 
48247             }
48248             
48249             // not sure if this supported any more..
48250             if ((typeof(v) == 'object') && f.getRawValue) {
48251                 v = f.getRawValue() ; // dates..
48252             }
48253             // combo boxes where name != hiddenName...
48254             if (f.name != f.getName()) {
48255                 ret[f.name] = f.getRawValue();
48256             }
48257             ret[f.getName()] = v;
48258         });
48259         
48260         return ret;
48261     },
48262
48263     /**
48264      * Clears all invalid messages in this form.
48265      * @return {BasicForm} this
48266      */
48267     clearInvalid : function(){
48268         this.items.each(function(f){
48269            f.clearInvalid();
48270         });
48271         
48272         Roo.each(this.childForms || [], function (f) {
48273             f.clearInvalid();
48274         });
48275         
48276         
48277         return this;
48278     },
48279
48280     /**
48281      * Resets this form.
48282      * @return {BasicForm} this
48283      */
48284     reset : function(){
48285         this.items.each(function(f){
48286             f.reset();
48287         });
48288         
48289         Roo.each(this.childForms || [], function (f) {
48290             f.reset();
48291         });
48292         this.resetHasChanged();
48293         
48294         return this;
48295     },
48296
48297     /**
48298      * Add Roo.form components to this form.
48299      * @param {Field} field1
48300      * @param {Field} field2 (optional)
48301      * @param {Field} etc (optional)
48302      * @return {BasicForm} this
48303      */
48304     add : function(){
48305         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48306         return this;
48307     },
48308
48309
48310     /**
48311      * Removes a field from the items collection (does NOT remove its markup).
48312      * @param {Field} field
48313      * @return {BasicForm} this
48314      */
48315     remove : function(field){
48316         this.items.remove(field);
48317         return this;
48318     },
48319
48320     /**
48321      * Looks at the fields in this form, checks them for an id attribute,
48322      * and calls applyTo on the existing dom element with that id.
48323      * @return {BasicForm} this
48324      */
48325     render : function(){
48326         this.items.each(function(f){
48327             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48328                 f.applyTo(f.id);
48329             }
48330         });
48331         return this;
48332     },
48333
48334     /**
48335      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48336      * @param {Object} values
48337      * @return {BasicForm} this
48338      */
48339     applyToFields : function(o){
48340         this.items.each(function(f){
48341            Roo.apply(f, o);
48342         });
48343         return this;
48344     },
48345
48346     /**
48347      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48348      * @param {Object} values
48349      * @return {BasicForm} this
48350      */
48351     applyIfToFields : function(o){
48352         this.items.each(function(f){
48353            Roo.applyIf(f, o);
48354         });
48355         return this;
48356     }
48357 });
48358
48359 // back compat
48360 Roo.BasicForm = Roo.form.BasicForm;
48361
48362 Roo.apply(Roo.form.BasicForm, {
48363     
48364     popover : {
48365         
48366         padding : 5,
48367         
48368         isApplied : false,
48369         
48370         isMasked : false,
48371         
48372         form : false,
48373         
48374         target : false,
48375         
48376         intervalID : false,
48377         
48378         maskEl : false,
48379         
48380         apply : function()
48381         {
48382             if(this.isApplied){
48383                 return;
48384             }
48385             
48386             this.maskEl = {
48387                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48388                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48389                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48390                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48391             };
48392             
48393             this.maskEl.top.enableDisplayMode("block");
48394             this.maskEl.left.enableDisplayMode("block");
48395             this.maskEl.bottom.enableDisplayMode("block");
48396             this.maskEl.right.enableDisplayMode("block");
48397             
48398             Roo.get(document.body).on('click', function(){
48399                 this.unmask();
48400             }, this);
48401             
48402             Roo.get(document.body).on('touchstart', function(){
48403                 this.unmask();
48404             }, this);
48405             
48406             this.isApplied = true
48407         },
48408         
48409         mask : function(form, target)
48410         {
48411             this.form = form;
48412             
48413             this.target = target;
48414             
48415             if(!this.form.errorMask || !target.el){
48416                 return;
48417             }
48418             
48419             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48420             
48421             var ot = this.target.el.calcOffsetsTo(scrollable);
48422             
48423             var scrollTo = ot[1] - this.form.maskOffset;
48424             
48425             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48426             
48427             scrollable.scrollTo('top', scrollTo);
48428             
48429             var el = this.target.wrap || this.target.el;
48430             
48431             var box = el.getBox();
48432             
48433             this.maskEl.top.setStyle('position', 'absolute');
48434             this.maskEl.top.setStyle('z-index', 10000);
48435             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48436             this.maskEl.top.setLeft(0);
48437             this.maskEl.top.setTop(0);
48438             this.maskEl.top.show();
48439             
48440             this.maskEl.left.setStyle('position', 'absolute');
48441             this.maskEl.left.setStyle('z-index', 10000);
48442             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48443             this.maskEl.left.setLeft(0);
48444             this.maskEl.left.setTop(box.y - this.padding);
48445             this.maskEl.left.show();
48446
48447             this.maskEl.bottom.setStyle('position', 'absolute');
48448             this.maskEl.bottom.setStyle('z-index', 10000);
48449             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48450             this.maskEl.bottom.setLeft(0);
48451             this.maskEl.bottom.setTop(box.bottom + this.padding);
48452             this.maskEl.bottom.show();
48453
48454             this.maskEl.right.setStyle('position', 'absolute');
48455             this.maskEl.right.setStyle('z-index', 10000);
48456             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48457             this.maskEl.right.setLeft(box.right + this.padding);
48458             this.maskEl.right.setTop(box.y - this.padding);
48459             this.maskEl.right.show();
48460
48461             this.intervalID = window.setInterval(function() {
48462                 Roo.form.BasicForm.popover.unmask();
48463             }, 10000);
48464
48465             window.onwheel = function(){ return false;};
48466             
48467             (function(){ this.isMasked = true; }).defer(500, this);
48468             
48469         },
48470         
48471         unmask : function()
48472         {
48473             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48474                 return;
48475             }
48476             
48477             this.maskEl.top.setStyle('position', 'absolute');
48478             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48479             this.maskEl.top.hide();
48480
48481             this.maskEl.left.setStyle('position', 'absolute');
48482             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48483             this.maskEl.left.hide();
48484
48485             this.maskEl.bottom.setStyle('position', 'absolute');
48486             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48487             this.maskEl.bottom.hide();
48488
48489             this.maskEl.right.setStyle('position', 'absolute');
48490             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48491             this.maskEl.right.hide();
48492             
48493             window.onwheel = function(){ return true;};
48494             
48495             if(this.intervalID){
48496                 window.clearInterval(this.intervalID);
48497                 this.intervalID = false;
48498             }
48499             
48500             this.isMasked = false;
48501             
48502         }
48503         
48504     }
48505     
48506 });/*
48507  * Based on:
48508  * Ext JS Library 1.1.1
48509  * Copyright(c) 2006-2007, Ext JS, LLC.
48510  *
48511  * Originally Released Under LGPL - original licence link has changed is not relivant.
48512  *
48513  * Fork - LGPL
48514  * <script type="text/javascript">
48515  */
48516
48517 /**
48518  * @class Roo.form.Form
48519  * @extends Roo.form.BasicForm
48520  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
48521  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48522  * @constructor
48523  * @param {Object} config Configuration options
48524  */
48525 Roo.form.Form = function(config){
48526     var xitems =  [];
48527     if (config.items) {
48528         xitems = config.items;
48529         delete config.items;
48530     }
48531    
48532     
48533     Roo.form.Form.superclass.constructor.call(this, null, config);
48534     this.url = this.url || this.action;
48535     if(!this.root){
48536         this.root = new Roo.form.Layout(Roo.applyIf({
48537             id: Roo.id()
48538         }, config));
48539     }
48540     this.active = this.root;
48541     /**
48542      * Array of all the buttons that have been added to this form via {@link addButton}
48543      * @type Array
48544      */
48545     this.buttons = [];
48546     this.allItems = [];
48547     this.addEvents({
48548         /**
48549          * @event clientvalidation
48550          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48551          * @param {Form} this
48552          * @param {Boolean} valid true if the form has passed client-side validation
48553          */
48554         clientvalidation: true,
48555         /**
48556          * @event rendered
48557          * Fires when the form is rendered
48558          * @param {Roo.form.Form} form
48559          */
48560         rendered : true
48561     });
48562     
48563     if (this.progressUrl) {
48564             // push a hidden field onto the list of fields..
48565             this.addxtype( {
48566                     xns: Roo.form, 
48567                     xtype : 'Hidden', 
48568                     name : 'UPLOAD_IDENTIFIER' 
48569             });
48570         }
48571         
48572     
48573     Roo.each(xitems, this.addxtype, this);
48574     
48575 };
48576
48577 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48578      /**
48579      * @cfg {Roo.Button} buttons[] buttons at bottom of form
48580      */
48581     
48582     /**
48583      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48584      */
48585     /**
48586      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48587      */
48588     /**
48589      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48590      */
48591     buttonAlign:'center',
48592
48593     /**
48594      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48595      */
48596     minButtonWidth:75,
48597
48598     /**
48599      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48600      * This property cascades to child containers if not set.
48601      */
48602     labelAlign:'left',
48603
48604     /**
48605      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48606      * fires a looping event with that state. This is required to bind buttons to the valid
48607      * state using the config value formBind:true on the button.
48608      */
48609     monitorValid : false,
48610
48611     /**
48612      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48613      */
48614     monitorPoll : 200,
48615     
48616     /**
48617      * @cfg {String} progressUrl - Url to return progress data 
48618      */
48619     
48620     progressUrl : false,
48621     /**
48622      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48623      * sending a formdata with extra parameters - eg uploaded elements.
48624      */
48625     
48626     formData : false,
48627     
48628     /**
48629      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48630      * fields are added and the column is closed. If no fields are passed the column remains open
48631      * until end() is called.
48632      * @param {Object} config The config to pass to the column
48633      * @param {Field} field1 (optional)
48634      * @param {Field} field2 (optional)
48635      * @param {Field} etc (optional)
48636      * @return Column The column container object
48637      */
48638     column : function(c){
48639         var col = new Roo.form.Column(c);
48640         this.start(col);
48641         if(arguments.length > 1){ // duplicate code required because of Opera
48642             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48643             this.end();
48644         }
48645         return col;
48646     },
48647
48648     /**
48649      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48650      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48651      * until end() is called.
48652      * @param {Object} config The config to pass to the fieldset
48653      * @param {Field} field1 (optional)
48654      * @param {Field} field2 (optional)
48655      * @param {Field} etc (optional)
48656      * @return FieldSet The fieldset container object
48657      */
48658     fieldset : function(c){
48659         var fs = new Roo.form.FieldSet(c);
48660         this.start(fs);
48661         if(arguments.length > 1){ // duplicate code required because of Opera
48662             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48663             this.end();
48664         }
48665         return fs;
48666     },
48667
48668     /**
48669      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48670      * fields are added and the container is closed. If no fields are passed the container remains open
48671      * until end() is called.
48672      * @param {Object} config The config to pass to the Layout
48673      * @param {Field} field1 (optional)
48674      * @param {Field} field2 (optional)
48675      * @param {Field} etc (optional)
48676      * @return Layout The container object
48677      */
48678     container : function(c){
48679         var l = new Roo.form.Layout(c);
48680         this.start(l);
48681         if(arguments.length > 1){ // duplicate code required because of Opera
48682             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48683             this.end();
48684         }
48685         return l;
48686     },
48687
48688     /**
48689      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48690      * @param {Object} container A Roo.form.Layout or subclass of Layout
48691      * @return {Form} this
48692      */
48693     start : function(c){
48694         // cascade label info
48695         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48696         this.active.stack.push(c);
48697         c.ownerCt = this.active;
48698         this.active = c;
48699         return this;
48700     },
48701
48702     /**
48703      * Closes the current open container
48704      * @return {Form} this
48705      */
48706     end : function(){
48707         if(this.active == this.root){
48708             return this;
48709         }
48710         this.active = this.active.ownerCt;
48711         return this;
48712     },
48713
48714     /**
48715      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48716      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48717      * as the label of the field.
48718      * @param {Field} field1
48719      * @param {Field} field2 (optional)
48720      * @param {Field} etc. (optional)
48721      * @return {Form} this
48722      */
48723     add : function(){
48724         this.active.stack.push.apply(this.active.stack, arguments);
48725         this.allItems.push.apply(this.allItems,arguments);
48726         var r = [];
48727         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48728             if(a[i].isFormField){
48729                 r.push(a[i]);
48730             }
48731         }
48732         if(r.length > 0){
48733             Roo.form.Form.superclass.add.apply(this, r);
48734         }
48735         return this;
48736     },
48737     
48738
48739     
48740     
48741     
48742      /**
48743      * Find any element that has been added to a form, using it's ID or name
48744      * This can include framesets, columns etc. along with regular fields..
48745      * @param {String} id - id or name to find.
48746      
48747      * @return {Element} e - or false if nothing found.
48748      */
48749     findbyId : function(id)
48750     {
48751         var ret = false;
48752         if (!id) {
48753             return ret;
48754         }
48755         Roo.each(this.allItems, function(f){
48756             if (f.id == id || f.name == id ){
48757                 ret = f;
48758                 return false;
48759             }
48760         });
48761         return ret;
48762     },
48763
48764     
48765     
48766     /**
48767      * Render this form into the passed container. This should only be called once!
48768      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48769      * @return {Form} this
48770      */
48771     render : function(ct)
48772     {
48773         
48774         
48775         
48776         ct = Roo.get(ct);
48777         var o = this.autoCreate || {
48778             tag: 'form',
48779             method : this.method || 'POST',
48780             id : this.id || Roo.id()
48781         };
48782         this.initEl(ct.createChild(o));
48783
48784         this.root.render(this.el);
48785         
48786        
48787              
48788         this.items.each(function(f){
48789             f.render('x-form-el-'+f.id);
48790         });
48791
48792         if(this.buttons.length > 0){
48793             // tables are required to maintain order and for correct IE layout
48794             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48795                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48796                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48797             }}, null, true);
48798             var tr = tb.getElementsByTagName('tr')[0];
48799             for(var i = 0, len = this.buttons.length; i < len; i++) {
48800                 var b = this.buttons[i];
48801                 var td = document.createElement('td');
48802                 td.className = 'x-form-btn-td';
48803                 b.render(tr.appendChild(td));
48804             }
48805         }
48806         if(this.monitorValid){ // initialize after render
48807             this.startMonitoring();
48808         }
48809         this.fireEvent('rendered', this);
48810         return this;
48811     },
48812
48813     /**
48814      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48815      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48816      * object or a valid Roo.DomHelper element config
48817      * @param {Function} handler The function called when the button is clicked
48818      * @param {Object} scope (optional) The scope of the handler function
48819      * @return {Roo.Button}
48820      */
48821     addButton : function(config, handler, scope){
48822         var bc = {
48823             handler: handler,
48824             scope: scope,
48825             minWidth: this.minButtonWidth,
48826             hideParent:true
48827         };
48828         if(typeof config == "string"){
48829             bc.text = config;
48830         }else{
48831             Roo.apply(bc, config);
48832         }
48833         var btn = new Roo.Button(null, bc);
48834         this.buttons.push(btn);
48835         return btn;
48836     },
48837
48838      /**
48839      * Adds a series of form elements (using the xtype property as the factory method.
48840      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48841      * @param {Object} config 
48842      */
48843     
48844     addxtype : function()
48845     {
48846         var ar = Array.prototype.slice.call(arguments, 0);
48847         var ret = false;
48848         for(var i = 0; i < ar.length; i++) {
48849             if (!ar[i]) {
48850                 continue; // skip -- if this happends something invalid got sent, we 
48851                 // should ignore it, as basically that interface element will not show up
48852                 // and that should be pretty obvious!!
48853             }
48854             
48855             if (Roo.form[ar[i].xtype]) {
48856                 ar[i].form = this;
48857                 var fe = Roo.factory(ar[i], Roo.form);
48858                 if (!ret) {
48859                     ret = fe;
48860                 }
48861                 fe.form = this;
48862                 if (fe.store) {
48863                     fe.store.form = this;
48864                 }
48865                 if (fe.isLayout) {  
48866                          
48867                     this.start(fe);
48868                     this.allItems.push(fe);
48869                     if (fe.items && fe.addxtype) {
48870                         fe.addxtype.apply(fe, fe.items);
48871                         delete fe.items;
48872                     }
48873                      this.end();
48874                     continue;
48875                 }
48876                 
48877                 
48878                  
48879                 this.add(fe);
48880               //  console.log('adding ' + ar[i].xtype);
48881             }
48882             if (ar[i].xtype == 'Button') {  
48883                 //console.log('adding button');
48884                 //console.log(ar[i]);
48885                 this.addButton(ar[i]);
48886                 this.allItems.push(fe);
48887                 continue;
48888             }
48889             
48890             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48891                 alert('end is not supported on xtype any more, use items');
48892             //    this.end();
48893             //    //console.log('adding end');
48894             }
48895             
48896         }
48897         return ret;
48898     },
48899     
48900     /**
48901      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48902      * option "monitorValid"
48903      */
48904     startMonitoring : function(){
48905         if(!this.bound){
48906             this.bound = true;
48907             Roo.TaskMgr.start({
48908                 run : this.bindHandler,
48909                 interval : this.monitorPoll || 200,
48910                 scope: this
48911             });
48912         }
48913     },
48914
48915     /**
48916      * Stops monitoring of the valid state of this form
48917      */
48918     stopMonitoring : function(){
48919         this.bound = false;
48920     },
48921
48922     // private
48923     bindHandler : function(){
48924         if(!this.bound){
48925             return false; // stops binding
48926         }
48927         var valid = true;
48928         this.items.each(function(f){
48929             if(!f.isValid(true)){
48930                 valid = false;
48931                 return false;
48932             }
48933         });
48934         for(var i = 0, len = this.buttons.length; i < len; i++){
48935             var btn = this.buttons[i];
48936             if(btn.formBind === true && btn.disabled === valid){
48937                 btn.setDisabled(!valid);
48938             }
48939         }
48940         this.fireEvent('clientvalidation', this, valid);
48941     }
48942     
48943     
48944     
48945     
48946     
48947     
48948     
48949     
48950 });
48951
48952
48953 // back compat
48954 Roo.Form = Roo.form.Form;
48955 /*
48956  * Based on:
48957  * Ext JS Library 1.1.1
48958  * Copyright(c) 2006-2007, Ext JS, LLC.
48959  *
48960  * Originally Released Under LGPL - original licence link has changed is not relivant.
48961  *
48962  * Fork - LGPL
48963  * <script type="text/javascript">
48964  */
48965
48966 // as we use this in bootstrap.
48967 Roo.namespace('Roo.form');
48968  /**
48969  * @class Roo.form.Action
48970  * Internal Class used to handle form actions
48971  * @constructor
48972  * @param {Roo.form.BasicForm} el The form element or its id
48973  * @param {Object} config Configuration options
48974  */
48975
48976  
48977  
48978 // define the action interface
48979 Roo.form.Action = function(form, options){
48980     this.form = form;
48981     this.options = options || {};
48982 };
48983 /**
48984  * Client Validation Failed
48985  * @const 
48986  */
48987 Roo.form.Action.CLIENT_INVALID = 'client';
48988 /**
48989  * Server Validation Failed
48990  * @const 
48991  */
48992 Roo.form.Action.SERVER_INVALID = 'server';
48993  /**
48994  * Connect to Server Failed
48995  * @const 
48996  */
48997 Roo.form.Action.CONNECT_FAILURE = 'connect';
48998 /**
48999  * Reading Data from Server Failed
49000  * @const 
49001  */
49002 Roo.form.Action.LOAD_FAILURE = 'load';
49003
49004 Roo.form.Action.prototype = {
49005     type : 'default',
49006     failureType : undefined,
49007     response : undefined,
49008     result : undefined,
49009
49010     // interface method
49011     run : function(options){
49012
49013     },
49014
49015     // interface method
49016     success : function(response){
49017
49018     },
49019
49020     // interface method
49021     handleResponse : function(response){
49022
49023     },
49024
49025     // default connection failure
49026     failure : function(response){
49027         
49028         this.response = response;
49029         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49030         this.form.afterAction(this, false);
49031     },
49032
49033     processResponse : function(response){
49034         this.response = response;
49035         if(!response.responseText){
49036             return true;
49037         }
49038         this.result = this.handleResponse(response);
49039         return this.result;
49040     },
49041
49042     // utility functions used internally
49043     getUrl : function(appendParams){
49044         var url = this.options.url || this.form.url || this.form.el.dom.action;
49045         if(appendParams){
49046             var p = this.getParams();
49047             if(p){
49048                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49049             }
49050         }
49051         return url;
49052     },
49053
49054     getMethod : function(){
49055         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49056     },
49057
49058     getParams : function(){
49059         var bp = this.form.baseParams;
49060         var p = this.options.params;
49061         if(p){
49062             if(typeof p == "object"){
49063                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49064             }else if(typeof p == 'string' && bp){
49065                 p += '&' + Roo.urlEncode(bp);
49066             }
49067         }else if(bp){
49068             p = Roo.urlEncode(bp);
49069         }
49070         return p;
49071     },
49072
49073     createCallback : function(){
49074         return {
49075             success: this.success,
49076             failure: this.failure,
49077             scope: this,
49078             timeout: (this.form.timeout*1000),
49079             upload: this.form.fileUpload ? this.success : undefined
49080         };
49081     }
49082 };
49083
49084 Roo.form.Action.Submit = function(form, options){
49085     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49086 };
49087
49088 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49089     type : 'submit',
49090
49091     haveProgress : false,
49092     uploadComplete : false,
49093     
49094     // uploadProgress indicator.
49095     uploadProgress : function()
49096     {
49097         if (!this.form.progressUrl) {
49098             return;
49099         }
49100         
49101         if (!this.haveProgress) {
49102             Roo.MessageBox.progress("Uploading", "Uploading");
49103         }
49104         if (this.uploadComplete) {
49105            Roo.MessageBox.hide();
49106            return;
49107         }
49108         
49109         this.haveProgress = true;
49110    
49111         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49112         
49113         var c = new Roo.data.Connection();
49114         c.request({
49115             url : this.form.progressUrl,
49116             params: {
49117                 id : uid
49118             },
49119             method: 'GET',
49120             success : function(req){
49121                //console.log(data);
49122                 var rdata = false;
49123                 var edata;
49124                 try  {
49125                    rdata = Roo.decode(req.responseText)
49126                 } catch (e) {
49127                     Roo.log("Invalid data from server..");
49128                     Roo.log(edata);
49129                     return;
49130                 }
49131                 if (!rdata || !rdata.success) {
49132                     Roo.log(rdata);
49133                     Roo.MessageBox.alert(Roo.encode(rdata));
49134                     return;
49135                 }
49136                 var data = rdata.data;
49137                 
49138                 if (this.uploadComplete) {
49139                    Roo.MessageBox.hide();
49140                    return;
49141                 }
49142                    
49143                 if (data){
49144                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49145                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49146                     );
49147                 }
49148                 this.uploadProgress.defer(2000,this);
49149             },
49150        
49151             failure: function(data) {
49152                 Roo.log('progress url failed ');
49153                 Roo.log(data);
49154             },
49155             scope : this
49156         });
49157            
49158     },
49159     
49160     
49161     run : function()
49162     {
49163         // run get Values on the form, so it syncs any secondary forms.
49164         this.form.getValues();
49165         
49166         var o = this.options;
49167         var method = this.getMethod();
49168         var isPost = method == 'POST';
49169         if(o.clientValidation === false || this.form.isValid()){
49170             
49171             if (this.form.progressUrl) {
49172                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49173                     (new Date() * 1) + '' + Math.random());
49174                     
49175             } 
49176             
49177             
49178             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49179                 form:this.form.el.dom,
49180                 url:this.getUrl(!isPost),
49181                 method: method,
49182                 params:isPost ? this.getParams() : null,
49183                 isUpload: this.form.fileUpload,
49184                 formData : this.form.formData
49185             }));
49186             
49187             this.uploadProgress();
49188
49189         }else if (o.clientValidation !== false){ // client validation failed
49190             this.failureType = Roo.form.Action.CLIENT_INVALID;
49191             this.form.afterAction(this, false);
49192         }
49193     },
49194
49195     success : function(response)
49196     {
49197         this.uploadComplete= true;
49198         if (this.haveProgress) {
49199             Roo.MessageBox.hide();
49200         }
49201         
49202         
49203         var result = this.processResponse(response);
49204         if(result === true || result.success){
49205             this.form.afterAction(this, true);
49206             return;
49207         }
49208         if(result.errors){
49209             this.form.markInvalid(result.errors);
49210             this.failureType = Roo.form.Action.SERVER_INVALID;
49211         }
49212         this.form.afterAction(this, false);
49213     },
49214     failure : function(response)
49215     {
49216         this.uploadComplete= true;
49217         if (this.haveProgress) {
49218             Roo.MessageBox.hide();
49219         }
49220         
49221         this.response = response;
49222         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49223         this.form.afterAction(this, false);
49224     },
49225     
49226     handleResponse : function(response){
49227         if(this.form.errorReader){
49228             var rs = this.form.errorReader.read(response);
49229             var errors = [];
49230             if(rs.records){
49231                 for(var i = 0, len = rs.records.length; i < len; i++) {
49232                     var r = rs.records[i];
49233                     errors[i] = r.data;
49234                 }
49235             }
49236             if(errors.length < 1){
49237                 errors = null;
49238             }
49239             return {
49240                 success : rs.success,
49241                 errors : errors
49242             };
49243         }
49244         var ret = false;
49245         try {
49246             ret = Roo.decode(response.responseText);
49247         } catch (e) {
49248             ret = {
49249                 success: false,
49250                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49251                 errors : []
49252             };
49253         }
49254         return ret;
49255         
49256     }
49257 });
49258
49259
49260 Roo.form.Action.Load = function(form, options){
49261     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49262     this.reader = this.form.reader;
49263 };
49264
49265 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49266     type : 'load',
49267
49268     run : function(){
49269         
49270         Roo.Ajax.request(Roo.apply(
49271                 this.createCallback(), {
49272                     method:this.getMethod(),
49273                     url:this.getUrl(false),
49274                     params:this.getParams()
49275         }));
49276     },
49277
49278     success : function(response){
49279         
49280         var result = this.processResponse(response);
49281         if(result === true || !result.success || !result.data){
49282             this.failureType = Roo.form.Action.LOAD_FAILURE;
49283             this.form.afterAction(this, false);
49284             return;
49285         }
49286         this.form.clearInvalid();
49287         this.form.setValues(result.data);
49288         this.form.afterAction(this, true);
49289     },
49290
49291     handleResponse : function(response){
49292         if(this.form.reader){
49293             var rs = this.form.reader.read(response);
49294             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49295             return {
49296                 success : rs.success,
49297                 data : data
49298             };
49299         }
49300         return Roo.decode(response.responseText);
49301     }
49302 });
49303
49304 Roo.form.Action.ACTION_TYPES = {
49305     'load' : Roo.form.Action.Load,
49306     'submit' : Roo.form.Action.Submit
49307 };/*
49308  * Based on:
49309  * Ext JS Library 1.1.1
49310  * Copyright(c) 2006-2007, Ext JS, LLC.
49311  *
49312  * Originally Released Under LGPL - original licence link has changed is not relivant.
49313  *
49314  * Fork - LGPL
49315  * <script type="text/javascript">
49316  */
49317  
49318 /**
49319  * @class Roo.form.Layout
49320  * @extends Roo.Component
49321  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49322  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49323  * @constructor
49324  * @param {Object} config Configuration options
49325  */
49326 Roo.form.Layout = function(config){
49327     var xitems = [];
49328     if (config.items) {
49329         xitems = config.items;
49330         delete config.items;
49331     }
49332     Roo.form.Layout.superclass.constructor.call(this, config);
49333     this.stack = [];
49334     Roo.each(xitems, this.addxtype, this);
49335      
49336 };
49337
49338 Roo.extend(Roo.form.Layout, Roo.Component, {
49339     /**
49340      * @cfg {String/Object} autoCreate
49341      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49342      */
49343     /**
49344      * @cfg {String/Object/Function} style
49345      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49346      * a function which returns such a specification.
49347      */
49348     /**
49349      * @cfg {String} labelAlign
49350      * Valid values are "left," "top" and "right" (defaults to "left")
49351      */
49352     /**
49353      * @cfg {Number} labelWidth
49354      * Fixed width in pixels of all field labels (defaults to undefined)
49355      */
49356     /**
49357      * @cfg {Boolean} clear
49358      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49359      */
49360     clear : true,
49361     /**
49362      * @cfg {String} labelSeparator
49363      * The separator to use after field labels (defaults to ':')
49364      */
49365     labelSeparator : ':',
49366     /**
49367      * @cfg {Boolean} hideLabels
49368      * True to suppress the display of field labels in this layout (defaults to false)
49369      */
49370     hideLabels : false,
49371
49372     // private
49373     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49374     
49375     isLayout : true,
49376     
49377     // private
49378     onRender : function(ct, position){
49379         if(this.el){ // from markup
49380             this.el = Roo.get(this.el);
49381         }else {  // generate
49382             var cfg = this.getAutoCreate();
49383             this.el = ct.createChild(cfg, position);
49384         }
49385         if(this.style){
49386             this.el.applyStyles(this.style);
49387         }
49388         if(this.labelAlign){
49389             this.el.addClass('x-form-label-'+this.labelAlign);
49390         }
49391         if(this.hideLabels){
49392             this.labelStyle = "display:none";
49393             this.elementStyle = "padding-left:0;";
49394         }else{
49395             if(typeof this.labelWidth == 'number'){
49396                 this.labelStyle = "width:"+this.labelWidth+"px;";
49397                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49398             }
49399             if(this.labelAlign == 'top'){
49400                 this.labelStyle = "width:auto;";
49401                 this.elementStyle = "padding-left:0;";
49402             }
49403         }
49404         var stack = this.stack;
49405         var slen = stack.length;
49406         if(slen > 0){
49407             if(!this.fieldTpl){
49408                 var t = new Roo.Template(
49409                     '<div class="x-form-item {5}">',
49410                         '<label for="{0}" style="{2}">{1}{4}</label>',
49411                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49412                         '</div>',
49413                     '</div><div class="x-form-clear-left"></div>'
49414                 );
49415                 t.disableFormats = true;
49416                 t.compile();
49417                 Roo.form.Layout.prototype.fieldTpl = t;
49418             }
49419             for(var i = 0; i < slen; i++) {
49420                 if(stack[i].isFormField){
49421                     this.renderField(stack[i]);
49422                 }else{
49423                     this.renderComponent(stack[i]);
49424                 }
49425             }
49426         }
49427         if(this.clear){
49428             this.el.createChild({cls:'x-form-clear'});
49429         }
49430     },
49431
49432     // private
49433     renderField : function(f){
49434         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49435                f.id, //0
49436                f.fieldLabel, //1
49437                f.labelStyle||this.labelStyle||'', //2
49438                this.elementStyle||'', //3
49439                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49440                f.itemCls||this.itemCls||''  //5
49441        ], true).getPrevSibling());
49442     },
49443
49444     // private
49445     renderComponent : function(c){
49446         c.render(c.isLayout ? this.el : this.el.createChild());    
49447     },
49448     /**
49449      * Adds a object form elements (using the xtype property as the factory method.)
49450      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49451      * @param {Object} config 
49452      */
49453     addxtype : function(o)
49454     {
49455         // create the lement.
49456         o.form = this.form;
49457         var fe = Roo.factory(o, Roo.form);
49458         this.form.allItems.push(fe);
49459         this.stack.push(fe);
49460         
49461         if (fe.isFormField) {
49462             this.form.items.add(fe);
49463         }
49464          
49465         return fe;
49466     }
49467 });
49468
49469 /**
49470  * @class Roo.form.Column
49471  * @extends Roo.form.Layout
49472  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49473  * @constructor
49474  * @param {Object} config Configuration options
49475  */
49476 Roo.form.Column = function(config){
49477     Roo.form.Column.superclass.constructor.call(this, config);
49478 };
49479
49480 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49481     /**
49482      * @cfg {Number/String} width
49483      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49484      */
49485     /**
49486      * @cfg {String/Object} autoCreate
49487      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49488      */
49489
49490     // private
49491     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49492
49493     // private
49494     onRender : function(ct, position){
49495         Roo.form.Column.superclass.onRender.call(this, ct, position);
49496         if(this.width){
49497             this.el.setWidth(this.width);
49498         }
49499     }
49500 });
49501
49502
49503 /**
49504  * @class Roo.form.Row
49505  * @extends Roo.form.Layout
49506  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49507  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49508  * @constructor
49509  * @param {Object} config Configuration options
49510  */
49511
49512  
49513 Roo.form.Row = function(config){
49514     Roo.form.Row.superclass.constructor.call(this, config);
49515 };
49516  
49517 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49518       /**
49519      * @cfg {Number/String} width
49520      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49521      */
49522     /**
49523      * @cfg {Number/String} height
49524      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49525      */
49526     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49527     
49528     padWidth : 20,
49529     // private
49530     onRender : function(ct, position){
49531         //console.log('row render');
49532         if(!this.rowTpl){
49533             var t = new Roo.Template(
49534                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49535                     '<label for="{0}" style="{2}">{1}{4}</label>',
49536                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49537                     '</div>',
49538                 '</div>'
49539             );
49540             t.disableFormats = true;
49541             t.compile();
49542             Roo.form.Layout.prototype.rowTpl = t;
49543         }
49544         this.fieldTpl = this.rowTpl;
49545         
49546         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49547         var labelWidth = 100;
49548         
49549         if ((this.labelAlign != 'top')) {
49550             if (typeof this.labelWidth == 'number') {
49551                 labelWidth = this.labelWidth
49552             }
49553             this.padWidth =  20 + labelWidth;
49554             
49555         }
49556         
49557         Roo.form.Column.superclass.onRender.call(this, ct, position);
49558         if(this.width){
49559             this.el.setWidth(this.width);
49560         }
49561         if(this.height){
49562             this.el.setHeight(this.height);
49563         }
49564     },
49565     
49566     // private
49567     renderField : function(f){
49568         f.fieldEl = this.fieldTpl.append(this.el, [
49569                f.id, f.fieldLabel,
49570                f.labelStyle||this.labelStyle||'',
49571                this.elementStyle||'',
49572                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49573                f.itemCls||this.itemCls||'',
49574                f.width ? f.width + this.padWidth : 160 + this.padWidth
49575        ],true);
49576     }
49577 });
49578  
49579
49580 /**
49581  * @class Roo.form.FieldSet
49582  * @extends Roo.form.Layout
49583  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49584  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49585  * @constructor
49586  * @param {Object} config Configuration options
49587  */
49588 Roo.form.FieldSet = function(config){
49589     Roo.form.FieldSet.superclass.constructor.call(this, config);
49590 };
49591
49592 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49593     /**
49594      * @cfg {String} legend
49595      * The text to display as the legend for the FieldSet (defaults to '')
49596      */
49597     /**
49598      * @cfg {String/Object} autoCreate
49599      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49600      */
49601
49602     // private
49603     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49604
49605     // private
49606     onRender : function(ct, position){
49607         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49608         if(this.legend){
49609             this.setLegend(this.legend);
49610         }
49611     },
49612
49613     // private
49614     setLegend : function(text){
49615         if(this.rendered){
49616             this.el.child('legend').update(text);
49617         }
49618     }
49619 });/*
49620  * Based on:
49621  * Ext JS Library 1.1.1
49622  * Copyright(c) 2006-2007, Ext JS, LLC.
49623  *
49624  * Originally Released Under LGPL - original licence link has changed is not relivant.
49625  *
49626  * Fork - LGPL
49627  * <script type="text/javascript">
49628  */
49629 /**
49630  * @class Roo.form.VTypes
49631  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49632  * @singleton
49633  */
49634 Roo.form.VTypes = function(){
49635     // closure these in so they are only created once.
49636     var alpha = /^[a-zA-Z_]+$/;
49637     var alphanum = /^[a-zA-Z0-9_]+$/;
49638     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49639     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49640
49641     // All these messages and functions are configurable
49642     return {
49643         /**
49644          * The function used to validate email addresses
49645          * @param {String} value The email address
49646          */
49647         'email' : function(v){
49648             return email.test(v);
49649         },
49650         /**
49651          * The error text to display when the email validation function returns false
49652          * @type String
49653          */
49654         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49655         /**
49656          * The keystroke filter mask to be applied on email input
49657          * @type RegExp
49658          */
49659         'emailMask' : /[a-z0-9_\.\-@]/i,
49660
49661         /**
49662          * The function used to validate URLs
49663          * @param {String} value The URL
49664          */
49665         'url' : function(v){
49666             return url.test(v);
49667         },
49668         /**
49669          * The error text to display when the url validation function returns false
49670          * @type String
49671          */
49672         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49673         
49674         /**
49675          * The function used to validate alpha values
49676          * @param {String} value The value
49677          */
49678         'alpha' : function(v){
49679             return alpha.test(v);
49680         },
49681         /**
49682          * The error text to display when the alpha validation function returns false
49683          * @type String
49684          */
49685         'alphaText' : 'This field should only contain letters and _',
49686         /**
49687          * The keystroke filter mask to be applied on alpha input
49688          * @type RegExp
49689          */
49690         'alphaMask' : /[a-z_]/i,
49691
49692         /**
49693          * The function used to validate alphanumeric values
49694          * @param {String} value The value
49695          */
49696         'alphanum' : function(v){
49697             return alphanum.test(v);
49698         },
49699         /**
49700          * The error text to display when the alphanumeric validation function returns false
49701          * @type String
49702          */
49703         'alphanumText' : 'This field should only contain letters, numbers and _',
49704         /**
49705          * The keystroke filter mask to be applied on alphanumeric input
49706          * @type RegExp
49707          */
49708         'alphanumMask' : /[a-z0-9_]/i
49709     };
49710 }();//<script type="text/javascript">
49711
49712 /**
49713  * @class Roo.form.FCKeditor
49714  * @extends Roo.form.TextArea
49715  * Wrapper around the FCKEditor http://www.fckeditor.net
49716  * @constructor
49717  * Creates a new FCKeditor
49718  * @param {Object} config Configuration options
49719  */
49720 Roo.form.FCKeditor = function(config){
49721     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49722     this.addEvents({
49723          /**
49724          * @event editorinit
49725          * Fired when the editor is initialized - you can add extra handlers here..
49726          * @param {FCKeditor} this
49727          * @param {Object} the FCK object.
49728          */
49729         editorinit : true
49730     });
49731     
49732     
49733 };
49734 Roo.form.FCKeditor.editors = { };
49735 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49736 {
49737     //defaultAutoCreate : {
49738     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49739     //},
49740     // private
49741     /**
49742      * @cfg {Object} fck options - see fck manual for details.
49743      */
49744     fckconfig : false,
49745     
49746     /**
49747      * @cfg {Object} fck toolbar set (Basic or Default)
49748      */
49749     toolbarSet : 'Basic',
49750     /**
49751      * @cfg {Object} fck BasePath
49752      */ 
49753     basePath : '/fckeditor/',
49754     
49755     
49756     frame : false,
49757     
49758     value : '',
49759     
49760    
49761     onRender : function(ct, position)
49762     {
49763         if(!this.el){
49764             this.defaultAutoCreate = {
49765                 tag: "textarea",
49766                 style:"width:300px;height:60px;",
49767                 autocomplete: "new-password"
49768             };
49769         }
49770         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49771         /*
49772         if(this.grow){
49773             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49774             if(this.preventScrollbars){
49775                 this.el.setStyle("overflow", "hidden");
49776             }
49777             this.el.setHeight(this.growMin);
49778         }
49779         */
49780         //console.log('onrender' + this.getId() );
49781         Roo.form.FCKeditor.editors[this.getId()] = this;
49782          
49783
49784         this.replaceTextarea() ;
49785         
49786     },
49787     
49788     getEditor : function() {
49789         return this.fckEditor;
49790     },
49791     /**
49792      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49793      * @param {Mixed} value The value to set
49794      */
49795     
49796     
49797     setValue : function(value)
49798     {
49799         //console.log('setValue: ' + value);
49800         
49801         if(typeof(value) == 'undefined') { // not sure why this is happending...
49802             return;
49803         }
49804         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49805         
49806         //if(!this.el || !this.getEditor()) {
49807         //    this.value = value;
49808             //this.setValue.defer(100,this,[value]);    
49809         //    return;
49810         //} 
49811         
49812         if(!this.getEditor()) {
49813             return;
49814         }
49815         
49816         this.getEditor().SetData(value);
49817         
49818         //
49819
49820     },
49821
49822     /**
49823      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49824      * @return {Mixed} value The field value
49825      */
49826     getValue : function()
49827     {
49828         
49829         if (this.frame && this.frame.dom.style.display == 'none') {
49830             return Roo.form.FCKeditor.superclass.getValue.call(this);
49831         }
49832         
49833         if(!this.el || !this.getEditor()) {
49834            
49835            // this.getValue.defer(100,this); 
49836             return this.value;
49837         }
49838        
49839         
49840         var value=this.getEditor().GetData();
49841         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49842         return Roo.form.FCKeditor.superclass.getValue.call(this);
49843         
49844
49845     },
49846
49847     /**
49848      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49849      * @return {Mixed} value The field value
49850      */
49851     getRawValue : function()
49852     {
49853         if (this.frame && this.frame.dom.style.display == 'none') {
49854             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49855         }
49856         
49857         if(!this.el || !this.getEditor()) {
49858             //this.getRawValue.defer(100,this); 
49859             return this.value;
49860             return;
49861         }
49862         
49863         
49864         
49865         var value=this.getEditor().GetData();
49866         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49867         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49868          
49869     },
49870     
49871     setSize : function(w,h) {
49872         
49873         
49874         
49875         //if (this.frame && this.frame.dom.style.display == 'none') {
49876         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49877         //    return;
49878         //}
49879         //if(!this.el || !this.getEditor()) {
49880         //    this.setSize.defer(100,this, [w,h]); 
49881         //    return;
49882         //}
49883         
49884         
49885         
49886         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49887         
49888         this.frame.dom.setAttribute('width', w);
49889         this.frame.dom.setAttribute('height', h);
49890         this.frame.setSize(w,h);
49891         
49892     },
49893     
49894     toggleSourceEdit : function(value) {
49895         
49896       
49897          
49898         this.el.dom.style.display = value ? '' : 'none';
49899         this.frame.dom.style.display = value ?  'none' : '';
49900         
49901     },
49902     
49903     
49904     focus: function(tag)
49905     {
49906         if (this.frame.dom.style.display == 'none') {
49907             return Roo.form.FCKeditor.superclass.focus.call(this);
49908         }
49909         if(!this.el || !this.getEditor()) {
49910             this.focus.defer(100,this, [tag]); 
49911             return;
49912         }
49913         
49914         
49915         
49916         
49917         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49918         this.getEditor().Focus();
49919         if (tgs.length) {
49920             if (!this.getEditor().Selection.GetSelection()) {
49921                 this.focus.defer(100,this, [tag]); 
49922                 return;
49923             }
49924             
49925             
49926             var r = this.getEditor().EditorDocument.createRange();
49927             r.setStart(tgs[0],0);
49928             r.setEnd(tgs[0],0);
49929             this.getEditor().Selection.GetSelection().removeAllRanges();
49930             this.getEditor().Selection.GetSelection().addRange(r);
49931             this.getEditor().Focus();
49932         }
49933         
49934     },
49935     
49936     
49937     
49938     replaceTextarea : function()
49939     {
49940         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49941             return ;
49942         }
49943         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49944         //{
49945             // We must check the elements firstly using the Id and then the name.
49946         var oTextarea = document.getElementById( this.getId() );
49947         
49948         var colElementsByName = document.getElementsByName( this.getId() ) ;
49949          
49950         oTextarea.style.display = 'none' ;
49951
49952         if ( oTextarea.tabIndex ) {            
49953             this.TabIndex = oTextarea.tabIndex ;
49954         }
49955         
49956         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49957         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49958         this.frame = Roo.get(this.getId() + '___Frame')
49959     },
49960     
49961     _getConfigHtml : function()
49962     {
49963         var sConfig = '' ;
49964
49965         for ( var o in this.fckconfig ) {
49966             sConfig += sConfig.length > 0  ? '&amp;' : '';
49967             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49968         }
49969
49970         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49971     },
49972     
49973     
49974     _getIFrameHtml : function()
49975     {
49976         var sFile = 'fckeditor.html' ;
49977         /* no idea what this is about..
49978         try
49979         {
49980             if ( (/fcksource=true/i).test( window.top.location.search ) )
49981                 sFile = 'fckeditor.original.html' ;
49982         }
49983         catch (e) { 
49984         */
49985
49986         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49987         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49988         
49989         
49990         var html = '<iframe id="' + this.getId() +
49991             '___Frame" src="' + sLink +
49992             '" width="' + this.width +
49993             '" height="' + this.height + '"' +
49994             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49995             ' frameborder="0" scrolling="no"></iframe>' ;
49996
49997         return html ;
49998     },
49999     
50000     _insertHtmlBefore : function( html, element )
50001     {
50002         if ( element.insertAdjacentHTML )       {
50003             // IE
50004             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50005         } else { // Gecko
50006             var oRange = document.createRange() ;
50007             oRange.setStartBefore( element ) ;
50008             var oFragment = oRange.createContextualFragment( html );
50009             element.parentNode.insertBefore( oFragment, element ) ;
50010         }
50011     }
50012     
50013     
50014   
50015     
50016     
50017     
50018     
50019
50020 });
50021
50022 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50023
50024 function FCKeditor_OnComplete(editorInstance){
50025     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50026     f.fckEditor = editorInstance;
50027     //console.log("loaded");
50028     f.fireEvent('editorinit', f, editorInstance);
50029
50030   
50031
50032  
50033
50034
50035
50036
50037
50038
50039
50040
50041
50042
50043
50044
50045
50046
50047
50048 //<script type="text/javascript">
50049 /**
50050  * @class Roo.form.GridField
50051  * @extends Roo.form.Field
50052  * Embed a grid (or editable grid into a form)
50053  * STATUS ALPHA
50054  * 
50055  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50056  * it needs 
50057  * xgrid.store = Roo.data.Store
50058  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50059  * xgrid.store.reader = Roo.data.JsonReader 
50060  * 
50061  * 
50062  * @constructor
50063  * Creates a new GridField
50064  * @param {Object} config Configuration options
50065  */
50066 Roo.form.GridField = function(config){
50067     Roo.form.GridField.superclass.constructor.call(this, config);
50068      
50069 };
50070
50071 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50072     /**
50073      * @cfg {Number} width  - used to restrict width of grid..
50074      */
50075     width : 100,
50076     /**
50077      * @cfg {Number} height - used to restrict height of grid..
50078      */
50079     height : 50,
50080      /**
50081      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50082          * 
50083          *}
50084      */
50085     xgrid : false, 
50086     /**
50087      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50088      * {tag: "input", type: "checkbox", autocomplete: "off"})
50089      */
50090    // defaultAutoCreate : { tag: 'div' },
50091     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50092     /**
50093      * @cfg {String} addTitle Text to include for adding a title.
50094      */
50095     addTitle : false,
50096     //
50097     onResize : function(){
50098         Roo.form.Field.superclass.onResize.apply(this, arguments);
50099     },
50100
50101     initEvents : function(){
50102         // Roo.form.Checkbox.superclass.initEvents.call(this);
50103         // has no events...
50104        
50105     },
50106
50107
50108     getResizeEl : function(){
50109         return this.wrap;
50110     },
50111
50112     getPositionEl : function(){
50113         return this.wrap;
50114     },
50115
50116     // private
50117     onRender : function(ct, position){
50118         
50119         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50120         var style = this.style;
50121         delete this.style;
50122         
50123         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50124         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50125         this.viewEl = this.wrap.createChild({ tag: 'div' });
50126         if (style) {
50127             this.viewEl.applyStyles(style);
50128         }
50129         if (this.width) {
50130             this.viewEl.setWidth(this.width);
50131         }
50132         if (this.height) {
50133             this.viewEl.setHeight(this.height);
50134         }
50135         //if(this.inputValue !== undefined){
50136         //this.setValue(this.value);
50137         
50138         
50139         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50140         
50141         
50142         this.grid.render();
50143         this.grid.getDataSource().on('remove', this.refreshValue, this);
50144         this.grid.getDataSource().on('update', this.refreshValue, this);
50145         this.grid.on('afteredit', this.refreshValue, this);
50146  
50147     },
50148      
50149     
50150     /**
50151      * Sets the value of the item. 
50152      * @param {String} either an object  or a string..
50153      */
50154     setValue : function(v){
50155         //this.value = v;
50156         v = v || []; // empty set..
50157         // this does not seem smart - it really only affects memoryproxy grids..
50158         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50159             var ds = this.grid.getDataSource();
50160             // assumes a json reader..
50161             var data = {}
50162             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50163             ds.loadData( data);
50164         }
50165         // clear selection so it does not get stale.
50166         if (this.grid.sm) { 
50167             this.grid.sm.clearSelections();
50168         }
50169         
50170         Roo.form.GridField.superclass.setValue.call(this, v);
50171         this.refreshValue();
50172         // should load data in the grid really....
50173     },
50174     
50175     // private
50176     refreshValue: function() {
50177          var val = [];
50178         this.grid.getDataSource().each(function(r) {
50179             val.push(r.data);
50180         });
50181         this.el.dom.value = Roo.encode(val);
50182     }
50183     
50184      
50185     
50186     
50187 });/*
50188  * Based on:
50189  * Ext JS Library 1.1.1
50190  * Copyright(c) 2006-2007, Ext JS, LLC.
50191  *
50192  * Originally Released Under LGPL - original licence link has changed is not relivant.
50193  *
50194  * Fork - LGPL
50195  * <script type="text/javascript">
50196  */
50197 /**
50198  * @class Roo.form.DisplayField
50199  * @extends Roo.form.Field
50200  * A generic Field to display non-editable data.
50201  * @cfg {Boolean} closable (true|false) default false
50202  * @constructor
50203  * Creates a new Display Field item.
50204  * @param {Object} config Configuration options
50205  */
50206 Roo.form.DisplayField = function(config){
50207     Roo.form.DisplayField.superclass.constructor.call(this, config);
50208     
50209     this.addEvents({
50210         /**
50211          * @event close
50212          * Fires after the click the close btn
50213              * @param {Roo.form.DisplayField} this
50214              */
50215         close : true
50216     });
50217 };
50218
50219 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50220     inputType:      'hidden',
50221     allowBlank:     true,
50222     readOnly:         true,
50223     
50224  
50225     /**
50226      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50227      */
50228     focusClass : undefined,
50229     /**
50230      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50231      */
50232     fieldClass: 'x-form-field',
50233     
50234      /**
50235      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50236      */
50237     valueRenderer: undefined,
50238     
50239     width: 100,
50240     /**
50241      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50242      * {tag: "input", type: "checkbox", autocomplete: "off"})
50243      */
50244      
50245  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50246  
50247     closable : false,
50248     
50249     onResize : function(){
50250         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50251         
50252     },
50253
50254     initEvents : function(){
50255         // Roo.form.Checkbox.superclass.initEvents.call(this);
50256         // has no events...
50257         
50258         if(this.closable){
50259             this.closeEl.on('click', this.onClose, this);
50260         }
50261        
50262     },
50263
50264
50265     getResizeEl : function(){
50266         return this.wrap;
50267     },
50268
50269     getPositionEl : function(){
50270         return this.wrap;
50271     },
50272
50273     // private
50274     onRender : function(ct, position){
50275         
50276         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50277         //if(this.inputValue !== undefined){
50278         this.wrap = this.el.wrap();
50279         
50280         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50281         
50282         if(this.closable){
50283             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50284         }
50285         
50286         if (this.bodyStyle) {
50287             this.viewEl.applyStyles(this.bodyStyle);
50288         }
50289         //this.viewEl.setStyle('padding', '2px');
50290         
50291         this.setValue(this.value);
50292         
50293     },
50294 /*
50295     // private
50296     initValue : Roo.emptyFn,
50297
50298   */
50299
50300         // private
50301     onClick : function(){
50302         
50303     },
50304
50305     /**
50306      * Sets the checked state of the checkbox.
50307      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50308      */
50309     setValue : function(v){
50310         this.value = v;
50311         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50312         // this might be called before we have a dom element..
50313         if (!this.viewEl) {
50314             return;
50315         }
50316         this.viewEl.dom.innerHTML = html;
50317         Roo.form.DisplayField.superclass.setValue.call(this, v);
50318
50319     },
50320     
50321     onClose : function(e)
50322     {
50323         e.preventDefault();
50324         
50325         this.fireEvent('close', this);
50326     }
50327 });/*
50328  * 
50329  * Licence- LGPL
50330  * 
50331  */
50332
50333 /**
50334  * @class Roo.form.DayPicker
50335  * @extends Roo.form.Field
50336  * A Day picker show [M] [T] [W] ....
50337  * @constructor
50338  * Creates a new Day Picker
50339  * @param {Object} config Configuration options
50340  */
50341 Roo.form.DayPicker= function(config){
50342     Roo.form.DayPicker.superclass.constructor.call(this, config);
50343      
50344 };
50345
50346 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50347     /**
50348      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50349      */
50350     focusClass : undefined,
50351     /**
50352      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50353      */
50354     fieldClass: "x-form-field",
50355    
50356     /**
50357      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50358      * {tag: "input", type: "checkbox", autocomplete: "off"})
50359      */
50360     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50361     
50362    
50363     actionMode : 'viewEl', 
50364     //
50365     // private
50366  
50367     inputType : 'hidden',
50368     
50369      
50370     inputElement: false, // real input element?
50371     basedOn: false, // ????
50372     
50373     isFormField: true, // not sure where this is needed!!!!
50374
50375     onResize : function(){
50376         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50377         if(!this.boxLabel){
50378             this.el.alignTo(this.wrap, 'c-c');
50379         }
50380     },
50381
50382     initEvents : function(){
50383         Roo.form.Checkbox.superclass.initEvents.call(this);
50384         this.el.on("click", this.onClick,  this);
50385         this.el.on("change", this.onClick,  this);
50386     },
50387
50388
50389     getResizeEl : function(){
50390         return this.wrap;
50391     },
50392
50393     getPositionEl : function(){
50394         return this.wrap;
50395     },
50396
50397     
50398     // private
50399     onRender : function(ct, position){
50400         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50401        
50402         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50403         
50404         var r1 = '<table><tr>';
50405         var r2 = '<tr class="x-form-daypick-icons">';
50406         for (var i=0; i < 7; i++) {
50407             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50408             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50409         }
50410         
50411         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50412         viewEl.select('img').on('click', this.onClick, this);
50413         this.viewEl = viewEl;   
50414         
50415         
50416         // this will not work on Chrome!!!
50417         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50418         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50419         
50420         
50421           
50422
50423     },
50424
50425     // private
50426     initValue : Roo.emptyFn,
50427
50428     /**
50429      * Returns the checked state of the checkbox.
50430      * @return {Boolean} True if checked, else false
50431      */
50432     getValue : function(){
50433         return this.el.dom.value;
50434         
50435     },
50436
50437         // private
50438     onClick : function(e){ 
50439         //this.setChecked(!this.checked);
50440         Roo.get(e.target).toggleClass('x-menu-item-checked');
50441         this.refreshValue();
50442         //if(this.el.dom.checked != this.checked){
50443         //    this.setValue(this.el.dom.checked);
50444        // }
50445     },
50446     
50447     // private
50448     refreshValue : function()
50449     {
50450         var val = '';
50451         this.viewEl.select('img',true).each(function(e,i,n)  {
50452             val += e.is(".x-menu-item-checked") ? String(n) : '';
50453         });
50454         this.setValue(val, true);
50455     },
50456
50457     /**
50458      * Sets the checked state of the checkbox.
50459      * On is always based on a string comparison between inputValue and the param.
50460      * @param {Boolean/String} value - the value to set 
50461      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50462      */
50463     setValue : function(v,suppressEvent){
50464         if (!this.el.dom) {
50465             return;
50466         }
50467         var old = this.el.dom.value ;
50468         this.el.dom.value = v;
50469         if (suppressEvent) {
50470             return ;
50471         }
50472          
50473         // update display..
50474         this.viewEl.select('img',true).each(function(e,i,n)  {
50475             
50476             var on = e.is(".x-menu-item-checked");
50477             var newv = v.indexOf(String(n)) > -1;
50478             if (on != newv) {
50479                 e.toggleClass('x-menu-item-checked');
50480             }
50481             
50482         });
50483         
50484         
50485         this.fireEvent('change', this, v, old);
50486         
50487         
50488     },
50489    
50490     // handle setting of hidden value by some other method!!?!?
50491     setFromHidden: function()
50492     {
50493         if(!this.el){
50494             return;
50495         }
50496         //console.log("SET FROM HIDDEN");
50497         //alert('setFrom hidden');
50498         this.setValue(this.el.dom.value);
50499     },
50500     
50501     onDestroy : function()
50502     {
50503         if(this.viewEl){
50504             Roo.get(this.viewEl).remove();
50505         }
50506          
50507         Roo.form.DayPicker.superclass.onDestroy.call(this);
50508     }
50509
50510 });/*
50511  * RooJS Library 1.1.1
50512  * Copyright(c) 2008-2011  Alan Knowles
50513  *
50514  * License - LGPL
50515  */
50516  
50517
50518 /**
50519  * @class Roo.form.ComboCheck
50520  * @extends Roo.form.ComboBox
50521  * A combobox for multiple select items.
50522  *
50523  * FIXME - could do with a reset button..
50524  * 
50525  * @constructor
50526  * Create a new ComboCheck
50527  * @param {Object} config Configuration options
50528  */
50529 Roo.form.ComboCheck = function(config){
50530     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50531     // should verify some data...
50532     // like
50533     // hiddenName = required..
50534     // displayField = required
50535     // valudField == required
50536     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50537     var _t = this;
50538     Roo.each(req, function(e) {
50539         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50540             throw "Roo.form.ComboCheck : missing value for: " + e;
50541         }
50542     });
50543     
50544     
50545 };
50546
50547 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50548      
50549      
50550     editable : false,
50551      
50552     selectedClass: 'x-menu-item-checked', 
50553     
50554     // private
50555     onRender : function(ct, position){
50556         var _t = this;
50557         
50558         
50559         
50560         if(!this.tpl){
50561             var cls = 'x-combo-list';
50562
50563             
50564             this.tpl =  new Roo.Template({
50565                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50566                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50567                    '<span>{' + this.displayField + '}</span>' +
50568                     '</div>' 
50569                 
50570             });
50571         }
50572  
50573         
50574         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50575         this.view.singleSelect = false;
50576         this.view.multiSelect = true;
50577         this.view.toggleSelect = true;
50578         this.pageTb.add(new Roo.Toolbar.Fill(), {
50579             
50580             text: 'Done',
50581             handler: function()
50582             {
50583                 _t.collapse();
50584             }
50585         });
50586     },
50587     
50588     onViewOver : function(e, t){
50589         // do nothing...
50590         return;
50591         
50592     },
50593     
50594     onViewClick : function(doFocus,index){
50595         return;
50596         
50597     },
50598     select: function () {
50599         //Roo.log("SELECT CALLED");
50600     },
50601      
50602     selectByValue : function(xv, scrollIntoView){
50603         var ar = this.getValueArray();
50604         var sels = [];
50605         
50606         Roo.each(ar, function(v) {
50607             if(v === undefined || v === null){
50608                 return;
50609             }
50610             var r = this.findRecord(this.valueField, v);
50611             if(r){
50612                 sels.push(this.store.indexOf(r))
50613                 
50614             }
50615         },this);
50616         this.view.select(sels);
50617         return false;
50618     },
50619     
50620     
50621     
50622     onSelect : function(record, index){
50623        // Roo.log("onselect Called");
50624        // this is only called by the clear button now..
50625         this.view.clearSelections();
50626         this.setValue('[]');
50627         if (this.value != this.valueBefore) {
50628             this.fireEvent('change', this, this.value, this.valueBefore);
50629             this.valueBefore = this.value;
50630         }
50631     },
50632     getValueArray : function()
50633     {
50634         var ar = [] ;
50635         
50636         try {
50637             //Roo.log(this.value);
50638             if (typeof(this.value) == 'undefined') {
50639                 return [];
50640             }
50641             var ar = Roo.decode(this.value);
50642             return  ar instanceof Array ? ar : []; //?? valid?
50643             
50644         } catch(e) {
50645             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50646             return [];
50647         }
50648          
50649     },
50650     expand : function ()
50651     {
50652         
50653         Roo.form.ComboCheck.superclass.expand.call(this);
50654         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50655         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50656         
50657
50658     },
50659     
50660     collapse : function(){
50661         Roo.form.ComboCheck.superclass.collapse.call(this);
50662         var sl = this.view.getSelectedIndexes();
50663         var st = this.store;
50664         var nv = [];
50665         var tv = [];
50666         var r;
50667         Roo.each(sl, function(i) {
50668             r = st.getAt(i);
50669             nv.push(r.get(this.valueField));
50670         },this);
50671         this.setValue(Roo.encode(nv));
50672         if (this.value != this.valueBefore) {
50673
50674             this.fireEvent('change', this, this.value, this.valueBefore);
50675             this.valueBefore = this.value;
50676         }
50677         
50678     },
50679     
50680     setValue : function(v){
50681         // Roo.log(v);
50682         this.value = v;
50683         
50684         var vals = this.getValueArray();
50685         var tv = [];
50686         Roo.each(vals, function(k) {
50687             var r = this.findRecord(this.valueField, k);
50688             if(r){
50689                 tv.push(r.data[this.displayField]);
50690             }else if(this.valueNotFoundText !== undefined){
50691                 tv.push( this.valueNotFoundText );
50692             }
50693         },this);
50694        // Roo.log(tv);
50695         
50696         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50697         this.hiddenField.value = v;
50698         this.value = v;
50699     }
50700     
50701 });/*
50702  * Based on:
50703  * Ext JS Library 1.1.1
50704  * Copyright(c) 2006-2007, Ext JS, LLC.
50705  *
50706  * Originally Released Under LGPL - original licence link has changed is not relivant.
50707  *
50708  * Fork - LGPL
50709  * <script type="text/javascript">
50710  */
50711  
50712 /**
50713  * @class Roo.form.Signature
50714  * @extends Roo.form.Field
50715  * Signature field.  
50716  * @constructor
50717  * 
50718  * @param {Object} config Configuration options
50719  */
50720
50721 Roo.form.Signature = function(config){
50722     Roo.form.Signature.superclass.constructor.call(this, config);
50723     
50724     this.addEvents({// not in used??
50725          /**
50726          * @event confirm
50727          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50728              * @param {Roo.form.Signature} combo This combo box
50729              */
50730         'confirm' : true,
50731         /**
50732          * @event reset
50733          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50734              * @param {Roo.form.ComboBox} combo This combo box
50735              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50736              */
50737         'reset' : true
50738     });
50739 };
50740
50741 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50742     /**
50743      * @cfg {Object} labels Label to use when rendering a form.
50744      * defaults to 
50745      * labels : { 
50746      *      clear : "Clear",
50747      *      confirm : "Confirm"
50748      *  }
50749      */
50750     labels : { 
50751         clear : "Clear",
50752         confirm : "Confirm"
50753     },
50754     /**
50755      * @cfg {Number} width The signature panel width (defaults to 300)
50756      */
50757     width: 300,
50758     /**
50759      * @cfg {Number} height The signature panel height (defaults to 100)
50760      */
50761     height : 100,
50762     /**
50763      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50764      */
50765     allowBlank : false,
50766     
50767     //private
50768     // {Object} signPanel The signature SVG panel element (defaults to {})
50769     signPanel : {},
50770     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50771     isMouseDown : false,
50772     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50773     isConfirmed : false,
50774     // {String} signatureTmp SVG mapping string (defaults to empty string)
50775     signatureTmp : '',
50776     
50777     
50778     defaultAutoCreate : { // modified by initCompnoent..
50779         tag: "input",
50780         type:"hidden"
50781     },
50782
50783     // private
50784     onRender : function(ct, position){
50785         
50786         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50787         
50788         this.wrap = this.el.wrap({
50789             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50790         });
50791         
50792         this.createToolbar(this);
50793         this.signPanel = this.wrap.createChild({
50794                 tag: 'div',
50795                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50796             }, this.el
50797         );
50798             
50799         this.svgID = Roo.id();
50800         this.svgEl = this.signPanel.createChild({
50801               xmlns : 'http://www.w3.org/2000/svg',
50802               tag : 'svg',
50803               id : this.svgID + "-svg",
50804               width: this.width,
50805               height: this.height,
50806               viewBox: '0 0 '+this.width+' '+this.height,
50807               cn : [
50808                 {
50809                     tag: "rect",
50810                     id: this.svgID + "-svg-r",
50811                     width: this.width,
50812                     height: this.height,
50813                     fill: "#ffa"
50814                 },
50815                 {
50816                     tag: "line",
50817                     id: this.svgID + "-svg-l",
50818                     x1: "0", // start
50819                     y1: (this.height*0.8), // start set the line in 80% of height
50820                     x2: this.width, // end
50821                     y2: (this.height*0.8), // end set the line in 80% of height
50822                     'stroke': "#666",
50823                     'stroke-width': "1",
50824                     'stroke-dasharray': "3",
50825                     'shape-rendering': "crispEdges",
50826                     'pointer-events': "none"
50827                 },
50828                 {
50829                     tag: "path",
50830                     id: this.svgID + "-svg-p",
50831                     'stroke': "navy",
50832                     'stroke-width': "3",
50833                     'fill': "none",
50834                     'pointer-events': 'none'
50835                 }
50836               ]
50837         });
50838         this.createSVG();
50839         this.svgBox = this.svgEl.dom.getScreenCTM();
50840     },
50841     createSVG : function(){ 
50842         var svg = this.signPanel;
50843         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50844         var t = this;
50845
50846         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50847         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50848         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50849         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50850         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50851         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50852         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50853         
50854     },
50855     isTouchEvent : function(e){
50856         return e.type.match(/^touch/);
50857     },
50858     getCoords : function (e) {
50859         var pt    = this.svgEl.dom.createSVGPoint();
50860         pt.x = e.clientX; 
50861         pt.y = e.clientY;
50862         if (this.isTouchEvent(e)) {
50863             pt.x =  e.targetTouches[0].clientX;
50864             pt.y = e.targetTouches[0].clientY;
50865         }
50866         var a = this.svgEl.dom.getScreenCTM();
50867         var b = a.inverse();
50868         var mx = pt.matrixTransform(b);
50869         return mx.x + ',' + mx.y;
50870     },
50871     //mouse event headler 
50872     down : function (e) {
50873         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50874         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50875         
50876         this.isMouseDown = true;
50877         
50878         e.preventDefault();
50879     },
50880     move : function (e) {
50881         if (this.isMouseDown) {
50882             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50883             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50884         }
50885         
50886         e.preventDefault();
50887     },
50888     up : function (e) {
50889         this.isMouseDown = false;
50890         var sp = this.signatureTmp.split(' ');
50891         
50892         if(sp.length > 1){
50893             if(!sp[sp.length-2].match(/^L/)){
50894                 sp.pop();
50895                 sp.pop();
50896                 sp.push("");
50897                 this.signatureTmp = sp.join(" ");
50898             }
50899         }
50900         if(this.getValue() != this.signatureTmp){
50901             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50902             this.isConfirmed = false;
50903         }
50904         e.preventDefault();
50905     },
50906     
50907     /**
50908      * Protected method that will not generally be called directly. It
50909      * is called when the editor creates its toolbar. Override this method if you need to
50910      * add custom toolbar buttons.
50911      * @param {HtmlEditor} editor
50912      */
50913     createToolbar : function(editor){
50914          function btn(id, toggle, handler){
50915             var xid = fid + '-'+ id ;
50916             return {
50917                 id : xid,
50918                 cmd : id,
50919                 cls : 'x-btn-icon x-edit-'+id,
50920                 enableToggle:toggle !== false,
50921                 scope: editor, // was editor...
50922                 handler:handler||editor.relayBtnCmd,
50923                 clickEvent:'mousedown',
50924                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50925                 tabIndex:-1
50926             };
50927         }
50928         
50929         
50930         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50931         this.tb = tb;
50932         this.tb.add(
50933            {
50934                 cls : ' x-signature-btn x-signature-'+id,
50935                 scope: editor, // was editor...
50936                 handler: this.reset,
50937                 clickEvent:'mousedown',
50938                 text: this.labels.clear
50939             },
50940             {
50941                  xtype : 'Fill',
50942                  xns: Roo.Toolbar
50943             }, 
50944             {
50945                 cls : '  x-signature-btn x-signature-'+id,
50946                 scope: editor, // was editor...
50947                 handler: this.confirmHandler,
50948                 clickEvent:'mousedown',
50949                 text: this.labels.confirm
50950             }
50951         );
50952     
50953     },
50954     //public
50955     /**
50956      * when user is clicked confirm then show this image.....
50957      * 
50958      * @return {String} Image Data URI
50959      */
50960     getImageDataURI : function(){
50961         var svg = this.svgEl.dom.parentNode.innerHTML;
50962         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50963         return src; 
50964     },
50965     /**
50966      * 
50967      * @return {Boolean} this.isConfirmed
50968      */
50969     getConfirmed : function(){
50970         return this.isConfirmed;
50971     },
50972     /**
50973      * 
50974      * @return {Number} this.width
50975      */
50976     getWidth : function(){
50977         return this.width;
50978     },
50979     /**
50980      * 
50981      * @return {Number} this.height
50982      */
50983     getHeight : function(){
50984         return this.height;
50985     },
50986     // private
50987     getSignature : function(){
50988         return this.signatureTmp;
50989     },
50990     // private
50991     reset : function(){
50992         this.signatureTmp = '';
50993         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50994         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50995         this.isConfirmed = false;
50996         Roo.form.Signature.superclass.reset.call(this);
50997     },
50998     setSignature : function(s){
50999         this.signatureTmp = s;
51000         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51001         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51002         this.setValue(s);
51003         this.isConfirmed = false;
51004         Roo.form.Signature.superclass.reset.call(this);
51005     }, 
51006     test : function(){
51007 //        Roo.log(this.signPanel.dom.contentWindow.up())
51008     },
51009     //private
51010     setConfirmed : function(){
51011         
51012         
51013         
51014 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51015     },
51016     // private
51017     confirmHandler : function(){
51018         if(!this.getSignature()){
51019             return;
51020         }
51021         
51022         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51023         this.setValue(this.getSignature());
51024         this.isConfirmed = true;
51025         
51026         this.fireEvent('confirm', this);
51027     },
51028     // private
51029     // Subclasses should provide the validation implementation by overriding this
51030     validateValue : function(value){
51031         if(this.allowBlank){
51032             return true;
51033         }
51034         
51035         if(this.isConfirmed){
51036             return true;
51037         }
51038         return false;
51039     }
51040 });/*
51041  * Based on:
51042  * Ext JS Library 1.1.1
51043  * Copyright(c) 2006-2007, Ext JS, LLC.
51044  *
51045  * Originally Released Under LGPL - original licence link has changed is not relivant.
51046  *
51047  * Fork - LGPL
51048  * <script type="text/javascript">
51049  */
51050  
51051
51052 /**
51053  * @class Roo.form.ComboBox
51054  * @extends Roo.form.TriggerField
51055  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51056  * @constructor
51057  * Create a new ComboBox.
51058  * @param {Object} config Configuration options
51059  */
51060 Roo.form.Select = function(config){
51061     Roo.form.Select.superclass.constructor.call(this, config);
51062      
51063 };
51064
51065 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51066     /**
51067      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51068      */
51069     /**
51070      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51071      * rendering into an Roo.Editor, defaults to false)
51072      */
51073     /**
51074      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51075      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51076      */
51077     /**
51078      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51079      */
51080     /**
51081      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51082      * the dropdown list (defaults to undefined, with no header element)
51083      */
51084
51085      /**
51086      * @cfg {String/Roo.Template} tpl The template to use to render the output
51087      */
51088      
51089     // private
51090     defaultAutoCreate : {tag: "select"  },
51091     /**
51092      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51093      */
51094     listWidth: undefined,
51095     /**
51096      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51097      * mode = 'remote' or 'text' if mode = 'local')
51098      */
51099     displayField: undefined,
51100     /**
51101      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51102      * mode = 'remote' or 'value' if mode = 'local'). 
51103      * Note: use of a valueField requires the user make a selection
51104      * in order for a value to be mapped.
51105      */
51106     valueField: undefined,
51107     
51108     
51109     /**
51110      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51111      * field's data value (defaults to the underlying DOM element's name)
51112      */
51113     hiddenName: undefined,
51114     /**
51115      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51116      */
51117     listClass: '',
51118     /**
51119      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51120      */
51121     selectedClass: 'x-combo-selected',
51122     /**
51123      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51124      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51125      * which displays a downward arrow icon).
51126      */
51127     triggerClass : 'x-form-arrow-trigger',
51128     /**
51129      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51130      */
51131     shadow:'sides',
51132     /**
51133      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51134      * anchor positions (defaults to 'tl-bl')
51135      */
51136     listAlign: 'tl-bl?',
51137     /**
51138      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51139      */
51140     maxHeight: 300,
51141     /**
51142      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51143      * query specified by the allQuery config option (defaults to 'query')
51144      */
51145     triggerAction: 'query',
51146     /**
51147      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51148      * (defaults to 4, does not apply if editable = false)
51149      */
51150     minChars : 4,
51151     /**
51152      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51153      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51154      */
51155     typeAhead: false,
51156     /**
51157      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51158      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51159      */
51160     queryDelay: 500,
51161     /**
51162      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51163      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51164      */
51165     pageSize: 0,
51166     /**
51167      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51168      * when editable = true (defaults to false)
51169      */
51170     selectOnFocus:false,
51171     /**
51172      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51173      */
51174     queryParam: 'query',
51175     /**
51176      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51177      * when mode = 'remote' (defaults to 'Loading...')
51178      */
51179     loadingText: 'Loading...',
51180     /**
51181      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51182      */
51183     resizable: false,
51184     /**
51185      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51186      */
51187     handleHeight : 8,
51188     /**
51189      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51190      * traditional select (defaults to true)
51191      */
51192     editable: true,
51193     /**
51194      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51195      */
51196     allQuery: '',
51197     /**
51198      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51199      */
51200     mode: 'remote',
51201     /**
51202      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51203      * listWidth has a higher value)
51204      */
51205     minListWidth : 70,
51206     /**
51207      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51208      * allow the user to set arbitrary text into the field (defaults to false)
51209      */
51210     forceSelection:false,
51211     /**
51212      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51213      * if typeAhead = true (defaults to 250)
51214      */
51215     typeAheadDelay : 250,
51216     /**
51217      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51218      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51219      */
51220     valueNotFoundText : undefined,
51221     
51222     /**
51223      * @cfg {String} defaultValue The value displayed after loading the store.
51224      */
51225     defaultValue: '',
51226     
51227     /**
51228      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51229      */
51230     blockFocus : false,
51231     
51232     /**
51233      * @cfg {Boolean} disableClear Disable showing of clear button.
51234      */
51235     disableClear : false,
51236     /**
51237      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51238      */
51239     alwaysQuery : false,
51240     
51241     //private
51242     addicon : false,
51243     editicon: false,
51244     
51245     // element that contains real text value.. (when hidden is used..)
51246      
51247     // private
51248     onRender : function(ct, position){
51249         Roo.form.Field.prototype.onRender.call(this, ct, position);
51250         
51251         if(this.store){
51252             this.store.on('beforeload', this.onBeforeLoad, this);
51253             this.store.on('load', this.onLoad, this);
51254             this.store.on('loadexception', this.onLoadException, this);
51255             this.store.load({});
51256         }
51257         
51258         
51259         
51260     },
51261
51262     // private
51263     initEvents : function(){
51264         //Roo.form.ComboBox.superclass.initEvents.call(this);
51265  
51266     },
51267
51268     onDestroy : function(){
51269        
51270         if(this.store){
51271             this.store.un('beforeload', this.onBeforeLoad, this);
51272             this.store.un('load', this.onLoad, this);
51273             this.store.un('loadexception', this.onLoadException, this);
51274         }
51275         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51276     },
51277
51278     // private
51279     fireKey : function(e){
51280         if(e.isNavKeyPress() && !this.list.isVisible()){
51281             this.fireEvent("specialkey", this, e);
51282         }
51283     },
51284
51285     // private
51286     onResize: function(w, h){
51287         
51288         return; 
51289     
51290         
51291     },
51292
51293     /**
51294      * Allow or prevent the user from directly editing the field text.  If false is passed,
51295      * the user will only be able to select from the items defined in the dropdown list.  This method
51296      * is the runtime equivalent of setting the 'editable' config option at config time.
51297      * @param {Boolean} value True to allow the user to directly edit the field text
51298      */
51299     setEditable : function(value){
51300          
51301     },
51302
51303     // private
51304     onBeforeLoad : function(){
51305         
51306         Roo.log("Select before load");
51307         return;
51308     
51309         this.innerList.update(this.loadingText ?
51310                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51311         //this.restrictHeight();
51312         this.selectedIndex = -1;
51313     },
51314
51315     // private
51316     onLoad : function(){
51317
51318     
51319         var dom = this.el.dom;
51320         dom.innerHTML = '';
51321          var od = dom.ownerDocument;
51322          
51323         if (this.emptyText) {
51324             var op = od.createElement('option');
51325             op.setAttribute('value', '');
51326             op.innerHTML = String.format('{0}', this.emptyText);
51327             dom.appendChild(op);
51328         }
51329         if(this.store.getCount() > 0){
51330            
51331             var vf = this.valueField;
51332             var df = this.displayField;
51333             this.store.data.each(function(r) {
51334                 // which colmsn to use... testing - cdoe / title..
51335                 var op = od.createElement('option');
51336                 op.setAttribute('value', r.data[vf]);
51337                 op.innerHTML = String.format('{0}', r.data[df]);
51338                 dom.appendChild(op);
51339             });
51340             if (typeof(this.defaultValue != 'undefined')) {
51341                 this.setValue(this.defaultValue);
51342             }
51343             
51344              
51345         }else{
51346             //this.onEmptyResults();
51347         }
51348         //this.el.focus();
51349     },
51350     // private
51351     onLoadException : function()
51352     {
51353         dom.innerHTML = '';
51354             
51355         Roo.log("Select on load exception");
51356         return;
51357     
51358         this.collapse();
51359         Roo.log(this.store.reader.jsonData);
51360         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51361             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51362         }
51363         
51364         
51365     },
51366     // private
51367     onTypeAhead : function(){
51368          
51369     },
51370
51371     // private
51372     onSelect : function(record, index){
51373         Roo.log('on select?');
51374         return;
51375         if(this.fireEvent('beforeselect', this, record, index) !== false){
51376             this.setFromData(index > -1 ? record.data : false);
51377             this.collapse();
51378             this.fireEvent('select', this, record, index);
51379         }
51380     },
51381
51382     /**
51383      * Returns the currently selected field value or empty string if no value is set.
51384      * @return {String} value The selected value
51385      */
51386     getValue : function(){
51387         var dom = this.el.dom;
51388         this.value = dom.options[dom.selectedIndex].value;
51389         return this.value;
51390         
51391     },
51392
51393     /**
51394      * Clears any text/value currently set in the field
51395      */
51396     clearValue : function(){
51397         this.value = '';
51398         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51399         
51400     },
51401
51402     /**
51403      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51404      * will be displayed in the field.  If the value does not match the data value of an existing item,
51405      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51406      * Otherwise the field will be blank (although the value will still be set).
51407      * @param {String} value The value to match
51408      */
51409     setValue : function(v){
51410         var d = this.el.dom;
51411         for (var i =0; i < d.options.length;i++) {
51412             if (v == d.options[i].value) {
51413                 d.selectedIndex = i;
51414                 this.value = v;
51415                 return;
51416             }
51417         }
51418         this.clearValue();
51419     },
51420     /**
51421      * @property {Object} the last set data for the element
51422      */
51423     
51424     lastData : false,
51425     /**
51426      * Sets the value of the field based on a object which is related to the record format for the store.
51427      * @param {Object} value the value to set as. or false on reset?
51428      */
51429     setFromData : function(o){
51430         Roo.log('setfrom data?');
51431          
51432         
51433         
51434     },
51435     // private
51436     reset : function(){
51437         this.clearValue();
51438     },
51439     // private
51440     findRecord : function(prop, value){
51441         
51442         return false;
51443     
51444         var record;
51445         if(this.store.getCount() > 0){
51446             this.store.each(function(r){
51447                 if(r.data[prop] == value){
51448                     record = r;
51449                     return false;
51450                 }
51451                 return true;
51452             });
51453         }
51454         return record;
51455     },
51456     
51457     getName: function()
51458     {
51459         // returns hidden if it's set..
51460         if (!this.rendered) {return ''};
51461         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51462         
51463     },
51464      
51465
51466     
51467
51468     // private
51469     onEmptyResults : function(){
51470         Roo.log('empty results');
51471         //this.collapse();
51472     },
51473
51474     /**
51475      * Returns true if the dropdown list is expanded, else false.
51476      */
51477     isExpanded : function(){
51478         return false;
51479     },
51480
51481     /**
51482      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51483      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51484      * @param {String} value The data value of the item to select
51485      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51486      * selected item if it is not currently in view (defaults to true)
51487      * @return {Boolean} True if the value matched an item in the list, else false
51488      */
51489     selectByValue : function(v, scrollIntoView){
51490         Roo.log('select By Value');
51491         return false;
51492     
51493         if(v !== undefined && v !== null){
51494             var r = this.findRecord(this.valueField || this.displayField, v);
51495             if(r){
51496                 this.select(this.store.indexOf(r), scrollIntoView);
51497                 return true;
51498             }
51499         }
51500         return false;
51501     },
51502
51503     /**
51504      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51505      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51506      * @param {Number} index The zero-based index of the list item to select
51507      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51508      * selected item if it is not currently in view (defaults to true)
51509      */
51510     select : function(index, scrollIntoView){
51511         Roo.log('select ');
51512         return  ;
51513         
51514         this.selectedIndex = index;
51515         this.view.select(index);
51516         if(scrollIntoView !== false){
51517             var el = this.view.getNode(index);
51518             if(el){
51519                 this.innerList.scrollChildIntoView(el, false);
51520             }
51521         }
51522     },
51523
51524       
51525
51526     // private
51527     validateBlur : function(){
51528         
51529         return;
51530         
51531     },
51532
51533     // private
51534     initQuery : function(){
51535         this.doQuery(this.getRawValue());
51536     },
51537
51538     // private
51539     doForce : function(){
51540         if(this.el.dom.value.length > 0){
51541             this.el.dom.value =
51542                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51543              
51544         }
51545     },
51546
51547     /**
51548      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51549      * query allowing the query action to be canceled if needed.
51550      * @param {String} query The SQL query to execute
51551      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51552      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51553      * saved in the current store (defaults to false)
51554      */
51555     doQuery : function(q, forceAll){
51556         
51557         Roo.log('doQuery?');
51558         if(q === undefined || q === null){
51559             q = '';
51560         }
51561         var qe = {
51562             query: q,
51563             forceAll: forceAll,
51564             combo: this,
51565             cancel:false
51566         };
51567         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51568             return false;
51569         }
51570         q = qe.query;
51571         forceAll = qe.forceAll;
51572         if(forceAll === true || (q.length >= this.minChars)){
51573             if(this.lastQuery != q || this.alwaysQuery){
51574                 this.lastQuery = q;
51575                 if(this.mode == 'local'){
51576                     this.selectedIndex = -1;
51577                     if(forceAll){
51578                         this.store.clearFilter();
51579                     }else{
51580                         this.store.filter(this.displayField, q);
51581                     }
51582                     this.onLoad();
51583                 }else{
51584                     this.store.baseParams[this.queryParam] = q;
51585                     this.store.load({
51586                         params: this.getParams(q)
51587                     });
51588                     this.expand();
51589                 }
51590             }else{
51591                 this.selectedIndex = -1;
51592                 this.onLoad();   
51593             }
51594         }
51595     },
51596
51597     // private
51598     getParams : function(q){
51599         var p = {};
51600         //p[this.queryParam] = q;
51601         if(this.pageSize){
51602             p.start = 0;
51603             p.limit = this.pageSize;
51604         }
51605         return p;
51606     },
51607
51608     /**
51609      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51610      */
51611     collapse : function(){
51612         
51613     },
51614
51615     // private
51616     collapseIf : function(e){
51617         
51618     },
51619
51620     /**
51621      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51622      */
51623     expand : function(){
51624         
51625     } ,
51626
51627     // private
51628      
51629
51630     /** 
51631     * @cfg {Boolean} grow 
51632     * @hide 
51633     */
51634     /** 
51635     * @cfg {Number} growMin 
51636     * @hide 
51637     */
51638     /** 
51639     * @cfg {Number} growMax 
51640     * @hide 
51641     */
51642     /**
51643      * @hide
51644      * @method autoSize
51645      */
51646     
51647     setWidth : function()
51648     {
51649         
51650     },
51651     getResizeEl : function(){
51652         return this.el;
51653     }
51654 });//<script type="text/javasscript">
51655  
51656
51657 /**
51658  * @class Roo.DDView
51659  * A DnD enabled version of Roo.View.
51660  * @param {Element/String} container The Element in which to create the View.
51661  * @param {String} tpl The template string used to create the markup for each element of the View
51662  * @param {Object} config The configuration properties. These include all the config options of
51663  * {@link Roo.View} plus some specific to this class.<br>
51664  * <p>
51665  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51666  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51667  * <p>
51668  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51669 .x-view-drag-insert-above {
51670         border-top:1px dotted #3366cc;
51671 }
51672 .x-view-drag-insert-below {
51673         border-bottom:1px dotted #3366cc;
51674 }
51675 </code></pre>
51676  * 
51677  */
51678  
51679 Roo.DDView = function(container, tpl, config) {
51680     Roo.DDView.superclass.constructor.apply(this, arguments);
51681     this.getEl().setStyle("outline", "0px none");
51682     this.getEl().unselectable();
51683     if (this.dragGroup) {
51684         this.setDraggable(this.dragGroup.split(","));
51685     }
51686     if (this.dropGroup) {
51687         this.setDroppable(this.dropGroup.split(","));
51688     }
51689     if (this.deletable) {
51690         this.setDeletable();
51691     }
51692     this.isDirtyFlag = false;
51693         this.addEvents({
51694                 "drop" : true
51695         });
51696 };
51697
51698 Roo.extend(Roo.DDView, Roo.View, {
51699 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51700 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51701 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51702 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51703
51704         isFormField: true,
51705
51706         reset: Roo.emptyFn,
51707         
51708         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51709
51710         validate: function() {
51711                 return true;
51712         },
51713         
51714         destroy: function() {
51715                 this.purgeListeners();
51716                 this.getEl.removeAllListeners();
51717                 this.getEl().remove();
51718                 if (this.dragZone) {
51719                         if (this.dragZone.destroy) {
51720                                 this.dragZone.destroy();
51721                         }
51722                 }
51723                 if (this.dropZone) {
51724                         if (this.dropZone.destroy) {
51725                                 this.dropZone.destroy();
51726                         }
51727                 }
51728         },
51729
51730 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51731         getName: function() {
51732                 return this.name;
51733         },
51734
51735 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51736         setValue: function(v) {
51737                 if (!this.store) {
51738                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51739                 }
51740                 var data = {};
51741                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51742                 this.store.proxy = new Roo.data.MemoryProxy(data);
51743                 this.store.load();
51744         },
51745
51746 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51747         getValue: function() {
51748                 var result = '(';
51749                 this.store.each(function(rec) {
51750                         result += rec.id + ',';
51751                 });
51752                 return result.substr(0, result.length - 1) + ')';
51753         },
51754         
51755         getIds: function() {
51756                 var i = 0, result = new Array(this.store.getCount());
51757                 this.store.each(function(rec) {
51758                         result[i++] = rec.id;
51759                 });
51760                 return result;
51761         },
51762         
51763         isDirty: function() {
51764                 return this.isDirtyFlag;
51765         },
51766
51767 /**
51768  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51769  *      whole Element becomes the target, and this causes the drop gesture to append.
51770  */
51771     getTargetFromEvent : function(e) {
51772                 var target = e.getTarget();
51773                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51774                 target = target.parentNode;
51775                 }
51776                 if (!target) {
51777                         target = this.el.dom.lastChild || this.el.dom;
51778                 }
51779                 return target;
51780     },
51781
51782 /**
51783  *      Create the drag data which consists of an object which has the property "ddel" as
51784  *      the drag proxy element. 
51785  */
51786     getDragData : function(e) {
51787         var target = this.findItemFromChild(e.getTarget());
51788                 if(target) {
51789                         this.handleSelection(e);
51790                         var selNodes = this.getSelectedNodes();
51791             var dragData = {
51792                 source: this,
51793                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51794                 nodes: selNodes,
51795                 records: []
51796                         };
51797                         var selectedIndices = this.getSelectedIndexes();
51798                         for (var i = 0; i < selectedIndices.length; i++) {
51799                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51800                         }
51801                         if (selNodes.length == 1) {
51802                                 dragData.ddel = target.cloneNode(true); // the div element
51803                         } else {
51804                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51805                                 div.className = 'multi-proxy';
51806                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51807                                         div.appendChild(selNodes[i].cloneNode(true));
51808                                 }
51809                                 dragData.ddel = div;
51810                         }
51811             //console.log(dragData)
51812             //console.log(dragData.ddel.innerHTML)
51813                         return dragData;
51814                 }
51815         //console.log('nodragData')
51816                 return false;
51817     },
51818     
51819 /**     Specify to which ddGroup items in this DDView may be dragged. */
51820     setDraggable: function(ddGroup) {
51821         if (ddGroup instanceof Array) {
51822                 Roo.each(ddGroup, this.setDraggable, this);
51823                 return;
51824         }
51825         if (this.dragZone) {
51826                 this.dragZone.addToGroup(ddGroup);
51827         } else {
51828                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51829                                 containerScroll: true,
51830                                 ddGroup: ddGroup 
51831
51832                         });
51833 //                      Draggability implies selection. DragZone's mousedown selects the element.
51834                         if (!this.multiSelect) { this.singleSelect = true; }
51835
51836 //                      Wire the DragZone's handlers up to methods in *this*
51837                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51838                 }
51839     },
51840
51841 /**     Specify from which ddGroup this DDView accepts drops. */
51842     setDroppable: function(ddGroup) {
51843         if (ddGroup instanceof Array) {
51844                 Roo.each(ddGroup, this.setDroppable, this);
51845                 return;
51846         }
51847         if (this.dropZone) {
51848                 this.dropZone.addToGroup(ddGroup);
51849         } else {
51850                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51851                                 containerScroll: true,
51852                                 ddGroup: ddGroup
51853                         });
51854
51855 //                      Wire the DropZone's handlers up to methods in *this*
51856                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51857                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51858                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51859                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51860                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51861                 }
51862     },
51863
51864 /**     Decide whether to drop above or below a View node. */
51865     getDropPoint : function(e, n, dd){
51866         if (n == this.el.dom) { return "above"; }
51867                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51868                 var c = t + (b - t) / 2;
51869                 var y = Roo.lib.Event.getPageY(e);
51870                 if(y <= c) {
51871                         return "above";
51872                 }else{
51873                         return "below";
51874                 }
51875     },
51876
51877     onNodeEnter : function(n, dd, e, data){
51878                 return false;
51879     },
51880     
51881     onNodeOver : function(n, dd, e, data){
51882                 var pt = this.getDropPoint(e, n, dd);
51883                 // set the insert point style on the target node
51884                 var dragElClass = this.dropNotAllowed;
51885                 if (pt) {
51886                         var targetElClass;
51887                         if (pt == "above"){
51888                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51889                                 targetElClass = "x-view-drag-insert-above";
51890                         } else {
51891                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51892                                 targetElClass = "x-view-drag-insert-below";
51893                         }
51894                         if (this.lastInsertClass != targetElClass){
51895                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51896                                 this.lastInsertClass = targetElClass;
51897                         }
51898                 }
51899                 return dragElClass;
51900         },
51901
51902     onNodeOut : function(n, dd, e, data){
51903                 this.removeDropIndicators(n);
51904     },
51905
51906     onNodeDrop : function(n, dd, e, data){
51907         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51908                 return false;
51909         }
51910         var pt = this.getDropPoint(e, n, dd);
51911                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51912                 if (pt == "below") { insertAt++; }
51913                 for (var i = 0; i < data.records.length; i++) {
51914                         var r = data.records[i];
51915                         var dup = this.store.getById(r.id);
51916                         if (dup && (dd != this.dragZone)) {
51917                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51918                         } else {
51919                                 if (data.copy) {
51920                                         this.store.insert(insertAt++, r.copy());
51921                                 } else {
51922                                         data.source.isDirtyFlag = true;
51923                                         r.store.remove(r);
51924                                         this.store.insert(insertAt++, r);
51925                                 }
51926                                 this.isDirtyFlag = true;
51927                         }
51928                 }
51929                 this.dragZone.cachedTarget = null;
51930                 return true;
51931     },
51932
51933     removeDropIndicators : function(n){
51934                 if(n){
51935                         Roo.fly(n).removeClass([
51936                                 "x-view-drag-insert-above",
51937                                 "x-view-drag-insert-below"]);
51938                         this.lastInsertClass = "_noclass";
51939                 }
51940     },
51941
51942 /**
51943  *      Utility method. Add a delete option to the DDView's context menu.
51944  *      @param {String} imageUrl The URL of the "delete" icon image.
51945  */
51946         setDeletable: function(imageUrl) {
51947                 if (!this.singleSelect && !this.multiSelect) {
51948                         this.singleSelect = true;
51949                 }
51950                 var c = this.getContextMenu();
51951                 this.contextMenu.on("itemclick", function(item) {
51952                         switch (item.id) {
51953                                 case "delete":
51954                                         this.remove(this.getSelectedIndexes());
51955                                         break;
51956                         }
51957                 }, this);
51958                 this.contextMenu.add({
51959                         icon: imageUrl,
51960                         id: "delete",
51961                         text: 'Delete'
51962                 });
51963         },
51964         
51965 /**     Return the context menu for this DDView. */
51966         getContextMenu: function() {
51967                 if (!this.contextMenu) {
51968 //                      Create the View's context menu
51969                         this.contextMenu = new Roo.menu.Menu({
51970                                 id: this.id + "-contextmenu"
51971                         });
51972                         this.el.on("contextmenu", this.showContextMenu, this);
51973                 }
51974                 return this.contextMenu;
51975         },
51976         
51977         disableContextMenu: function() {
51978                 if (this.contextMenu) {
51979                         this.el.un("contextmenu", this.showContextMenu, this);
51980                 }
51981         },
51982
51983         showContextMenu: function(e, item) {
51984         item = this.findItemFromChild(e.getTarget());
51985                 if (item) {
51986                         e.stopEvent();
51987                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51988                         this.contextMenu.showAt(e.getXY());
51989             }
51990     },
51991
51992 /**
51993  *      Remove {@link Roo.data.Record}s at the specified indices.
51994  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51995  */
51996     remove: function(selectedIndices) {
51997                 selectedIndices = [].concat(selectedIndices);
51998                 for (var i = 0; i < selectedIndices.length; i++) {
51999                         var rec = this.store.getAt(selectedIndices[i]);
52000                         this.store.remove(rec);
52001                 }
52002     },
52003
52004 /**
52005  *      Double click fires the event, but also, if this is draggable, and there is only one other
52006  *      related DropZone, it transfers the selected node.
52007  */
52008     onDblClick : function(e){
52009         var item = this.findItemFromChild(e.getTarget());
52010         if(item){
52011             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52012                 return false;
52013             }
52014             if (this.dragGroup) {
52015                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52016                     while (targets.indexOf(this.dropZone) > -1) {
52017                             targets.remove(this.dropZone);
52018                                 }
52019                     if (targets.length == 1) {
52020                                         this.dragZone.cachedTarget = null;
52021                         var el = Roo.get(targets[0].getEl());
52022                         var box = el.getBox(true);
52023                         targets[0].onNodeDrop(el.dom, {
52024                                 target: el.dom,
52025                                 xy: [box.x, box.y + box.height - 1]
52026                         }, null, this.getDragData(e));
52027                     }
52028                 }
52029         }
52030     },
52031     
52032     handleSelection: function(e) {
52033                 this.dragZone.cachedTarget = null;
52034         var item = this.findItemFromChild(e.getTarget());
52035         if (!item) {
52036                 this.clearSelections(true);
52037                 return;
52038         }
52039                 if (item && (this.multiSelect || this.singleSelect)){
52040                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52041                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52042                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52043                                 this.unselect(item);
52044                         } else {
52045                                 this.select(item, this.multiSelect && e.ctrlKey);
52046                                 this.lastSelection = item;
52047                         }
52048                 }
52049     },
52050
52051     onItemClick : function(item, index, e){
52052                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52053                         return false;
52054                 }
52055                 return true;
52056     },
52057
52058     unselect : function(nodeInfo, suppressEvent){
52059                 var node = this.getNode(nodeInfo);
52060                 if(node && this.isSelected(node)){
52061                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52062                                 Roo.fly(node).removeClass(this.selectedClass);
52063                                 this.selections.remove(node);
52064                                 if(!suppressEvent){
52065                                         this.fireEvent("selectionchange", this, this.selections);
52066                                 }
52067                         }
52068                 }
52069     }
52070 });
52071 /*
52072  * Based on:
52073  * Ext JS Library 1.1.1
52074  * Copyright(c) 2006-2007, Ext JS, LLC.
52075  *
52076  * Originally Released Under LGPL - original licence link has changed is not relivant.
52077  *
52078  * Fork - LGPL
52079  * <script type="text/javascript">
52080  */
52081  
52082 /**
52083  * @class Roo.LayoutManager
52084  * @extends Roo.util.Observable
52085  * Base class for layout managers.
52086  */
52087 Roo.LayoutManager = function(container, config){
52088     Roo.LayoutManager.superclass.constructor.call(this);
52089     this.el = Roo.get(container);
52090     // ie scrollbar fix
52091     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52092         document.body.scroll = "no";
52093     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52094         this.el.position('relative');
52095     }
52096     this.id = this.el.id;
52097     this.el.addClass("x-layout-container");
52098     /** false to disable window resize monitoring @type Boolean */
52099     this.monitorWindowResize = true;
52100     this.regions = {};
52101     this.addEvents({
52102         /**
52103          * @event layout
52104          * Fires when a layout is performed. 
52105          * @param {Roo.LayoutManager} this
52106          */
52107         "layout" : true,
52108         /**
52109          * @event regionresized
52110          * Fires when the user resizes a region. 
52111          * @param {Roo.LayoutRegion} region The resized region
52112          * @param {Number} newSize The new size (width for east/west, height for north/south)
52113          */
52114         "regionresized" : true,
52115         /**
52116          * @event regioncollapsed
52117          * Fires when a region is collapsed. 
52118          * @param {Roo.LayoutRegion} region The collapsed region
52119          */
52120         "regioncollapsed" : true,
52121         /**
52122          * @event regionexpanded
52123          * Fires when a region is expanded.  
52124          * @param {Roo.LayoutRegion} region The expanded region
52125          */
52126         "regionexpanded" : true
52127     });
52128     this.updating = false;
52129     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52130 };
52131
52132 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52133     /**
52134      * Returns true if this layout is currently being updated
52135      * @return {Boolean}
52136      */
52137     isUpdating : function(){
52138         return this.updating; 
52139     },
52140     
52141     /**
52142      * Suspend the LayoutManager from doing auto-layouts while
52143      * making multiple add or remove calls
52144      */
52145     beginUpdate : function(){
52146         this.updating = true;    
52147     },
52148     
52149     /**
52150      * Restore auto-layouts and optionally disable the manager from performing a layout
52151      * @param {Boolean} noLayout true to disable a layout update 
52152      */
52153     endUpdate : function(noLayout){
52154         this.updating = false;
52155         if(!noLayout){
52156             this.layout();
52157         }    
52158     },
52159     
52160     layout: function(){
52161         
52162     },
52163     
52164     onRegionResized : function(region, newSize){
52165         this.fireEvent("regionresized", region, newSize);
52166         this.layout();
52167     },
52168     
52169     onRegionCollapsed : function(region){
52170         this.fireEvent("regioncollapsed", region);
52171     },
52172     
52173     onRegionExpanded : function(region){
52174         this.fireEvent("regionexpanded", region);
52175     },
52176         
52177     /**
52178      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52179      * performs box-model adjustments.
52180      * @return {Object} The size as an object {width: (the width), height: (the height)}
52181      */
52182     getViewSize : function(){
52183         var size;
52184         if(this.el.dom != document.body){
52185             size = this.el.getSize();
52186         }else{
52187             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52188         }
52189         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52190         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52191         return size;
52192     },
52193     
52194     /**
52195      * Returns the Element this layout is bound to.
52196      * @return {Roo.Element}
52197      */
52198     getEl : function(){
52199         return this.el;
52200     },
52201     
52202     /**
52203      * Returns the specified region.
52204      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52205      * @return {Roo.LayoutRegion}
52206      */
52207     getRegion : function(target){
52208         return this.regions[target.toLowerCase()];
52209     },
52210     
52211     onWindowResize : function(){
52212         if(this.monitorWindowResize){
52213             this.layout();
52214         }
52215     }
52216 });/*
52217  * Based on:
52218  * Ext JS Library 1.1.1
52219  * Copyright(c) 2006-2007, Ext JS, LLC.
52220  *
52221  * Originally Released Under LGPL - original licence link has changed is not relivant.
52222  *
52223  * Fork - LGPL
52224  * <script type="text/javascript">
52225  */
52226 /**
52227  * @class Roo.BorderLayout
52228  * @extends Roo.LayoutManager
52229  * @children Roo.ContentPanel
52230  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52231  * please see: <br><br>
52232  * <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>
52233  * <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>
52234  * Example:
52235  <pre><code>
52236  var layout = new Roo.BorderLayout(document.body, {
52237     north: {
52238         initialSize: 25,
52239         titlebar: false
52240     },
52241     west: {
52242         split:true,
52243         initialSize: 200,
52244         minSize: 175,
52245         maxSize: 400,
52246         titlebar: true,
52247         collapsible: true
52248     },
52249     east: {
52250         split:true,
52251         initialSize: 202,
52252         minSize: 175,
52253         maxSize: 400,
52254         titlebar: true,
52255         collapsible: true
52256     },
52257     south: {
52258         split:true,
52259         initialSize: 100,
52260         minSize: 100,
52261         maxSize: 200,
52262         titlebar: true,
52263         collapsible: true
52264     },
52265     center: {
52266         titlebar: true,
52267         autoScroll:true,
52268         resizeTabs: true,
52269         minTabWidth: 50,
52270         preferredTabWidth: 150
52271     }
52272 });
52273
52274 // shorthand
52275 var CP = Roo.ContentPanel;
52276
52277 layout.beginUpdate();
52278 layout.add("north", new CP("north", "North"));
52279 layout.add("south", new CP("south", {title: "South", closable: true}));
52280 layout.add("west", new CP("west", {title: "West"}));
52281 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52282 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52283 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52284 layout.getRegion("center").showPanel("center1");
52285 layout.endUpdate();
52286 </code></pre>
52287
52288 <b>The container the layout is rendered into can be either the body element or any other element.
52289 If it is not the body element, the container needs to either be an absolute positioned element,
52290 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52291 the container size if it is not the body element.</b>
52292
52293 * @constructor
52294 * Create a new BorderLayout
52295 * @param {String/HTMLElement/Element} container The container this layout is bound to
52296 * @param {Object} config Configuration options
52297  */
52298 Roo.BorderLayout = function(container, config){
52299     config = config || {};
52300     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52301     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52302     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52303         var target = this.factory.validRegions[i];
52304         if(config[target]){
52305             this.addRegion(target, config[target]);
52306         }
52307     }
52308 };
52309
52310 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52311         
52312         /**
52313          * @cfg {Roo.LayoutRegion} east
52314          */
52315         /**
52316          * @cfg {Roo.LayoutRegion} west
52317          */
52318         /**
52319          * @cfg {Roo.LayoutRegion} north
52320          */
52321         /**
52322          * @cfg {Roo.LayoutRegion} south
52323          */
52324         /**
52325          * @cfg {Roo.LayoutRegion} center
52326          */
52327     /**
52328      * Creates and adds a new region if it doesn't already exist.
52329      * @param {String} target The target region key (north, south, east, west or center).
52330      * @param {Object} config The regions config object
52331      * @return {BorderLayoutRegion} The new region
52332      */
52333     addRegion : function(target, config){
52334         if(!this.regions[target]){
52335             var r = this.factory.create(target, this, config);
52336             this.bindRegion(target, r);
52337         }
52338         return this.regions[target];
52339     },
52340
52341     // private (kinda)
52342     bindRegion : function(name, r){
52343         this.regions[name] = r;
52344         r.on("visibilitychange", this.layout, this);
52345         r.on("paneladded", this.layout, this);
52346         r.on("panelremoved", this.layout, this);
52347         r.on("invalidated", this.layout, this);
52348         r.on("resized", this.onRegionResized, this);
52349         r.on("collapsed", this.onRegionCollapsed, this);
52350         r.on("expanded", this.onRegionExpanded, this);
52351     },
52352
52353     /**
52354      * Performs a layout update.
52355      */
52356     layout : function(){
52357         if(this.updating) {
52358             return;
52359         }
52360         var size = this.getViewSize();
52361         var w = size.width;
52362         var h = size.height;
52363         var centerW = w;
52364         var centerH = h;
52365         var centerY = 0;
52366         var centerX = 0;
52367         //var x = 0, y = 0;
52368
52369         var rs = this.regions;
52370         var north = rs["north"];
52371         var south = rs["south"]; 
52372         var west = rs["west"];
52373         var east = rs["east"];
52374         var center = rs["center"];
52375         //if(this.hideOnLayout){ // not supported anymore
52376             //c.el.setStyle("display", "none");
52377         //}
52378         if(north && north.isVisible()){
52379             var b = north.getBox();
52380             var m = north.getMargins();
52381             b.width = w - (m.left+m.right);
52382             b.x = m.left;
52383             b.y = m.top;
52384             centerY = b.height + b.y + m.bottom;
52385             centerH -= centerY;
52386             north.updateBox(this.safeBox(b));
52387         }
52388         if(south && south.isVisible()){
52389             var b = south.getBox();
52390             var m = south.getMargins();
52391             b.width = w - (m.left+m.right);
52392             b.x = m.left;
52393             var totalHeight = (b.height + m.top + m.bottom);
52394             b.y = h - totalHeight + m.top;
52395             centerH -= totalHeight;
52396             south.updateBox(this.safeBox(b));
52397         }
52398         if(west && west.isVisible()){
52399             var b = west.getBox();
52400             var m = west.getMargins();
52401             b.height = centerH - (m.top+m.bottom);
52402             b.x = m.left;
52403             b.y = centerY + m.top;
52404             var totalWidth = (b.width + m.left + m.right);
52405             centerX += totalWidth;
52406             centerW -= totalWidth;
52407             west.updateBox(this.safeBox(b));
52408         }
52409         if(east && east.isVisible()){
52410             var b = east.getBox();
52411             var m = east.getMargins();
52412             b.height = centerH - (m.top+m.bottom);
52413             var totalWidth = (b.width + m.left + m.right);
52414             b.x = w - totalWidth + m.left;
52415             b.y = centerY + m.top;
52416             centerW -= totalWidth;
52417             east.updateBox(this.safeBox(b));
52418         }
52419         if(center){
52420             var m = center.getMargins();
52421             var centerBox = {
52422                 x: centerX + m.left,
52423                 y: centerY + m.top,
52424                 width: centerW - (m.left+m.right),
52425                 height: centerH - (m.top+m.bottom)
52426             };
52427             //if(this.hideOnLayout){
52428                 //center.el.setStyle("display", "block");
52429             //}
52430             center.updateBox(this.safeBox(centerBox));
52431         }
52432         this.el.repaint();
52433         this.fireEvent("layout", this);
52434     },
52435
52436     // private
52437     safeBox : function(box){
52438         box.width = Math.max(0, box.width);
52439         box.height = Math.max(0, box.height);
52440         return box;
52441     },
52442
52443     /**
52444      * Adds a ContentPanel (or subclass) to this layout.
52445      * @param {String} target The target region key (north, south, east, west or center).
52446      * @param {Roo.ContentPanel} panel The panel to add
52447      * @return {Roo.ContentPanel} The added panel
52448      */
52449     add : function(target, panel){
52450          
52451         target = target.toLowerCase();
52452         return this.regions[target].add(panel);
52453     },
52454
52455     /**
52456      * Remove a ContentPanel (or subclass) to this layout.
52457      * @param {String} target The target region key (north, south, east, west or center).
52458      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52459      * @return {Roo.ContentPanel} The removed panel
52460      */
52461     remove : function(target, panel){
52462         target = target.toLowerCase();
52463         return this.regions[target].remove(panel);
52464     },
52465
52466     /**
52467      * Searches all regions for a panel with the specified id
52468      * @param {String} panelId
52469      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52470      */
52471     findPanel : function(panelId){
52472         var rs = this.regions;
52473         for(var target in rs){
52474             if(typeof rs[target] != "function"){
52475                 var p = rs[target].getPanel(panelId);
52476                 if(p){
52477                     return p;
52478                 }
52479             }
52480         }
52481         return null;
52482     },
52483
52484     /**
52485      * Searches all regions for a panel with the specified id and activates (shows) it.
52486      * @param {String/ContentPanel} panelId The panels id or the panel itself
52487      * @return {Roo.ContentPanel} The shown panel or null
52488      */
52489     showPanel : function(panelId) {
52490       var rs = this.regions;
52491       for(var target in rs){
52492          var r = rs[target];
52493          if(typeof r != "function"){
52494             if(r.hasPanel(panelId)){
52495                return r.showPanel(panelId);
52496             }
52497          }
52498       }
52499       return null;
52500    },
52501
52502    /**
52503      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52504      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52505      */
52506     restoreState : function(provider){
52507         if(!provider){
52508             provider = Roo.state.Manager;
52509         }
52510         var sm = new Roo.LayoutStateManager();
52511         sm.init(this, provider);
52512     },
52513
52514     /**
52515      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52516      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52517      * a valid ContentPanel config object.  Example:
52518      * <pre><code>
52519 // Create the main layout
52520 var layout = new Roo.BorderLayout('main-ct', {
52521     west: {
52522         split:true,
52523         minSize: 175,
52524         titlebar: true
52525     },
52526     center: {
52527         title:'Components'
52528     }
52529 }, 'main-ct');
52530
52531 // Create and add multiple ContentPanels at once via configs
52532 layout.batchAdd({
52533    west: {
52534        id: 'source-files',
52535        autoCreate:true,
52536        title:'Ext Source Files',
52537        autoScroll:true,
52538        fitToFrame:true
52539    },
52540    center : {
52541        el: cview,
52542        autoScroll:true,
52543        fitToFrame:true,
52544        toolbar: tb,
52545        resizeEl:'cbody'
52546    }
52547 });
52548 </code></pre>
52549      * @param {Object} regions An object containing ContentPanel configs by region name
52550      */
52551     batchAdd : function(regions){
52552         this.beginUpdate();
52553         for(var rname in regions){
52554             var lr = this.regions[rname];
52555             if(lr){
52556                 this.addTypedPanels(lr, regions[rname]);
52557             }
52558         }
52559         this.endUpdate();
52560     },
52561
52562     // private
52563     addTypedPanels : function(lr, ps){
52564         if(typeof ps == 'string'){
52565             lr.add(new Roo.ContentPanel(ps));
52566         }
52567         else if(ps instanceof Array){
52568             for(var i =0, len = ps.length; i < len; i++){
52569                 this.addTypedPanels(lr, ps[i]);
52570             }
52571         }
52572         else if(!ps.events){ // raw config?
52573             var el = ps.el;
52574             delete ps.el; // prevent conflict
52575             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52576         }
52577         else {  // panel object assumed!
52578             lr.add(ps);
52579         }
52580     },
52581     /**
52582      * Adds a xtype elements to the layout.
52583      * <pre><code>
52584
52585 layout.addxtype({
52586        xtype : 'ContentPanel',
52587        region: 'west',
52588        items: [ .... ]
52589    }
52590 );
52591
52592 layout.addxtype({
52593         xtype : 'NestedLayoutPanel',
52594         region: 'west',
52595         layout: {
52596            center: { },
52597            west: { }   
52598         },
52599         items : [ ... list of content panels or nested layout panels.. ]
52600    }
52601 );
52602 </code></pre>
52603      * @param {Object} cfg Xtype definition of item to add.
52604      */
52605     addxtype : function(cfg)
52606     {
52607         // basically accepts a pannel...
52608         // can accept a layout region..!?!?
52609         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52610         
52611         if (!cfg.xtype.match(/Panel$/)) {
52612             return false;
52613         }
52614         var ret = false;
52615         
52616         if (typeof(cfg.region) == 'undefined') {
52617             Roo.log("Failed to add Panel, region was not set");
52618             Roo.log(cfg);
52619             return false;
52620         }
52621         var region = cfg.region;
52622         delete cfg.region;
52623         
52624           
52625         var xitems = [];
52626         if (cfg.items) {
52627             xitems = cfg.items;
52628             delete cfg.items;
52629         }
52630         var nb = false;
52631         
52632         switch(cfg.xtype) 
52633         {
52634             case 'ContentPanel':  // ContentPanel (el, cfg)
52635             case 'ScrollPanel':  // ContentPanel (el, cfg)
52636             case 'ViewPanel': 
52637                 if(cfg.autoCreate) {
52638                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52639                 } else {
52640                     var el = this.el.createChild();
52641                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52642                 }
52643                 
52644                 this.add(region, ret);
52645                 break;
52646             
52647             
52648             case 'TreePanel': // our new panel!
52649                 cfg.el = this.el.createChild();
52650                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52651                 this.add(region, ret);
52652                 break;
52653             
52654             case 'NestedLayoutPanel': 
52655                 // create a new Layout (which is  a Border Layout...
52656                 var el = this.el.createChild();
52657                 var clayout = cfg.layout;
52658                 delete cfg.layout;
52659                 clayout.items   = clayout.items  || [];
52660                 // replace this exitems with the clayout ones..
52661                 xitems = clayout.items;
52662                  
52663                 
52664                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52665                     cfg.background = false;
52666                 }
52667                 var layout = new Roo.BorderLayout(el, clayout);
52668                 
52669                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52670                 //console.log('adding nested layout panel '  + cfg.toSource());
52671                 this.add(region, ret);
52672                 nb = {}; /// find first...
52673                 break;
52674                 
52675             case 'GridPanel': 
52676             
52677                 // needs grid and region
52678                 
52679                 //var el = this.getRegion(region).el.createChild();
52680                 var el = this.el.createChild();
52681                 // create the grid first...
52682                 
52683                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52684                 delete cfg.grid;
52685                 if (region == 'center' && this.active ) {
52686                     cfg.background = false;
52687                 }
52688                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52689                 
52690                 this.add(region, ret);
52691                 if (cfg.background) {
52692                     ret.on('activate', function(gp) {
52693                         if (!gp.grid.rendered) {
52694                             gp.grid.render();
52695                         }
52696                     });
52697                 } else {
52698                     grid.render();
52699                 }
52700                 break;
52701            
52702            
52703            
52704                 
52705                 
52706                 
52707             default:
52708                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52709                     
52710                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52711                     this.add(region, ret);
52712                 } else {
52713                 
52714                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52715                     return null;
52716                 }
52717                 
52718              // GridPanel (grid, cfg)
52719             
52720         }
52721         this.beginUpdate();
52722         // add children..
52723         var region = '';
52724         var abn = {};
52725         Roo.each(xitems, function(i)  {
52726             region = nb && i.region ? i.region : false;
52727             
52728             var add = ret.addxtype(i);
52729            
52730             if (region) {
52731                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52732                 if (!i.background) {
52733                     abn[region] = nb[region] ;
52734                 }
52735             }
52736             
52737         });
52738         this.endUpdate();
52739
52740         // make the last non-background panel active..
52741         //if (nb) { Roo.log(abn); }
52742         if (nb) {
52743             
52744             for(var r in abn) {
52745                 region = this.getRegion(r);
52746                 if (region) {
52747                     // tried using nb[r], but it does not work..
52748                      
52749                     region.showPanel(abn[r]);
52750                    
52751                 }
52752             }
52753         }
52754         return ret;
52755         
52756     }
52757 });
52758
52759 /**
52760  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52761  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52762  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52763  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52764  * <pre><code>
52765 // shorthand
52766 var CP = Roo.ContentPanel;
52767
52768 var layout = Roo.BorderLayout.create({
52769     north: {
52770         initialSize: 25,
52771         titlebar: false,
52772         panels: [new CP("north", "North")]
52773     },
52774     west: {
52775         split:true,
52776         initialSize: 200,
52777         minSize: 175,
52778         maxSize: 400,
52779         titlebar: true,
52780         collapsible: true,
52781         panels: [new CP("west", {title: "West"})]
52782     },
52783     east: {
52784         split:true,
52785         initialSize: 202,
52786         minSize: 175,
52787         maxSize: 400,
52788         titlebar: true,
52789         collapsible: true,
52790         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52791     },
52792     south: {
52793         split:true,
52794         initialSize: 100,
52795         minSize: 100,
52796         maxSize: 200,
52797         titlebar: true,
52798         collapsible: true,
52799         panels: [new CP("south", {title: "South", closable: true})]
52800     },
52801     center: {
52802         titlebar: true,
52803         autoScroll:true,
52804         resizeTabs: true,
52805         minTabWidth: 50,
52806         preferredTabWidth: 150,
52807         panels: [
52808             new CP("center1", {title: "Close Me", closable: true}),
52809             new CP("center2", {title: "Center Panel", closable: false})
52810         ]
52811     }
52812 }, document.body);
52813
52814 layout.getRegion("center").showPanel("center1");
52815 </code></pre>
52816  * @param config
52817  * @param targetEl
52818  */
52819 Roo.BorderLayout.create = function(config, targetEl){
52820     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52821     layout.beginUpdate();
52822     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52823     for(var j = 0, jlen = regions.length; j < jlen; j++){
52824         var lr = regions[j];
52825         if(layout.regions[lr] && config[lr].panels){
52826             var r = layout.regions[lr];
52827             var ps = config[lr].panels;
52828             layout.addTypedPanels(r, ps);
52829         }
52830     }
52831     layout.endUpdate();
52832     return layout;
52833 };
52834
52835 // private
52836 Roo.BorderLayout.RegionFactory = {
52837     // private
52838     validRegions : ["north","south","east","west","center"],
52839
52840     // private
52841     create : function(target, mgr, config){
52842         target = target.toLowerCase();
52843         if(config.lightweight || config.basic){
52844             return new Roo.BasicLayoutRegion(mgr, config, target);
52845         }
52846         switch(target){
52847             case "north":
52848                 return new Roo.NorthLayoutRegion(mgr, config);
52849             case "south":
52850                 return new Roo.SouthLayoutRegion(mgr, config);
52851             case "east":
52852                 return new Roo.EastLayoutRegion(mgr, config);
52853             case "west":
52854                 return new Roo.WestLayoutRegion(mgr, config);
52855             case "center":
52856                 return new Roo.CenterLayoutRegion(mgr, config);
52857         }
52858         throw 'Layout region "'+target+'" not supported.';
52859     }
52860 };/*
52861  * Based on:
52862  * Ext JS Library 1.1.1
52863  * Copyright(c) 2006-2007, Ext JS, LLC.
52864  *
52865  * Originally Released Under LGPL - original licence link has changed is not relivant.
52866  *
52867  * Fork - LGPL
52868  * <script type="text/javascript">
52869  */
52870  
52871 /**
52872  * @class Roo.BasicLayoutRegion
52873  * @extends Roo.util.Observable
52874  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52875  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52876  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52877  */
52878 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52879     this.mgr = mgr;
52880     this.position  = pos;
52881     this.events = {
52882         /**
52883          * @scope Roo.BasicLayoutRegion
52884          */
52885         
52886         /**
52887          * @event beforeremove
52888          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52889          * @param {Roo.LayoutRegion} this
52890          * @param {Roo.ContentPanel} panel The panel
52891          * @param {Object} e The cancel event object
52892          */
52893         "beforeremove" : true,
52894         /**
52895          * @event invalidated
52896          * Fires when the layout for this region is changed.
52897          * @param {Roo.LayoutRegion} this
52898          */
52899         "invalidated" : true,
52900         /**
52901          * @event visibilitychange
52902          * Fires when this region is shown or hidden 
52903          * @param {Roo.LayoutRegion} this
52904          * @param {Boolean} visibility true or false
52905          */
52906         "visibilitychange" : true,
52907         /**
52908          * @event paneladded
52909          * Fires when a panel is added. 
52910          * @param {Roo.LayoutRegion} this
52911          * @param {Roo.ContentPanel} panel The panel
52912          */
52913         "paneladded" : true,
52914         /**
52915          * @event panelremoved
52916          * Fires when a panel is removed. 
52917          * @param {Roo.LayoutRegion} this
52918          * @param {Roo.ContentPanel} panel The panel
52919          */
52920         "panelremoved" : true,
52921         /**
52922          * @event beforecollapse
52923          * Fires when this region before collapse.
52924          * @param {Roo.LayoutRegion} this
52925          */
52926         "beforecollapse" : true,
52927         /**
52928          * @event collapsed
52929          * Fires when this region is collapsed.
52930          * @param {Roo.LayoutRegion} this
52931          */
52932         "collapsed" : true,
52933         /**
52934          * @event expanded
52935          * Fires when this region is expanded.
52936          * @param {Roo.LayoutRegion} this
52937          */
52938         "expanded" : true,
52939         /**
52940          * @event slideshow
52941          * Fires when this region is slid into view.
52942          * @param {Roo.LayoutRegion} this
52943          */
52944         "slideshow" : true,
52945         /**
52946          * @event slidehide
52947          * Fires when this region slides out of view. 
52948          * @param {Roo.LayoutRegion} this
52949          */
52950         "slidehide" : true,
52951         /**
52952          * @event panelactivated
52953          * Fires when a panel is activated. 
52954          * @param {Roo.LayoutRegion} this
52955          * @param {Roo.ContentPanel} panel The activated panel
52956          */
52957         "panelactivated" : true,
52958         /**
52959          * @event resized
52960          * Fires when the user resizes this region. 
52961          * @param {Roo.LayoutRegion} this
52962          * @param {Number} newSize The new size (width for east/west, height for north/south)
52963          */
52964         "resized" : true
52965     };
52966     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52967     this.panels = new Roo.util.MixedCollection();
52968     this.panels.getKey = this.getPanelId.createDelegate(this);
52969     this.box = null;
52970     this.activePanel = null;
52971     // ensure listeners are added...
52972     
52973     if (config.listeners || config.events) {
52974         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52975             listeners : config.listeners || {},
52976             events : config.events || {}
52977         });
52978     }
52979     
52980     if(skipConfig !== true){
52981         this.applyConfig(config);
52982     }
52983 };
52984
52985 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52986     getPanelId : function(p){
52987         return p.getId();
52988     },
52989     
52990     applyConfig : function(config){
52991         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52992         this.config = config;
52993         
52994     },
52995     
52996     /**
52997      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52998      * the width, for horizontal (north, south) the height.
52999      * @param {Number} newSize The new width or height
53000      */
53001     resizeTo : function(newSize){
53002         var el = this.el ? this.el :
53003                  (this.activePanel ? this.activePanel.getEl() : null);
53004         if(el){
53005             switch(this.position){
53006                 case "east":
53007                 case "west":
53008                     el.setWidth(newSize);
53009                     this.fireEvent("resized", this, newSize);
53010                 break;
53011                 case "north":
53012                 case "south":
53013                     el.setHeight(newSize);
53014                     this.fireEvent("resized", this, newSize);
53015                 break;                
53016             }
53017         }
53018     },
53019     
53020     getBox : function(){
53021         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53022     },
53023     
53024     getMargins : function(){
53025         return this.margins;
53026     },
53027     
53028     updateBox : function(box){
53029         this.box = box;
53030         var el = this.activePanel.getEl();
53031         el.dom.style.left = box.x + "px";
53032         el.dom.style.top = box.y + "px";
53033         this.activePanel.setSize(box.width, box.height);
53034     },
53035     
53036     /**
53037      * Returns the container element for this region.
53038      * @return {Roo.Element}
53039      */
53040     getEl : function(){
53041         return this.activePanel;
53042     },
53043     
53044     /**
53045      * Returns true if this region is currently visible.
53046      * @return {Boolean}
53047      */
53048     isVisible : function(){
53049         return this.activePanel ? true : false;
53050     },
53051     
53052     setActivePanel : function(panel){
53053         panel = this.getPanel(panel);
53054         if(this.activePanel && this.activePanel != panel){
53055             this.activePanel.setActiveState(false);
53056             this.activePanel.getEl().setLeftTop(-10000,-10000);
53057         }
53058         this.activePanel = panel;
53059         panel.setActiveState(true);
53060         if(this.box){
53061             panel.setSize(this.box.width, this.box.height);
53062         }
53063         this.fireEvent("panelactivated", this, panel);
53064         this.fireEvent("invalidated");
53065     },
53066     
53067     /**
53068      * Show the specified panel.
53069      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53070      * @return {Roo.ContentPanel} The shown panel or null
53071      */
53072     showPanel : function(panel){
53073         if(panel = this.getPanel(panel)){
53074             this.setActivePanel(panel);
53075         }
53076         return panel;
53077     },
53078     
53079     /**
53080      * Get the active panel for this region.
53081      * @return {Roo.ContentPanel} The active panel or null
53082      */
53083     getActivePanel : function(){
53084         return this.activePanel;
53085     },
53086     
53087     /**
53088      * Add the passed ContentPanel(s)
53089      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53090      * @return {Roo.ContentPanel} The panel added (if only one was added)
53091      */
53092     add : function(panel){
53093         if(arguments.length > 1){
53094             for(var i = 0, len = arguments.length; i < len; i++) {
53095                 this.add(arguments[i]);
53096             }
53097             return null;
53098         }
53099         if(this.hasPanel(panel)){
53100             this.showPanel(panel);
53101             return panel;
53102         }
53103         var el = panel.getEl();
53104         if(el.dom.parentNode != this.mgr.el.dom){
53105             this.mgr.el.dom.appendChild(el.dom);
53106         }
53107         if(panel.setRegion){
53108             panel.setRegion(this);
53109         }
53110         this.panels.add(panel);
53111         el.setStyle("position", "absolute");
53112         if(!panel.background){
53113             this.setActivePanel(panel);
53114             if(this.config.initialSize && this.panels.getCount()==1){
53115                 this.resizeTo(this.config.initialSize);
53116             }
53117         }
53118         this.fireEvent("paneladded", this, panel);
53119         return panel;
53120     },
53121     
53122     /**
53123      * Returns true if the panel is in this region.
53124      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53125      * @return {Boolean}
53126      */
53127     hasPanel : function(panel){
53128         if(typeof panel == "object"){ // must be panel obj
53129             panel = panel.getId();
53130         }
53131         return this.getPanel(panel) ? true : false;
53132     },
53133     
53134     /**
53135      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53137      * @param {Boolean} preservePanel Overrides the config preservePanel option
53138      * @return {Roo.ContentPanel} The panel that was removed
53139      */
53140     remove : function(panel, preservePanel){
53141         panel = this.getPanel(panel);
53142         if(!panel){
53143             return null;
53144         }
53145         var e = {};
53146         this.fireEvent("beforeremove", this, panel, e);
53147         if(e.cancel === true){
53148             return null;
53149         }
53150         var panelId = panel.getId();
53151         this.panels.removeKey(panelId);
53152         return panel;
53153     },
53154     
53155     /**
53156      * Returns the panel specified or null if it's not in this region.
53157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53158      * @return {Roo.ContentPanel}
53159      */
53160     getPanel : function(id){
53161         if(typeof id == "object"){ // must be panel obj
53162             return id;
53163         }
53164         return this.panels.get(id);
53165     },
53166     
53167     /**
53168      * Returns this regions position (north/south/east/west/center).
53169      * @return {String} 
53170      */
53171     getPosition: function(){
53172         return this.position;    
53173     }
53174 });/*
53175  * Based on:
53176  * Ext JS Library 1.1.1
53177  * Copyright(c) 2006-2007, Ext JS, LLC.
53178  *
53179  * Originally Released Under LGPL - original licence link has changed is not relivant.
53180  *
53181  * Fork - LGPL
53182  * <script type="text/javascript">
53183  */
53184  
53185 /**
53186  * @class Roo.LayoutRegion
53187  * @extends Roo.BasicLayoutRegion
53188  * This class represents a region in a layout manager.
53189  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53190  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53191  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53192  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53193  * @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})
53194  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53195  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53196  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53197  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53198  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53199  * @cfg {String}    title           The title for the region (overrides panel titles)
53200  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53201  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53202  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53203  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53204  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53205  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53206  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53207  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53208  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53209  * @cfg {Boolean}   showPin         True to show a pin button
53210  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53211  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53212  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53213  * @cfg {Number}    width           For East/West panels
53214  * @cfg {Number}    height          For North/South panels
53215  * @cfg {Boolean}   split           To show the splitter
53216  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53217  */
53218 Roo.LayoutRegion = function(mgr, config, pos){
53219     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53220     var dh = Roo.DomHelper;
53221     /** This region's container element 
53222     * @type Roo.Element */
53223     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53224     /** This region's title element 
53225     * @type Roo.Element */
53226
53227     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53228         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53229         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53230     ]}, true);
53231     this.titleEl.enableDisplayMode();
53232     /** This region's title text element 
53233     * @type HTMLElement */
53234     this.titleTextEl = this.titleEl.dom.firstChild;
53235     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53236     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53237     this.closeBtn.enableDisplayMode();
53238     this.closeBtn.on("click", this.closeClicked, this);
53239     this.closeBtn.hide();
53240
53241     this.createBody(config);
53242     this.visible = true;
53243     this.collapsed = false;
53244
53245     if(config.hideWhenEmpty){
53246         this.hide();
53247         this.on("paneladded", this.validateVisibility, this);
53248         this.on("panelremoved", this.validateVisibility, this);
53249     }
53250     this.applyConfig(config);
53251 };
53252
53253 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53254
53255     createBody : function(){
53256         /** This region's body element 
53257         * @type Roo.Element */
53258         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53259     },
53260
53261     applyConfig : function(c){
53262         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53263             var dh = Roo.DomHelper;
53264             if(c.titlebar !== false){
53265                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53266                 this.collapseBtn.on("click", this.collapse, this);
53267                 this.collapseBtn.enableDisplayMode();
53268
53269                 if(c.showPin === true || this.showPin){
53270                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53271                     this.stickBtn.enableDisplayMode();
53272                     this.stickBtn.on("click", this.expand, this);
53273                     this.stickBtn.hide();
53274                 }
53275             }
53276             /** This region's collapsed element
53277             * @type Roo.Element */
53278             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53279                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53280             ]}, true);
53281             if(c.floatable !== false){
53282                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53283                this.collapsedEl.on("click", this.collapseClick, this);
53284             }
53285
53286             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53287                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53288                    id: "message", unselectable: "on", style:{"float":"left"}});
53289                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53290              }
53291             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53292             this.expandBtn.on("click", this.expand, this);
53293         }
53294         if(this.collapseBtn){
53295             this.collapseBtn.setVisible(c.collapsible == true);
53296         }
53297         this.cmargins = c.cmargins || this.cmargins ||
53298                          (this.position == "west" || this.position == "east" ?
53299                              {top: 0, left: 2, right:2, bottom: 0} :
53300                              {top: 2, left: 0, right:0, bottom: 2});
53301         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53302         this.bottomTabs = c.tabPosition != "top";
53303         this.autoScroll = c.autoScroll || false;
53304         if(this.autoScroll){
53305             this.bodyEl.setStyle("overflow", "auto");
53306         }else{
53307             this.bodyEl.setStyle("overflow", "hidden");
53308         }
53309         //if(c.titlebar !== false){
53310             if((!c.titlebar && !c.title) || c.titlebar === false){
53311                 this.titleEl.hide();
53312             }else{
53313                 this.titleEl.show();
53314                 if(c.title){
53315                     this.titleTextEl.innerHTML = c.title;
53316                 }
53317             }
53318         //}
53319         this.duration = c.duration || .30;
53320         this.slideDuration = c.slideDuration || .45;
53321         this.config = c;
53322         if(c.collapsed){
53323             this.collapse(true);
53324         }
53325         if(c.hidden){
53326             this.hide();
53327         }
53328     },
53329     /**
53330      * Returns true if this region is currently visible.
53331      * @return {Boolean}
53332      */
53333     isVisible : function(){
53334         return this.visible;
53335     },
53336
53337     /**
53338      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53339      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53340      */
53341     setCollapsedTitle : function(title){
53342         title = title || "&#160;";
53343         if(this.collapsedTitleTextEl){
53344             this.collapsedTitleTextEl.innerHTML = title;
53345         }
53346     },
53347
53348     getBox : function(){
53349         var b;
53350         if(!this.collapsed){
53351             b = this.el.getBox(false, true);
53352         }else{
53353             b = this.collapsedEl.getBox(false, true);
53354         }
53355         return b;
53356     },
53357
53358     getMargins : function(){
53359         return this.collapsed ? this.cmargins : this.margins;
53360     },
53361
53362     highlight : function(){
53363         this.el.addClass("x-layout-panel-dragover");
53364     },
53365
53366     unhighlight : function(){
53367         this.el.removeClass("x-layout-panel-dragover");
53368     },
53369
53370     updateBox : function(box){
53371         this.box = box;
53372         if(!this.collapsed){
53373             this.el.dom.style.left = box.x + "px";
53374             this.el.dom.style.top = box.y + "px";
53375             this.updateBody(box.width, box.height);
53376         }else{
53377             this.collapsedEl.dom.style.left = box.x + "px";
53378             this.collapsedEl.dom.style.top = box.y + "px";
53379             this.collapsedEl.setSize(box.width, box.height);
53380         }
53381         if(this.tabs){
53382             this.tabs.autoSizeTabs();
53383         }
53384     },
53385
53386     updateBody : function(w, h){
53387         if(w !== null){
53388             this.el.setWidth(w);
53389             w -= this.el.getBorderWidth("rl");
53390             if(this.config.adjustments){
53391                 w += this.config.adjustments[0];
53392             }
53393         }
53394         if(h !== null){
53395             this.el.setHeight(h);
53396             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53397             h -= this.el.getBorderWidth("tb");
53398             if(this.config.adjustments){
53399                 h += this.config.adjustments[1];
53400             }
53401             this.bodyEl.setHeight(h);
53402             if(this.tabs){
53403                 h = this.tabs.syncHeight(h);
53404             }
53405         }
53406         if(this.panelSize){
53407             w = w !== null ? w : this.panelSize.width;
53408             h = h !== null ? h : this.panelSize.height;
53409         }
53410         if(this.activePanel){
53411             var el = this.activePanel.getEl();
53412             w = w !== null ? w : el.getWidth();
53413             h = h !== null ? h : el.getHeight();
53414             this.panelSize = {width: w, height: h};
53415             this.activePanel.setSize(w, h);
53416         }
53417         if(Roo.isIE && this.tabs){
53418             this.tabs.el.repaint();
53419         }
53420     },
53421
53422     /**
53423      * Returns the container element for this region.
53424      * @return {Roo.Element}
53425      */
53426     getEl : function(){
53427         return this.el;
53428     },
53429
53430     /**
53431      * Hides this region.
53432      */
53433     hide : function(){
53434         if(!this.collapsed){
53435             this.el.dom.style.left = "-2000px";
53436             this.el.hide();
53437         }else{
53438             this.collapsedEl.dom.style.left = "-2000px";
53439             this.collapsedEl.hide();
53440         }
53441         this.visible = false;
53442         this.fireEvent("visibilitychange", this, false);
53443     },
53444
53445     /**
53446      * Shows this region if it was previously hidden.
53447      */
53448     show : function(){
53449         if(!this.collapsed){
53450             this.el.show();
53451         }else{
53452             this.collapsedEl.show();
53453         }
53454         this.visible = true;
53455         this.fireEvent("visibilitychange", this, true);
53456     },
53457
53458     closeClicked : function(){
53459         if(this.activePanel){
53460             this.remove(this.activePanel);
53461         }
53462     },
53463
53464     collapseClick : function(e){
53465         if(this.isSlid){
53466            e.stopPropagation();
53467            this.slideIn();
53468         }else{
53469            e.stopPropagation();
53470            this.slideOut();
53471         }
53472     },
53473
53474     /**
53475      * Collapses this region.
53476      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53477      */
53478     collapse : function(skipAnim, skipCheck){
53479         if(this.collapsed) {
53480             return;
53481         }
53482         
53483         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53484             
53485             this.collapsed = true;
53486             if(this.split){
53487                 this.split.el.hide();
53488             }
53489             if(this.config.animate && skipAnim !== true){
53490                 this.fireEvent("invalidated", this);
53491                 this.animateCollapse();
53492             }else{
53493                 this.el.setLocation(-20000,-20000);
53494                 this.el.hide();
53495                 this.collapsedEl.show();
53496                 this.fireEvent("collapsed", this);
53497                 this.fireEvent("invalidated", this);
53498             }
53499         }
53500         
53501     },
53502
53503     animateCollapse : function(){
53504         // overridden
53505     },
53506
53507     /**
53508      * Expands this region if it was previously collapsed.
53509      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53510      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53511      */
53512     expand : function(e, skipAnim){
53513         if(e) {
53514             e.stopPropagation();
53515         }
53516         if(!this.collapsed || this.el.hasActiveFx()) {
53517             return;
53518         }
53519         if(this.isSlid){
53520             this.afterSlideIn();
53521             skipAnim = true;
53522         }
53523         this.collapsed = false;
53524         if(this.config.animate && skipAnim !== true){
53525             this.animateExpand();
53526         }else{
53527             this.el.show();
53528             if(this.split){
53529                 this.split.el.show();
53530             }
53531             this.collapsedEl.setLocation(-2000,-2000);
53532             this.collapsedEl.hide();
53533             this.fireEvent("invalidated", this);
53534             this.fireEvent("expanded", this);
53535         }
53536     },
53537
53538     animateExpand : function(){
53539         // overridden
53540     },
53541
53542     initTabs : function()
53543     {
53544         this.bodyEl.setStyle("overflow", "hidden");
53545         var ts = new Roo.TabPanel(
53546                 this.bodyEl.dom,
53547                 {
53548                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53549                     disableTooltips: this.config.disableTabTips,
53550                     toolbar : this.config.toolbar
53551                 }
53552         );
53553         if(this.config.hideTabs){
53554             ts.stripWrap.setDisplayed(false);
53555         }
53556         this.tabs = ts;
53557         ts.resizeTabs = this.config.resizeTabs === true;
53558         ts.minTabWidth = this.config.minTabWidth || 40;
53559         ts.maxTabWidth = this.config.maxTabWidth || 250;
53560         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53561         ts.monitorResize = false;
53562         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53563         ts.bodyEl.addClass('x-layout-tabs-body');
53564         this.panels.each(this.initPanelAsTab, this);
53565     },
53566
53567     initPanelAsTab : function(panel){
53568         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53569                     this.config.closeOnTab && panel.isClosable());
53570         if(panel.tabTip !== undefined){
53571             ti.setTooltip(panel.tabTip);
53572         }
53573         ti.on("activate", function(){
53574               this.setActivePanel(panel);
53575         }, this);
53576         if(this.config.closeOnTab){
53577             ti.on("beforeclose", function(t, e){
53578                 e.cancel = true;
53579                 this.remove(panel);
53580             }, this);
53581         }
53582         return ti;
53583     },
53584
53585     updatePanelTitle : function(panel, title){
53586         if(this.activePanel == panel){
53587             this.updateTitle(title);
53588         }
53589         if(this.tabs){
53590             var ti = this.tabs.getTab(panel.getEl().id);
53591             ti.setText(title);
53592             if(panel.tabTip !== undefined){
53593                 ti.setTooltip(panel.tabTip);
53594             }
53595         }
53596     },
53597
53598     updateTitle : function(title){
53599         if(this.titleTextEl && !this.config.title){
53600             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53601         }
53602     },
53603
53604     setActivePanel : function(panel){
53605         panel = this.getPanel(panel);
53606         if(this.activePanel && this.activePanel != panel){
53607             this.activePanel.setActiveState(false);
53608         }
53609         this.activePanel = panel;
53610         panel.setActiveState(true);
53611         if(this.panelSize){
53612             panel.setSize(this.panelSize.width, this.panelSize.height);
53613         }
53614         if(this.closeBtn){
53615             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53616         }
53617         this.updateTitle(panel.getTitle());
53618         if(this.tabs){
53619             this.fireEvent("invalidated", this);
53620         }
53621         this.fireEvent("panelactivated", this, panel);
53622     },
53623
53624     /**
53625      * Shows the specified panel.
53626      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53627      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53628      */
53629     showPanel : function(panel)
53630     {
53631         panel = this.getPanel(panel);
53632         if(panel){
53633             if(this.tabs){
53634                 var tab = this.tabs.getTab(panel.getEl().id);
53635                 if(tab.isHidden()){
53636                     this.tabs.unhideTab(tab.id);
53637                 }
53638                 tab.activate();
53639             }else{
53640                 this.setActivePanel(panel);
53641             }
53642         }
53643         return panel;
53644     },
53645
53646     /**
53647      * Get the active panel for this region.
53648      * @return {Roo.ContentPanel} The active panel or null
53649      */
53650     getActivePanel : function(){
53651         return this.activePanel;
53652     },
53653
53654     validateVisibility : function(){
53655         if(this.panels.getCount() < 1){
53656             this.updateTitle("&#160;");
53657             this.closeBtn.hide();
53658             this.hide();
53659         }else{
53660             if(!this.isVisible()){
53661                 this.show();
53662             }
53663         }
53664     },
53665
53666     /**
53667      * Adds the passed ContentPanel(s) to this region.
53668      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53669      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53670      */
53671     add : function(panel){
53672         if(arguments.length > 1){
53673             for(var i = 0, len = arguments.length; i < len; i++) {
53674                 this.add(arguments[i]);
53675             }
53676             return null;
53677         }
53678         if(this.hasPanel(panel)){
53679             this.showPanel(panel);
53680             return panel;
53681         }
53682         panel.setRegion(this);
53683         this.panels.add(panel);
53684         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53685             this.bodyEl.dom.appendChild(panel.getEl().dom);
53686             if(panel.background !== true){
53687                 this.setActivePanel(panel);
53688             }
53689             this.fireEvent("paneladded", this, panel);
53690             return panel;
53691         }
53692         if(!this.tabs){
53693             this.initTabs();
53694         }else{
53695             this.initPanelAsTab(panel);
53696         }
53697         if(panel.background !== true){
53698             this.tabs.activate(panel.getEl().id);
53699         }
53700         this.fireEvent("paneladded", this, panel);
53701         return panel;
53702     },
53703
53704     /**
53705      * Hides the tab for the specified panel.
53706      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53707      */
53708     hidePanel : function(panel){
53709         if(this.tabs && (panel = this.getPanel(panel))){
53710             this.tabs.hideTab(panel.getEl().id);
53711         }
53712     },
53713
53714     /**
53715      * Unhides the tab for a previously hidden panel.
53716      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53717      */
53718     unhidePanel : function(panel){
53719         if(this.tabs && (panel = this.getPanel(panel))){
53720             this.tabs.unhideTab(panel.getEl().id);
53721         }
53722     },
53723
53724     clearPanels : function(){
53725         while(this.panels.getCount() > 0){
53726              this.remove(this.panels.first());
53727         }
53728     },
53729
53730     /**
53731      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53732      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53733      * @param {Boolean} preservePanel Overrides the config preservePanel option
53734      * @return {Roo.ContentPanel} The panel that was removed
53735      */
53736     remove : function(panel, preservePanel){
53737         panel = this.getPanel(panel);
53738         if(!panel){
53739             return null;
53740         }
53741         var e = {};
53742         this.fireEvent("beforeremove", this, panel, e);
53743         if(e.cancel === true){
53744             return null;
53745         }
53746         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53747         var panelId = panel.getId();
53748         this.panels.removeKey(panelId);
53749         if(preservePanel){
53750             document.body.appendChild(panel.getEl().dom);
53751         }
53752         if(this.tabs){
53753             this.tabs.removeTab(panel.getEl().id);
53754         }else if (!preservePanel){
53755             this.bodyEl.dom.removeChild(panel.getEl().dom);
53756         }
53757         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53758             var p = this.panels.first();
53759             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53760             tempEl.appendChild(p.getEl().dom);
53761             this.bodyEl.update("");
53762             this.bodyEl.dom.appendChild(p.getEl().dom);
53763             tempEl = null;
53764             this.updateTitle(p.getTitle());
53765             this.tabs = null;
53766             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53767             this.setActivePanel(p);
53768         }
53769         panel.setRegion(null);
53770         if(this.activePanel == panel){
53771             this.activePanel = null;
53772         }
53773         if(this.config.autoDestroy !== false && preservePanel !== true){
53774             try{panel.destroy();}catch(e){}
53775         }
53776         this.fireEvent("panelremoved", this, panel);
53777         return panel;
53778     },
53779
53780     /**
53781      * Returns the TabPanel component used by this region
53782      * @return {Roo.TabPanel}
53783      */
53784     getTabs : function(){
53785         return this.tabs;
53786     },
53787
53788     createTool : function(parentEl, className){
53789         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53790             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53791         btn.addClassOnOver("x-layout-tools-button-over");
53792         return btn;
53793     }
53794 });/*
53795  * Based on:
53796  * Ext JS Library 1.1.1
53797  * Copyright(c) 2006-2007, Ext JS, LLC.
53798  *
53799  * Originally Released Under LGPL - original licence link has changed is not relivant.
53800  *
53801  * Fork - LGPL
53802  * <script type="text/javascript">
53803  */
53804  
53805
53806
53807 /**
53808  * @class Roo.SplitLayoutRegion
53809  * @extends Roo.LayoutRegion
53810  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53811  */
53812 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53813     this.cursor = cursor;
53814     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53815 };
53816
53817 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53818     splitTip : "Drag to resize.",
53819     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53820     useSplitTips : false,
53821
53822     applyConfig : function(config){
53823         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53824         if(config.split){
53825             if(!this.split){
53826                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53827                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53828                 /** The SplitBar for this region 
53829                 * @type Roo.SplitBar */
53830                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53831                 this.split.on("moved", this.onSplitMove, this);
53832                 this.split.useShim = config.useShim === true;
53833                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53834                 if(this.useSplitTips){
53835                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53836                 }
53837                 if(config.collapsible){
53838                     this.split.el.on("dblclick", this.collapse,  this);
53839                 }
53840             }
53841             if(typeof config.minSize != "undefined"){
53842                 this.split.minSize = config.minSize;
53843             }
53844             if(typeof config.maxSize != "undefined"){
53845                 this.split.maxSize = config.maxSize;
53846             }
53847             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53848                 this.hideSplitter();
53849             }
53850         }
53851     },
53852
53853     getHMaxSize : function(){
53854          var cmax = this.config.maxSize || 10000;
53855          var center = this.mgr.getRegion("center");
53856          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53857     },
53858
53859     getVMaxSize : function(){
53860          var cmax = this.config.maxSize || 10000;
53861          var center = this.mgr.getRegion("center");
53862          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53863     },
53864
53865     onSplitMove : function(split, newSize){
53866         this.fireEvent("resized", this, newSize);
53867     },
53868     
53869     /** 
53870      * Returns the {@link Roo.SplitBar} for this region.
53871      * @return {Roo.SplitBar}
53872      */
53873     getSplitBar : function(){
53874         return this.split;
53875     },
53876     
53877     hide : function(){
53878         this.hideSplitter();
53879         Roo.SplitLayoutRegion.superclass.hide.call(this);
53880     },
53881
53882     hideSplitter : function(){
53883         if(this.split){
53884             this.split.el.setLocation(-2000,-2000);
53885             this.split.el.hide();
53886         }
53887     },
53888
53889     show : function(){
53890         if(this.split){
53891             this.split.el.show();
53892         }
53893         Roo.SplitLayoutRegion.superclass.show.call(this);
53894     },
53895     
53896     beforeSlide: function(){
53897         if(Roo.isGecko){// firefox overflow auto bug workaround
53898             this.bodyEl.clip();
53899             if(this.tabs) {
53900                 this.tabs.bodyEl.clip();
53901             }
53902             if(this.activePanel){
53903                 this.activePanel.getEl().clip();
53904                 
53905                 if(this.activePanel.beforeSlide){
53906                     this.activePanel.beforeSlide();
53907                 }
53908             }
53909         }
53910     },
53911     
53912     afterSlide : function(){
53913         if(Roo.isGecko){// firefox overflow auto bug workaround
53914             this.bodyEl.unclip();
53915             if(this.tabs) {
53916                 this.tabs.bodyEl.unclip();
53917             }
53918             if(this.activePanel){
53919                 this.activePanel.getEl().unclip();
53920                 if(this.activePanel.afterSlide){
53921                     this.activePanel.afterSlide();
53922                 }
53923             }
53924         }
53925     },
53926
53927     initAutoHide : function(){
53928         if(this.autoHide !== false){
53929             if(!this.autoHideHd){
53930                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53931                 this.autoHideHd = {
53932                     "mouseout": function(e){
53933                         if(!e.within(this.el, true)){
53934                             st.delay(500);
53935                         }
53936                     },
53937                     "mouseover" : function(e){
53938                         st.cancel();
53939                     },
53940                     scope : this
53941                 };
53942             }
53943             this.el.on(this.autoHideHd);
53944         }
53945     },
53946
53947     clearAutoHide : function(){
53948         if(this.autoHide !== false){
53949             this.el.un("mouseout", this.autoHideHd.mouseout);
53950             this.el.un("mouseover", this.autoHideHd.mouseover);
53951         }
53952     },
53953
53954     clearMonitor : function(){
53955         Roo.get(document).un("click", this.slideInIf, this);
53956     },
53957
53958     // these names are backwards but not changed for compat
53959     slideOut : function(){
53960         if(this.isSlid || this.el.hasActiveFx()){
53961             return;
53962         }
53963         this.isSlid = true;
53964         if(this.collapseBtn){
53965             this.collapseBtn.hide();
53966         }
53967         this.closeBtnState = this.closeBtn.getStyle('display');
53968         this.closeBtn.hide();
53969         if(this.stickBtn){
53970             this.stickBtn.show();
53971         }
53972         this.el.show();
53973         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53974         this.beforeSlide();
53975         this.el.setStyle("z-index", 10001);
53976         this.el.slideIn(this.getSlideAnchor(), {
53977             callback: function(){
53978                 this.afterSlide();
53979                 this.initAutoHide();
53980                 Roo.get(document).on("click", this.slideInIf, this);
53981                 this.fireEvent("slideshow", this);
53982             },
53983             scope: this,
53984             block: true
53985         });
53986     },
53987
53988     afterSlideIn : function(){
53989         this.clearAutoHide();
53990         this.isSlid = false;
53991         this.clearMonitor();
53992         this.el.setStyle("z-index", "");
53993         if(this.collapseBtn){
53994             this.collapseBtn.show();
53995         }
53996         this.closeBtn.setStyle('display', this.closeBtnState);
53997         if(this.stickBtn){
53998             this.stickBtn.hide();
53999         }
54000         this.fireEvent("slidehide", this);
54001     },
54002
54003     slideIn : function(cb){
54004         if(!this.isSlid || this.el.hasActiveFx()){
54005             Roo.callback(cb);
54006             return;
54007         }
54008         this.isSlid = false;
54009         this.beforeSlide();
54010         this.el.slideOut(this.getSlideAnchor(), {
54011             callback: function(){
54012                 this.el.setLeftTop(-10000, -10000);
54013                 this.afterSlide();
54014                 this.afterSlideIn();
54015                 Roo.callback(cb);
54016             },
54017             scope: this,
54018             block: true
54019         });
54020     },
54021     
54022     slideInIf : function(e){
54023         if(!e.within(this.el)){
54024             this.slideIn();
54025         }
54026     },
54027
54028     animateCollapse : function(){
54029         this.beforeSlide();
54030         this.el.setStyle("z-index", 20000);
54031         var anchor = this.getSlideAnchor();
54032         this.el.slideOut(anchor, {
54033             callback : function(){
54034                 this.el.setStyle("z-index", "");
54035                 this.collapsedEl.slideIn(anchor, {duration:.3});
54036                 this.afterSlide();
54037                 this.el.setLocation(-10000,-10000);
54038                 this.el.hide();
54039                 this.fireEvent("collapsed", this);
54040             },
54041             scope: this,
54042             block: true
54043         });
54044     },
54045
54046     animateExpand : function(){
54047         this.beforeSlide();
54048         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54049         this.el.setStyle("z-index", 20000);
54050         this.collapsedEl.hide({
54051             duration:.1
54052         });
54053         this.el.slideIn(this.getSlideAnchor(), {
54054             callback : function(){
54055                 this.el.setStyle("z-index", "");
54056                 this.afterSlide();
54057                 if(this.split){
54058                     this.split.el.show();
54059                 }
54060                 this.fireEvent("invalidated", this);
54061                 this.fireEvent("expanded", this);
54062             },
54063             scope: this,
54064             block: true
54065         });
54066     },
54067
54068     anchors : {
54069         "west" : "left",
54070         "east" : "right",
54071         "north" : "top",
54072         "south" : "bottom"
54073     },
54074
54075     sanchors : {
54076         "west" : "l",
54077         "east" : "r",
54078         "north" : "t",
54079         "south" : "b"
54080     },
54081
54082     canchors : {
54083         "west" : "tl-tr",
54084         "east" : "tr-tl",
54085         "north" : "tl-bl",
54086         "south" : "bl-tl"
54087     },
54088
54089     getAnchor : function(){
54090         return this.anchors[this.position];
54091     },
54092
54093     getCollapseAnchor : function(){
54094         return this.canchors[this.position];
54095     },
54096
54097     getSlideAnchor : function(){
54098         return this.sanchors[this.position];
54099     },
54100
54101     getAlignAdj : function(){
54102         var cm = this.cmargins;
54103         switch(this.position){
54104             case "west":
54105                 return [0, 0];
54106             break;
54107             case "east":
54108                 return [0, 0];
54109             break;
54110             case "north":
54111                 return [0, 0];
54112             break;
54113             case "south":
54114                 return [0, 0];
54115             break;
54116         }
54117     },
54118
54119     getExpandAdj : function(){
54120         var c = this.collapsedEl, cm = this.cmargins;
54121         switch(this.position){
54122             case "west":
54123                 return [-(cm.right+c.getWidth()+cm.left), 0];
54124             break;
54125             case "east":
54126                 return [cm.right+c.getWidth()+cm.left, 0];
54127             break;
54128             case "north":
54129                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54130             break;
54131             case "south":
54132                 return [0, cm.top+cm.bottom+c.getHeight()];
54133             break;
54134         }
54135     }
54136 });/*
54137  * Based on:
54138  * Ext JS Library 1.1.1
54139  * Copyright(c) 2006-2007, Ext JS, LLC.
54140  *
54141  * Originally Released Under LGPL - original licence link has changed is not relivant.
54142  *
54143  * Fork - LGPL
54144  * <script type="text/javascript">
54145  */
54146 /*
54147  * These classes are private internal classes
54148  */
54149 Roo.CenterLayoutRegion = function(mgr, config){
54150     Roo.LayoutRegion.call(this, mgr, config, "center");
54151     this.visible = true;
54152     this.minWidth = config.minWidth || 20;
54153     this.minHeight = config.minHeight || 20;
54154 };
54155
54156 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54157     hide : function(){
54158         // center panel can't be hidden
54159     },
54160     
54161     show : function(){
54162         // center panel can't be hidden
54163     },
54164     
54165     getMinWidth: function(){
54166         return this.minWidth;
54167     },
54168     
54169     getMinHeight: function(){
54170         return this.minHeight;
54171     }
54172 });
54173
54174
54175 Roo.NorthLayoutRegion = function(mgr, config){
54176     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54177     if(this.split){
54178         this.split.placement = Roo.SplitBar.TOP;
54179         this.split.orientation = Roo.SplitBar.VERTICAL;
54180         this.split.el.addClass("x-layout-split-v");
54181     }
54182     var size = config.initialSize || config.height;
54183     if(typeof size != "undefined"){
54184         this.el.setHeight(size);
54185     }
54186 };
54187 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54188     orientation: Roo.SplitBar.VERTICAL,
54189     getBox : function(){
54190         if(this.collapsed){
54191             return this.collapsedEl.getBox();
54192         }
54193         var box = this.el.getBox();
54194         if(this.split){
54195             box.height += this.split.el.getHeight();
54196         }
54197         return box;
54198     },
54199     
54200     updateBox : function(box){
54201         if(this.split && !this.collapsed){
54202             box.height -= this.split.el.getHeight();
54203             this.split.el.setLeft(box.x);
54204             this.split.el.setTop(box.y+box.height);
54205             this.split.el.setWidth(box.width);
54206         }
54207         if(this.collapsed){
54208             this.updateBody(box.width, null);
54209         }
54210         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54211     }
54212 });
54213
54214 Roo.SouthLayoutRegion = function(mgr, config){
54215     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54216     if(this.split){
54217         this.split.placement = Roo.SplitBar.BOTTOM;
54218         this.split.orientation = Roo.SplitBar.VERTICAL;
54219         this.split.el.addClass("x-layout-split-v");
54220     }
54221     var size = config.initialSize || config.height;
54222     if(typeof size != "undefined"){
54223         this.el.setHeight(size);
54224     }
54225 };
54226 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54227     orientation: Roo.SplitBar.VERTICAL,
54228     getBox : function(){
54229         if(this.collapsed){
54230             return this.collapsedEl.getBox();
54231         }
54232         var box = this.el.getBox();
54233         if(this.split){
54234             var sh = this.split.el.getHeight();
54235             box.height += sh;
54236             box.y -= sh;
54237         }
54238         return box;
54239     },
54240     
54241     updateBox : function(box){
54242         if(this.split && !this.collapsed){
54243             var sh = this.split.el.getHeight();
54244             box.height -= sh;
54245             box.y += sh;
54246             this.split.el.setLeft(box.x);
54247             this.split.el.setTop(box.y-sh);
54248             this.split.el.setWidth(box.width);
54249         }
54250         if(this.collapsed){
54251             this.updateBody(box.width, null);
54252         }
54253         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54254     }
54255 });
54256
54257 Roo.EastLayoutRegion = function(mgr, config){
54258     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54259     if(this.split){
54260         this.split.placement = Roo.SplitBar.RIGHT;
54261         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54262         this.split.el.addClass("x-layout-split-h");
54263     }
54264     var size = config.initialSize || config.width;
54265     if(typeof size != "undefined"){
54266         this.el.setWidth(size);
54267     }
54268 };
54269 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54270     orientation: Roo.SplitBar.HORIZONTAL,
54271     getBox : function(){
54272         if(this.collapsed){
54273             return this.collapsedEl.getBox();
54274         }
54275         var box = this.el.getBox();
54276         if(this.split){
54277             var sw = this.split.el.getWidth();
54278             box.width += sw;
54279             box.x -= sw;
54280         }
54281         return box;
54282     },
54283
54284     updateBox : function(box){
54285         if(this.split && !this.collapsed){
54286             var sw = this.split.el.getWidth();
54287             box.width -= sw;
54288             this.split.el.setLeft(box.x);
54289             this.split.el.setTop(box.y);
54290             this.split.el.setHeight(box.height);
54291             box.x += sw;
54292         }
54293         if(this.collapsed){
54294             this.updateBody(null, box.height);
54295         }
54296         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54297     }
54298 });
54299
54300 Roo.WestLayoutRegion = function(mgr, config){
54301     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54302     if(this.split){
54303         this.split.placement = Roo.SplitBar.LEFT;
54304         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54305         this.split.el.addClass("x-layout-split-h");
54306     }
54307     var size = config.initialSize || config.width;
54308     if(typeof size != "undefined"){
54309         this.el.setWidth(size);
54310     }
54311 };
54312 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54313     orientation: Roo.SplitBar.HORIZONTAL,
54314     getBox : function(){
54315         if(this.collapsed){
54316             return this.collapsedEl.getBox();
54317         }
54318         var box = this.el.getBox();
54319         if(this.split){
54320             box.width += this.split.el.getWidth();
54321         }
54322         return box;
54323     },
54324     
54325     updateBox : function(box){
54326         if(this.split && !this.collapsed){
54327             var sw = this.split.el.getWidth();
54328             box.width -= sw;
54329             this.split.el.setLeft(box.x+box.width);
54330             this.split.el.setTop(box.y);
54331             this.split.el.setHeight(box.height);
54332         }
54333         if(this.collapsed){
54334             this.updateBody(null, box.height);
54335         }
54336         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54337     }
54338 });
54339 /*
54340  * Based on:
54341  * Ext JS Library 1.1.1
54342  * Copyright(c) 2006-2007, Ext JS, LLC.
54343  *
54344  * Originally Released Under LGPL - original licence link has changed is not relivant.
54345  *
54346  * Fork - LGPL
54347  * <script type="text/javascript">
54348  */
54349  
54350  
54351 /*
54352  * Private internal class for reading and applying state
54353  */
54354 Roo.LayoutStateManager = function(layout){
54355      // default empty state
54356      this.state = {
54357         north: {},
54358         south: {},
54359         east: {},
54360         west: {}       
54361     };
54362 };
54363
54364 Roo.LayoutStateManager.prototype = {
54365     init : function(layout, provider){
54366         this.provider = provider;
54367         var state = provider.get(layout.id+"-layout-state");
54368         if(state){
54369             var wasUpdating = layout.isUpdating();
54370             if(!wasUpdating){
54371                 layout.beginUpdate();
54372             }
54373             for(var key in state){
54374                 if(typeof state[key] != "function"){
54375                     var rstate = state[key];
54376                     var r = layout.getRegion(key);
54377                     if(r && rstate){
54378                         if(rstate.size){
54379                             r.resizeTo(rstate.size);
54380                         }
54381                         if(rstate.collapsed == true){
54382                             r.collapse(true);
54383                         }else{
54384                             r.expand(null, true);
54385                         }
54386                     }
54387                 }
54388             }
54389             if(!wasUpdating){
54390                 layout.endUpdate();
54391             }
54392             this.state = state; 
54393         }
54394         this.layout = layout;
54395         layout.on("regionresized", this.onRegionResized, this);
54396         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54397         layout.on("regionexpanded", this.onRegionExpanded, this);
54398     },
54399     
54400     storeState : function(){
54401         this.provider.set(this.layout.id+"-layout-state", this.state);
54402     },
54403     
54404     onRegionResized : function(region, newSize){
54405         this.state[region.getPosition()].size = newSize;
54406         this.storeState();
54407     },
54408     
54409     onRegionCollapsed : function(region){
54410         this.state[region.getPosition()].collapsed = true;
54411         this.storeState();
54412     },
54413     
54414     onRegionExpanded : function(region){
54415         this.state[region.getPosition()].collapsed = false;
54416         this.storeState();
54417     }
54418 };/*
54419  * Based on:
54420  * Ext JS Library 1.1.1
54421  * Copyright(c) 2006-2007, Ext JS, LLC.
54422  *
54423  * Originally Released Under LGPL - original licence link has changed is not relivant.
54424  *
54425  * Fork - LGPL
54426  * <script type="text/javascript">
54427  */
54428 /**
54429  * @class Roo.ContentPanel
54430  * @extends Roo.util.Observable
54431  * @children Roo.form.Form Roo.JsonView Roo.View
54432  * A basic ContentPanel element.
54433  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54434  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54435  * @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
54436  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54437  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54438  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54439  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
54440  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54441  * @cfg {String} title          The title for this panel
54442  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54443  * @cfg {String} url            Calls {@link #setUrl} with this value
54444  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54445  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54446  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54447  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54448  * @cfg {String}    style  Extra style to add to the content panel
54449  * @cfg {Roo.menu.Menu} menu  popup menu
54450
54451  * @constructor
54452  * Create a new ContentPanel.
54453  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54454  * @param {String/Object} config A string to set only the title or a config object
54455  * @param {String} content (optional) Set the HTML content for this panel
54456  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54457  */
54458 Roo.ContentPanel = function(el, config, content){
54459     
54460      
54461     /*
54462     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54463         config = el;
54464         el = Roo.id();
54465     }
54466     if (config && config.parentLayout) { 
54467         el = config.parentLayout.el.createChild(); 
54468     }
54469     */
54470     if(el.autoCreate){ // xtype is available if this is called from factory
54471         config = el;
54472         el = Roo.id();
54473     }
54474     this.el = Roo.get(el);
54475     if(!this.el && config && config.autoCreate){
54476         if(typeof config.autoCreate == "object"){
54477             if(!config.autoCreate.id){
54478                 config.autoCreate.id = config.id||el;
54479             }
54480             this.el = Roo.DomHelper.append(document.body,
54481                         config.autoCreate, true);
54482         }else{
54483             this.el = Roo.DomHelper.append(document.body,
54484                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54485         }
54486     }
54487     
54488     
54489     this.closable = false;
54490     this.loaded = false;
54491     this.active = false;
54492     if(typeof config == "string"){
54493         this.title = config;
54494     }else{
54495         Roo.apply(this, config);
54496     }
54497     
54498     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54499         this.wrapEl = this.el.wrap();
54500         this.toolbar.container = this.el.insertSibling(false, 'before');
54501         this.toolbar = new Roo.Toolbar(this.toolbar);
54502     }
54503     
54504     // xtype created footer. - not sure if will work as we normally have to render first..
54505     if (this.footer && !this.footer.el && this.footer.xtype) {
54506         if (!this.wrapEl) {
54507             this.wrapEl = this.el.wrap();
54508         }
54509     
54510         this.footer.container = this.wrapEl.createChild();
54511          
54512         this.footer = Roo.factory(this.footer, Roo);
54513         
54514     }
54515     
54516     if(this.resizeEl){
54517         this.resizeEl = Roo.get(this.resizeEl, true);
54518     }else{
54519         this.resizeEl = this.el;
54520     }
54521     // handle view.xtype
54522     
54523  
54524     
54525     
54526     this.addEvents({
54527         /**
54528          * @event activate
54529          * Fires when this panel is activated. 
54530          * @param {Roo.ContentPanel} this
54531          */
54532         "activate" : true,
54533         /**
54534          * @event deactivate
54535          * Fires when this panel is activated. 
54536          * @param {Roo.ContentPanel} this
54537          */
54538         "deactivate" : true,
54539
54540         /**
54541          * @event resize
54542          * Fires when this panel is resized if fitToFrame is true.
54543          * @param {Roo.ContentPanel} this
54544          * @param {Number} width The width after any component adjustments
54545          * @param {Number} height The height after any component adjustments
54546          */
54547         "resize" : true,
54548         
54549          /**
54550          * @event render
54551          * Fires when this tab is created
54552          * @param {Roo.ContentPanel} this
54553          */
54554         "render" : true
54555          
54556         
54557     });
54558     
54559
54560     
54561     
54562     if(this.autoScroll){
54563         this.resizeEl.setStyle("overflow", "auto");
54564     } else {
54565         // fix randome scrolling
54566         this.el.on('scroll', function() {
54567             Roo.log('fix random scolling');
54568             this.scrollTo('top',0); 
54569         });
54570     }
54571     content = content || this.content;
54572     if(content){
54573         this.setContent(content);
54574     }
54575     if(config && config.url){
54576         this.setUrl(this.url, this.params, this.loadOnce);
54577     }
54578     
54579     
54580     
54581     Roo.ContentPanel.superclass.constructor.call(this);
54582     
54583     if (this.view && typeof(this.view.xtype) != 'undefined') {
54584         this.view.el = this.el.appendChild(document.createElement("div"));
54585         this.view = Roo.factory(this.view); 
54586         this.view.render  &&  this.view.render(false, '');  
54587     }
54588     
54589     
54590     this.fireEvent('render', this);
54591 };
54592
54593 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54594     tabTip:'',
54595     setRegion : function(region){
54596         this.region = region;
54597         if(region){
54598            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54599         }else{
54600            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54601         } 
54602     },
54603     
54604     /**
54605      * Returns the toolbar for this Panel if one was configured. 
54606      * @return {Roo.Toolbar} 
54607      */
54608     getToolbar : function(){
54609         return this.toolbar;
54610     },
54611     
54612     setActiveState : function(active){
54613         this.active = active;
54614         if(!active){
54615             this.fireEvent("deactivate", this);
54616         }else{
54617             this.fireEvent("activate", this);
54618         }
54619     },
54620     /**
54621      * Updates this panel's element
54622      * @param {String} content The new content
54623      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54624     */
54625     setContent : function(content, loadScripts){
54626         this.el.update(content, loadScripts);
54627     },
54628
54629     ignoreResize : function(w, h){
54630         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54631             return true;
54632         }else{
54633             this.lastSize = {width: w, height: h};
54634             return false;
54635         }
54636     },
54637     /**
54638      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54639      * @return {Roo.UpdateManager} The UpdateManager
54640      */
54641     getUpdateManager : function(){
54642         return this.el.getUpdateManager();
54643     },
54644      /**
54645      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54646      * @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:
54647 <pre><code>
54648 panel.load({
54649     url: "your-url.php",
54650     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54651     callback: yourFunction,
54652     scope: yourObject, //(optional scope)
54653     discardUrl: false,
54654     nocache: false,
54655     text: "Loading...",
54656     timeout: 30,
54657     scripts: false
54658 });
54659 </code></pre>
54660      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54661      * 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.
54662      * @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}
54663      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54664      * @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.
54665      * @return {Roo.ContentPanel} this
54666      */
54667     load : function(){
54668         var um = this.el.getUpdateManager();
54669         um.update.apply(um, arguments);
54670         return this;
54671     },
54672
54673
54674     /**
54675      * 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.
54676      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54677      * @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)
54678      * @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)
54679      * @return {Roo.UpdateManager} The UpdateManager
54680      */
54681     setUrl : function(url, params, loadOnce){
54682         if(this.refreshDelegate){
54683             this.removeListener("activate", this.refreshDelegate);
54684         }
54685         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54686         this.on("activate", this.refreshDelegate);
54687         return this.el.getUpdateManager();
54688     },
54689     
54690     _handleRefresh : function(url, params, loadOnce){
54691         if(!loadOnce || !this.loaded){
54692             var updater = this.el.getUpdateManager();
54693             updater.update(url, params, this._setLoaded.createDelegate(this));
54694         }
54695     },
54696     
54697     _setLoaded : function(){
54698         this.loaded = true;
54699     }, 
54700     
54701     /**
54702      * Returns this panel's id
54703      * @return {String} 
54704      */
54705     getId : function(){
54706         return this.el.id;
54707     },
54708     
54709     /** 
54710      * Returns this panel's element - used by regiosn to add.
54711      * @return {Roo.Element} 
54712      */
54713     getEl : function(){
54714         return this.wrapEl || this.el;
54715     },
54716     
54717     adjustForComponents : function(width, height)
54718     {
54719         //Roo.log('adjustForComponents ');
54720         if(this.resizeEl != this.el){
54721             width -= this.el.getFrameWidth('lr');
54722             height -= this.el.getFrameWidth('tb');
54723         }
54724         if(this.toolbar){
54725             var te = this.toolbar.getEl();
54726             height -= te.getHeight();
54727             te.setWidth(width);
54728         }
54729         if(this.footer){
54730             var te = this.footer.getEl();
54731             //Roo.log("footer:" + te.getHeight());
54732             
54733             height -= te.getHeight();
54734             te.setWidth(width);
54735         }
54736         
54737         
54738         if(this.adjustments){
54739             width += this.adjustments[0];
54740             height += this.adjustments[1];
54741         }
54742         return {"width": width, "height": height};
54743     },
54744     
54745     setSize : function(width, height){
54746         if(this.fitToFrame && !this.ignoreResize(width, height)){
54747             if(this.fitContainer && this.resizeEl != this.el){
54748                 this.el.setSize(width, height);
54749             }
54750             var size = this.adjustForComponents(width, height);
54751             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54752             this.fireEvent('resize', this, size.width, size.height);
54753         }
54754     },
54755     
54756     /**
54757      * Returns this panel's title
54758      * @return {String} 
54759      */
54760     getTitle : function(){
54761         return this.title;
54762     },
54763     
54764     /**
54765      * Set this panel's title
54766      * @param {String} title
54767      */
54768     setTitle : function(title){
54769         this.title = title;
54770         if(this.region){
54771             this.region.updatePanelTitle(this, title);
54772         }
54773     },
54774     
54775     /**
54776      * Returns true is this panel was configured to be closable
54777      * @return {Boolean} 
54778      */
54779     isClosable : function(){
54780         return this.closable;
54781     },
54782     
54783     beforeSlide : function(){
54784         this.el.clip();
54785         this.resizeEl.clip();
54786     },
54787     
54788     afterSlide : function(){
54789         this.el.unclip();
54790         this.resizeEl.unclip();
54791     },
54792     
54793     /**
54794      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54795      *   Will fail silently if the {@link #setUrl} method has not been called.
54796      *   This does not activate the panel, just updates its content.
54797      */
54798     refresh : function(){
54799         if(this.refreshDelegate){
54800            this.loaded = false;
54801            this.refreshDelegate();
54802         }
54803     },
54804     
54805     /**
54806      * Destroys this panel
54807      */
54808     destroy : function(){
54809         this.el.removeAllListeners();
54810         var tempEl = document.createElement("span");
54811         tempEl.appendChild(this.el.dom);
54812         tempEl.innerHTML = "";
54813         this.el.remove();
54814         this.el = null;
54815     },
54816     
54817     /**
54818      * form - if the content panel contains a form - this is a reference to it.
54819      * @type {Roo.form.Form}
54820      */
54821     form : false,
54822     /**
54823      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54824      *    This contains a reference to it.
54825      * @type {Roo.View}
54826      */
54827     view : false,
54828     
54829       /**
54830      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54831      * <pre><code>
54832
54833 layout.addxtype({
54834        xtype : 'Form',
54835        items: [ .... ]
54836    }
54837 );
54838
54839 </code></pre>
54840      * @param {Object} cfg Xtype definition of item to add.
54841      */
54842     
54843     addxtype : function(cfg) {
54844         // add form..
54845         if (cfg.xtype.match(/^Form$/)) {
54846             
54847             var el;
54848             //if (this.footer) {
54849             //    el = this.footer.container.insertSibling(false, 'before');
54850             //} else {
54851                 el = this.el.createChild();
54852             //}
54853
54854             this.form = new  Roo.form.Form(cfg);
54855             
54856             
54857             if ( this.form.allItems.length) {
54858                 this.form.render(el.dom);
54859             }
54860             return this.form;
54861         }
54862         // should only have one of theses..
54863         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54864             // views.. should not be just added - used named prop 'view''
54865             
54866             cfg.el = this.el.appendChild(document.createElement("div"));
54867             // factory?
54868             
54869             var ret = new Roo.factory(cfg);
54870              
54871              ret.render && ret.render(false, ''); // render blank..
54872             this.view = ret;
54873             return ret;
54874         }
54875         return false;
54876     }
54877 });
54878
54879 /**
54880  * @class Roo.GridPanel
54881  * @extends Roo.ContentPanel
54882  * @constructor
54883  * Create a new GridPanel.
54884  * @param {Roo.grid.Grid} grid The grid for this panel
54885  * @param {String/Object} config A string to set only the panel's title, or a config object
54886  */
54887 Roo.GridPanel = function(grid, config){
54888     
54889   
54890     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54891         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54892         
54893     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54894     
54895     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54896     
54897     if(this.toolbar){
54898         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54899     }
54900     // xtype created footer. - not sure if will work as we normally have to render first..
54901     if (this.footer && !this.footer.el && this.footer.xtype) {
54902         
54903         this.footer.container = this.grid.getView().getFooterPanel(true);
54904         this.footer.dataSource = this.grid.dataSource;
54905         this.footer = Roo.factory(this.footer, Roo);
54906         
54907     }
54908     
54909     grid.monitorWindowResize = false; // turn off autosizing
54910     grid.autoHeight = false;
54911     grid.autoWidth = false;
54912     this.grid = grid;
54913     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54914 };
54915
54916 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54917     getId : function(){
54918         return this.grid.id;
54919     },
54920     
54921     /**
54922      * Returns the grid for this panel
54923      * @return {Roo.grid.Grid} 
54924      */
54925     getGrid : function(){
54926         return this.grid;    
54927     },
54928     
54929     setSize : function(width, height){
54930         if(!this.ignoreResize(width, height)){
54931             var grid = this.grid;
54932             var size = this.adjustForComponents(width, height);
54933             grid.getGridEl().setSize(size.width, size.height);
54934             grid.autoSize();
54935         }
54936     },
54937     
54938     beforeSlide : function(){
54939         this.grid.getView().scroller.clip();
54940     },
54941     
54942     afterSlide : function(){
54943         this.grid.getView().scroller.unclip();
54944     },
54945     
54946     destroy : function(){
54947         this.grid.destroy();
54948         delete this.grid;
54949         Roo.GridPanel.superclass.destroy.call(this); 
54950     }
54951 });
54952
54953
54954 /**
54955  * @class Roo.NestedLayoutPanel
54956  * @extends Roo.ContentPanel
54957  * @constructor
54958  * Create a new NestedLayoutPanel.
54959  * 
54960  * 
54961  * @param {Roo.BorderLayout} layout [required] The layout for this panel
54962  * @param {String/Object} config A string to set only the title or a config object
54963  */
54964 Roo.NestedLayoutPanel = function(layout, config)
54965 {
54966     // construct with only one argument..
54967     /* FIXME - implement nicer consturctors
54968     if (layout.layout) {
54969         config = layout;
54970         layout = config.layout;
54971         delete config.layout;
54972     }
54973     if (layout.xtype && !layout.getEl) {
54974         // then layout needs constructing..
54975         layout = Roo.factory(layout, Roo);
54976     }
54977     */
54978     
54979     
54980     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54981     
54982     layout.monitorWindowResize = false; // turn off autosizing
54983     this.layout = layout;
54984     this.layout.getEl().addClass("x-layout-nested-layout");
54985     
54986     
54987     
54988     
54989 };
54990
54991 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54992
54993     setSize : function(width, height){
54994         if(!this.ignoreResize(width, height)){
54995             var size = this.adjustForComponents(width, height);
54996             var el = this.layout.getEl();
54997             el.setSize(size.width, size.height);
54998             var touch = el.dom.offsetWidth;
54999             this.layout.layout();
55000             // ie requires a double layout on the first pass
55001             if(Roo.isIE && !this.initialized){
55002                 this.initialized = true;
55003                 this.layout.layout();
55004             }
55005         }
55006     },
55007     
55008     // activate all subpanels if not currently active..
55009     
55010     setActiveState : function(active){
55011         this.active = active;
55012         if(!active){
55013             this.fireEvent("deactivate", this);
55014             return;
55015         }
55016         
55017         this.fireEvent("activate", this);
55018         // not sure if this should happen before or after..
55019         if (!this.layout) {
55020             return; // should not happen..
55021         }
55022         var reg = false;
55023         for (var r in this.layout.regions) {
55024             reg = this.layout.getRegion(r);
55025             if (reg.getActivePanel()) {
55026                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55027                 reg.setActivePanel(reg.getActivePanel());
55028                 continue;
55029             }
55030             if (!reg.panels.length) {
55031                 continue;
55032             }
55033             reg.showPanel(reg.getPanel(0));
55034         }
55035         
55036         
55037         
55038         
55039     },
55040     
55041     /**
55042      * Returns the nested BorderLayout for this panel
55043      * @return {Roo.BorderLayout} 
55044      */
55045     getLayout : function(){
55046         return this.layout;
55047     },
55048     
55049      /**
55050      * Adds a xtype elements to the layout of the nested panel
55051      * <pre><code>
55052
55053 panel.addxtype({
55054        xtype : 'ContentPanel',
55055        region: 'west',
55056        items: [ .... ]
55057    }
55058 );
55059
55060 panel.addxtype({
55061         xtype : 'NestedLayoutPanel',
55062         region: 'west',
55063         layout: {
55064            center: { },
55065            west: { }   
55066         },
55067         items : [ ... list of content panels or nested layout panels.. ]
55068    }
55069 );
55070 </code></pre>
55071      * @param {Object} cfg Xtype definition of item to add.
55072      */
55073     addxtype : function(cfg) {
55074         return this.layout.addxtype(cfg);
55075     
55076     }
55077 });
55078
55079 Roo.ScrollPanel = function(el, config, content){
55080     config = config || {};
55081     config.fitToFrame = true;
55082     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55083     
55084     this.el.dom.style.overflow = "hidden";
55085     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55086     this.el.removeClass("x-layout-inactive-content");
55087     this.el.on("mousewheel", this.onWheel, this);
55088
55089     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55090     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55091     up.unselectable(); down.unselectable();
55092     up.on("click", this.scrollUp, this);
55093     down.on("click", this.scrollDown, this);
55094     up.addClassOnOver("x-scroller-btn-over");
55095     down.addClassOnOver("x-scroller-btn-over");
55096     up.addClassOnClick("x-scroller-btn-click");
55097     down.addClassOnClick("x-scroller-btn-click");
55098     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55099
55100     this.resizeEl = this.el;
55101     this.el = wrap; this.up = up; this.down = down;
55102 };
55103
55104 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55105     increment : 100,
55106     wheelIncrement : 5,
55107     scrollUp : function(){
55108         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55109     },
55110
55111     scrollDown : function(){
55112         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55113     },
55114
55115     afterScroll : function(){
55116         var el = this.resizeEl;
55117         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55118         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55119         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55120     },
55121
55122     setSize : function(){
55123         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55124         this.afterScroll();
55125     },
55126
55127     onWheel : function(e){
55128         var d = e.getWheelDelta();
55129         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55130         this.afterScroll();
55131         e.stopEvent();
55132     },
55133
55134     setContent : function(content, loadScripts){
55135         this.resizeEl.update(content, loadScripts);
55136     }
55137
55138 });
55139
55140
55141
55142 /**
55143  * @class Roo.TreePanel
55144  * @extends Roo.ContentPanel
55145  * Treepanel component
55146  * 
55147  * @constructor
55148  * Create a new TreePanel. - defaults to fit/scoll contents.
55149  * @param {String/Object} config A string to set only the panel's title, or a config object
55150  */
55151 Roo.TreePanel = function(config){
55152     var el = config.el;
55153     var tree = config.tree;
55154     delete config.tree; 
55155     delete config.el; // hopefull!
55156     
55157     // wrapper for IE7 strict & safari scroll issue
55158     
55159     var treeEl = el.createChild();
55160     config.resizeEl = treeEl;
55161     
55162     
55163     
55164     Roo.TreePanel.superclass.constructor.call(this, el, config);
55165  
55166  
55167     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55168     //console.log(tree);
55169     this.on('activate', function()
55170     {
55171         if (this.tree.rendered) {
55172             return;
55173         }
55174         //console.log('render tree');
55175         this.tree.render();
55176     });
55177     // this should not be needed.. - it's actually the 'el' that resizes?
55178     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55179     
55180     //this.on('resize',  function (cp, w, h) {
55181     //        this.tree.innerCt.setWidth(w);
55182     //        this.tree.innerCt.setHeight(h);
55183     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55184     //});
55185
55186         
55187     
55188 };
55189
55190 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55191     fitToFrame : true,
55192     autoScroll : true,
55193     /*
55194      * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55195      */
55196     tree : false
55197
55198 });
55199
55200
55201
55202
55203
55204
55205
55206
55207
55208
55209
55210 /*
55211  * Based on:
55212  * Ext JS Library 1.1.1
55213  * Copyright(c) 2006-2007, Ext JS, LLC.
55214  *
55215  * Originally Released Under LGPL - original licence link has changed is not relivant.
55216  *
55217  * Fork - LGPL
55218  * <script type="text/javascript">
55219  */
55220  
55221
55222 /**
55223  * @class Roo.ReaderLayout
55224  * @extends Roo.BorderLayout
55225  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55226  * center region containing two nested regions (a top one for a list view and one for item preview below),
55227  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55228  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55229  * expedites the setup of the overall layout and regions for this common application style.
55230  * Example:
55231  <pre><code>
55232 var reader = new Roo.ReaderLayout();
55233 var CP = Roo.ContentPanel;  // shortcut for adding
55234
55235 reader.beginUpdate();
55236 reader.add("north", new CP("north", "North"));
55237 reader.add("west", new CP("west", {title: "West"}));
55238 reader.add("east", new CP("east", {title: "East"}));
55239
55240 reader.regions.listView.add(new CP("listView", "List"));
55241 reader.regions.preview.add(new CP("preview", "Preview"));
55242 reader.endUpdate();
55243 </code></pre>
55244 * @constructor
55245 * Create a new ReaderLayout
55246 * @param {Object} config Configuration options
55247 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55248 * document.body if omitted)
55249 */
55250 Roo.ReaderLayout = function(config, renderTo){
55251     var c = config || {size:{}};
55252     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55253         north: c.north !== false ? Roo.apply({
55254             split:false,
55255             initialSize: 32,
55256             titlebar: false
55257         }, c.north) : false,
55258         west: c.west !== false ? Roo.apply({
55259             split:true,
55260             initialSize: 200,
55261             minSize: 175,
55262             maxSize: 400,
55263             titlebar: true,
55264             collapsible: true,
55265             animate: true,
55266             margins:{left:5,right:0,bottom:5,top:5},
55267             cmargins:{left:5,right:5,bottom:5,top:5}
55268         }, c.west) : false,
55269         east: c.east !== false ? Roo.apply({
55270             split:true,
55271             initialSize: 200,
55272             minSize: 175,
55273             maxSize: 400,
55274             titlebar: true,
55275             collapsible: true,
55276             animate: true,
55277             margins:{left:0,right:5,bottom:5,top:5},
55278             cmargins:{left:5,right:5,bottom:5,top:5}
55279         }, c.east) : false,
55280         center: Roo.apply({
55281             tabPosition: 'top',
55282             autoScroll:false,
55283             closeOnTab: true,
55284             titlebar:false,
55285             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55286         }, c.center)
55287     });
55288
55289     this.el.addClass('x-reader');
55290
55291     this.beginUpdate();
55292
55293     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55294         south: c.preview !== false ? Roo.apply({
55295             split:true,
55296             initialSize: 200,
55297             minSize: 100,
55298             autoScroll:true,
55299             collapsible:true,
55300             titlebar: true,
55301             cmargins:{top:5,left:0, right:0, bottom:0}
55302         }, c.preview) : false,
55303         center: Roo.apply({
55304             autoScroll:false,
55305             titlebar:false,
55306             minHeight:200
55307         }, c.listView)
55308     });
55309     this.add('center', new Roo.NestedLayoutPanel(inner,
55310             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55311
55312     this.endUpdate();
55313
55314     this.regions.preview = inner.getRegion('south');
55315     this.regions.listView = inner.getRegion('center');
55316 };
55317
55318 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55319  * Based on:
55320  * Ext JS Library 1.1.1
55321  * Copyright(c) 2006-2007, Ext JS, LLC.
55322  *
55323  * Originally Released Under LGPL - original licence link has changed is not relivant.
55324  *
55325  * Fork - LGPL
55326  * <script type="text/javascript">
55327  */
55328  
55329 /**
55330  * @class Roo.grid.Grid
55331  * @extends Roo.util.Observable
55332  * This class represents the primary interface of a component based grid control.
55333  * <br><br>Usage:<pre><code>
55334  var grid = new Roo.grid.Grid("my-container-id", {
55335      ds: myDataStore,
55336      cm: myColModel,
55337      selModel: mySelectionModel,
55338      autoSizeColumns: true,
55339      monitorWindowResize: false,
55340      trackMouseOver: true
55341  });
55342  // set any options
55343  grid.render();
55344  * </code></pre>
55345  * <b>Common Problems:</b><br/>
55346  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55347  * element will correct this<br/>
55348  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55349  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55350  * are unpredictable.<br/>
55351  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55352  * grid to calculate dimensions/offsets.<br/>
55353   * @constructor
55354  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55355  * The container MUST have some type of size defined for the grid to fill. The container will be
55356  * automatically set to position relative if it isn't already.
55357  * @param {Object} config A config object that sets properties on this grid.
55358  */
55359 Roo.grid.Grid = function(container, config){
55360         // initialize the container
55361         this.container = Roo.get(container);
55362         this.container.update("");
55363         this.container.setStyle("overflow", "hidden");
55364     this.container.addClass('x-grid-container');
55365
55366     this.id = this.container.id;
55367
55368     Roo.apply(this, config);
55369     // check and correct shorthanded configs
55370     if(this.ds){
55371         this.dataSource = this.ds;
55372         delete this.ds;
55373     }
55374     if(this.cm){
55375         this.colModel = this.cm;
55376         delete this.cm;
55377     }
55378     if(this.sm){
55379         this.selModel = this.sm;
55380         delete this.sm;
55381     }
55382
55383     if (this.selModel) {
55384         this.selModel = Roo.factory(this.selModel, Roo.grid);
55385         this.sm = this.selModel;
55386         this.sm.xmodule = this.xmodule || false;
55387     }
55388     if (typeof(this.colModel.config) == 'undefined') {
55389         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55390         this.cm = this.colModel;
55391         this.cm.xmodule = this.xmodule || false;
55392     }
55393     if (this.dataSource) {
55394         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55395         this.ds = this.dataSource;
55396         this.ds.xmodule = this.xmodule || false;
55397          
55398     }
55399     
55400     
55401     
55402     if(this.width){
55403         this.container.setWidth(this.width);
55404     }
55405
55406     if(this.height){
55407         this.container.setHeight(this.height);
55408     }
55409     /** @private */
55410         this.addEvents({
55411         // raw events
55412         /**
55413          * @event click
55414          * The raw click event for the entire grid.
55415          * @param {Roo.EventObject} e
55416          */
55417         "click" : true,
55418         /**
55419          * @event dblclick
55420          * The raw dblclick event for the entire grid.
55421          * @param {Roo.EventObject} e
55422          */
55423         "dblclick" : true,
55424         /**
55425          * @event contextmenu
55426          * The raw contextmenu event for the entire grid.
55427          * @param {Roo.EventObject} e
55428          */
55429         "contextmenu" : true,
55430         /**
55431          * @event mousedown
55432          * The raw mousedown event for the entire grid.
55433          * @param {Roo.EventObject} e
55434          */
55435         "mousedown" : true,
55436         /**
55437          * @event mouseup
55438          * The raw mouseup event for the entire grid.
55439          * @param {Roo.EventObject} e
55440          */
55441         "mouseup" : true,
55442         /**
55443          * @event mouseover
55444          * The raw mouseover event for the entire grid.
55445          * @param {Roo.EventObject} e
55446          */
55447         "mouseover" : true,
55448         /**
55449          * @event mouseout
55450          * The raw mouseout event for the entire grid.
55451          * @param {Roo.EventObject} e
55452          */
55453         "mouseout" : true,
55454         /**
55455          * @event keypress
55456          * The raw keypress event for the entire grid.
55457          * @param {Roo.EventObject} e
55458          */
55459         "keypress" : true,
55460         /**
55461          * @event keydown
55462          * The raw keydown event for the entire grid.
55463          * @param {Roo.EventObject} e
55464          */
55465         "keydown" : true,
55466
55467         // custom events
55468
55469         /**
55470          * @event cellclick
55471          * Fires when a cell is clicked
55472          * @param {Grid} this
55473          * @param {Number} rowIndex
55474          * @param {Number} columnIndex
55475          * @param {Roo.EventObject} e
55476          */
55477         "cellclick" : true,
55478         /**
55479          * @event celldblclick
55480          * Fires when a cell is double clicked
55481          * @param {Grid} this
55482          * @param {Number} rowIndex
55483          * @param {Number} columnIndex
55484          * @param {Roo.EventObject} e
55485          */
55486         "celldblclick" : true,
55487         /**
55488          * @event rowclick
55489          * Fires when a row is clicked
55490          * @param {Grid} this
55491          * @param {Number} rowIndex
55492          * @param {Roo.EventObject} e
55493          */
55494         "rowclick" : true,
55495         /**
55496          * @event rowdblclick
55497          * Fires when a row is double clicked
55498          * @param {Grid} this
55499          * @param {Number} rowIndex
55500          * @param {Roo.EventObject} e
55501          */
55502         "rowdblclick" : true,
55503         /**
55504          * @event headerclick
55505          * Fires when a header is clicked
55506          * @param {Grid} this
55507          * @param {Number} columnIndex
55508          * @param {Roo.EventObject} e
55509          */
55510         "headerclick" : true,
55511         /**
55512          * @event headerdblclick
55513          * Fires when a header cell is double clicked
55514          * @param {Grid} this
55515          * @param {Number} columnIndex
55516          * @param {Roo.EventObject} e
55517          */
55518         "headerdblclick" : true,
55519         /**
55520          * @event rowcontextmenu
55521          * Fires when a row is right clicked
55522          * @param {Grid} this
55523          * @param {Number} rowIndex
55524          * @param {Roo.EventObject} e
55525          */
55526         "rowcontextmenu" : true,
55527         /**
55528          * @event cellcontextmenu
55529          * Fires when a cell is right clicked
55530          * @param {Grid} this
55531          * @param {Number} rowIndex
55532          * @param {Number} cellIndex
55533          * @param {Roo.EventObject} e
55534          */
55535          "cellcontextmenu" : true,
55536         /**
55537          * @event headercontextmenu
55538          * Fires when a header is right clicked
55539          * @param {Grid} this
55540          * @param {Number} columnIndex
55541          * @param {Roo.EventObject} e
55542          */
55543         "headercontextmenu" : true,
55544         /**
55545          * @event bodyscroll
55546          * Fires when the body element is scrolled
55547          * @param {Number} scrollLeft
55548          * @param {Number} scrollTop
55549          */
55550         "bodyscroll" : true,
55551         /**
55552          * @event columnresize
55553          * Fires when the user resizes a column
55554          * @param {Number} columnIndex
55555          * @param {Number} newSize
55556          */
55557         "columnresize" : true,
55558         /**
55559          * @event columnmove
55560          * Fires when the user moves a column
55561          * @param {Number} oldIndex
55562          * @param {Number} newIndex
55563          */
55564         "columnmove" : true,
55565         /**
55566          * @event startdrag
55567          * Fires when row(s) start being dragged
55568          * @param {Grid} this
55569          * @param {Roo.GridDD} dd The drag drop object
55570          * @param {event} e The raw browser event
55571          */
55572         "startdrag" : true,
55573         /**
55574          * @event enddrag
55575          * Fires when a drag operation is complete
55576          * @param {Grid} this
55577          * @param {Roo.GridDD} dd The drag drop object
55578          * @param {event} e The raw browser event
55579          */
55580         "enddrag" : true,
55581         /**
55582          * @event dragdrop
55583          * Fires when dragged row(s) are dropped on a valid DD target
55584          * @param {Grid} this
55585          * @param {Roo.GridDD} dd The drag drop object
55586          * @param {String} targetId The target drag drop object
55587          * @param {event} e The raw browser event
55588          */
55589         "dragdrop" : true,
55590         /**
55591          * @event dragover
55592          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55593          * @param {Grid} this
55594          * @param {Roo.GridDD} dd The drag drop object
55595          * @param {String} targetId The target drag drop object
55596          * @param {event} e The raw browser event
55597          */
55598         "dragover" : true,
55599         /**
55600          * @event dragenter
55601          *  Fires when the dragged row(s) first cross another DD target while being dragged
55602          * @param {Grid} this
55603          * @param {Roo.GridDD} dd The drag drop object
55604          * @param {String} targetId The target drag drop object
55605          * @param {event} e The raw browser event
55606          */
55607         "dragenter" : true,
55608         /**
55609          * @event dragout
55610          * Fires when the dragged row(s) leave another DD target while being dragged
55611          * @param {Grid} this
55612          * @param {Roo.GridDD} dd The drag drop object
55613          * @param {String} targetId The target drag drop object
55614          * @param {event} e The raw browser event
55615          */
55616         "dragout" : true,
55617         /**
55618          * @event rowclass
55619          * Fires when a row is rendered, so you can change add a style to it.
55620          * @param {GridView} gridview   The grid view
55621          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55622          */
55623         'rowclass' : true,
55624
55625         /**
55626          * @event render
55627          * Fires when the grid is rendered
55628          * @param {Grid} grid
55629          */
55630         'render' : true
55631     });
55632
55633     Roo.grid.Grid.superclass.constructor.call(this);
55634 };
55635 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55636     
55637     /**
55638          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
55639          */
55640         /**
55641          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
55642          */
55643         /**
55644          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
55645          */
55646         /**
55647          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
55648          */
55649         /**
55650      * @cfg {String} ddGroup - drag drop group.
55651      */
55652       /**
55653      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55654      */
55655
55656     /**
55657      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55658      */
55659     minColumnWidth : 25,
55660
55661     /**
55662      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55663      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55664      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55665      */
55666     autoSizeColumns : false,
55667
55668     /**
55669      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55670      */
55671     autoSizeHeaders : true,
55672
55673     /**
55674      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55675      */
55676     monitorWindowResize : true,
55677
55678     /**
55679      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55680      * rows measured to get a columns size. Default is 0 (all rows).
55681      */
55682     maxRowsToMeasure : 0,
55683
55684     /**
55685      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55686      */
55687     trackMouseOver : true,
55688
55689     /**
55690     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55691     */
55692       /**
55693     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55694     */
55695     
55696     /**
55697     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55698     */
55699     enableDragDrop : false,
55700     
55701     /**
55702     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55703     */
55704     enableColumnMove : true,
55705     
55706     /**
55707     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55708     */
55709     enableColumnHide : true,
55710     
55711     /**
55712     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55713     */
55714     enableRowHeightSync : false,
55715     
55716     /**
55717     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55718     */
55719     stripeRows : true,
55720     
55721     /**
55722     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55723     */
55724     autoHeight : false,
55725
55726     /**
55727      * @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.
55728      */
55729     autoExpandColumn : false,
55730
55731     /**
55732     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55733     * Default is 50.
55734     */
55735     autoExpandMin : 50,
55736
55737     /**
55738     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55739     */
55740     autoExpandMax : 1000,
55741
55742     /**
55743     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55744     */
55745     view : null,
55746
55747     /**
55748     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55749     */
55750     loadMask : false,
55751     /**
55752     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55753     */
55754     dropTarget: false,
55755     
55756    
55757     
55758     // private
55759     rendered : false,
55760
55761     /**
55762     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55763     * of a fixed width. Default is false.
55764     */
55765     /**
55766     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55767     */
55768     
55769     
55770     /**
55771     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55772     * %0 is replaced with the number of selected rows.
55773     */
55774     ddText : "{0} selected row{1}",
55775     
55776     
55777     /**
55778      * Called once after all setup has been completed and the grid is ready to be rendered.
55779      * @return {Roo.grid.Grid} this
55780      */
55781     render : function()
55782     {
55783         var c = this.container;
55784         // try to detect autoHeight/width mode
55785         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55786             this.autoHeight = true;
55787         }
55788         var view = this.getView();
55789         view.init(this);
55790
55791         c.on("click", this.onClick, this);
55792         c.on("dblclick", this.onDblClick, this);
55793         c.on("contextmenu", this.onContextMenu, this);
55794         c.on("keydown", this.onKeyDown, this);
55795         if (Roo.isTouch) {
55796             c.on("touchstart", this.onTouchStart, this);
55797         }
55798
55799         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55800
55801         this.getSelectionModel().init(this);
55802
55803         view.render();
55804
55805         if(this.loadMask){
55806             this.loadMask = new Roo.LoadMask(this.container,
55807                     Roo.apply({store:this.dataSource}, this.loadMask));
55808         }
55809         
55810         
55811         if (this.toolbar && this.toolbar.xtype) {
55812             this.toolbar.container = this.getView().getHeaderPanel(true);
55813             this.toolbar = new Roo.Toolbar(this.toolbar);
55814         }
55815         if (this.footer && this.footer.xtype) {
55816             this.footer.dataSource = this.getDataSource();
55817             this.footer.container = this.getView().getFooterPanel(true);
55818             this.footer = Roo.factory(this.footer, Roo);
55819         }
55820         if (this.dropTarget && this.dropTarget.xtype) {
55821             delete this.dropTarget.xtype;
55822             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55823         }
55824         
55825         
55826         this.rendered = true;
55827         this.fireEvent('render', this);
55828         return this;
55829     },
55830
55831     /**
55832      * Reconfigures the grid to use a different Store and Column Model.
55833      * The View will be bound to the new objects and refreshed.
55834      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55835      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55836      */
55837     reconfigure : function(dataSource, colModel){
55838         if(this.loadMask){
55839             this.loadMask.destroy();
55840             this.loadMask = new Roo.LoadMask(this.container,
55841                     Roo.apply({store:dataSource}, this.loadMask));
55842         }
55843         this.view.bind(dataSource, colModel);
55844         this.dataSource = dataSource;
55845         this.colModel = colModel;
55846         this.view.refresh(true);
55847     },
55848     /**
55849      * addColumns
55850      * Add's a column, default at the end..
55851      
55852      * @param {int} position to add (default end)
55853      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55854      */
55855     addColumns : function(pos, ar)
55856     {
55857         
55858         for (var i =0;i< ar.length;i++) {
55859             var cfg = ar[i];
55860             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55861             this.cm.lookup[cfg.id] = cfg;
55862         }
55863         
55864         
55865         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55866             pos = this.cm.config.length; //this.cm.config.push(cfg);
55867         } 
55868         pos = Math.max(0,pos);
55869         ar.unshift(0);
55870         ar.unshift(pos);
55871         this.cm.config.splice.apply(this.cm.config, ar);
55872         
55873         
55874         
55875         this.view.generateRules(this.cm);
55876         this.view.refresh(true);
55877         
55878     },
55879     
55880     
55881     
55882     
55883     // private
55884     onKeyDown : function(e){
55885         this.fireEvent("keydown", e);
55886     },
55887
55888     /**
55889      * Destroy this grid.
55890      * @param {Boolean} removeEl True to remove the element
55891      */
55892     destroy : function(removeEl, keepListeners){
55893         if(this.loadMask){
55894             this.loadMask.destroy();
55895         }
55896         var c = this.container;
55897         c.removeAllListeners();
55898         this.view.destroy();
55899         this.colModel.purgeListeners();
55900         if(!keepListeners){
55901             this.purgeListeners();
55902         }
55903         c.update("");
55904         if(removeEl === true){
55905             c.remove();
55906         }
55907     },
55908
55909     // private
55910     processEvent : function(name, e){
55911         // does this fire select???
55912         //Roo.log('grid:processEvent '  + name);
55913         
55914         if (name != 'touchstart' ) {
55915             this.fireEvent(name, e);    
55916         }
55917         
55918         var t = e.getTarget();
55919         var v = this.view;
55920         var header = v.findHeaderIndex(t);
55921         if(header !== false){
55922             var ename = name == 'touchstart' ? 'click' : name;
55923              
55924             this.fireEvent("header" + ename, this, header, e);
55925         }else{
55926             var row = v.findRowIndex(t);
55927             var cell = v.findCellIndex(t);
55928             if (name == 'touchstart') {
55929                 // first touch is always a click.
55930                 // hopefull this happens after selection is updated.?
55931                 name = false;
55932                 
55933                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55934                     var cs = this.selModel.getSelectedCell();
55935                     if (row == cs[0] && cell == cs[1]){
55936                         name = 'dblclick';
55937                     }
55938                 }
55939                 if (typeof(this.selModel.getSelections) != 'undefined') {
55940                     var cs = this.selModel.getSelections();
55941                     var ds = this.dataSource;
55942                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55943                         name = 'dblclick';
55944                     }
55945                 }
55946                 if (!name) {
55947                     return;
55948                 }
55949             }
55950             
55951             
55952             if(row !== false){
55953                 this.fireEvent("row" + name, this, row, e);
55954                 if(cell !== false){
55955                     this.fireEvent("cell" + name, this, row, cell, e);
55956                 }
55957             }
55958         }
55959     },
55960
55961     // private
55962     onClick : function(e){
55963         this.processEvent("click", e);
55964     },
55965    // private
55966     onTouchStart : function(e){
55967         this.processEvent("touchstart", e);
55968     },
55969
55970     // private
55971     onContextMenu : function(e, t){
55972         this.processEvent("contextmenu", e);
55973     },
55974
55975     // private
55976     onDblClick : function(e){
55977         this.processEvent("dblclick", e);
55978     },
55979
55980     // private
55981     walkCells : function(row, col, step, fn, scope){
55982         var cm = this.colModel, clen = cm.getColumnCount();
55983         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55984         if(step < 0){
55985             if(col < 0){
55986                 row--;
55987                 first = false;
55988             }
55989             while(row >= 0){
55990                 if(!first){
55991                     col = clen-1;
55992                 }
55993                 first = false;
55994                 while(col >= 0){
55995                     if(fn.call(scope || this, row, col, cm) === true){
55996                         return [row, col];
55997                     }
55998                     col--;
55999                 }
56000                 row--;
56001             }
56002         } else {
56003             if(col >= clen){
56004                 row++;
56005                 first = false;
56006             }
56007             while(row < rlen){
56008                 if(!first){
56009                     col = 0;
56010                 }
56011                 first = false;
56012                 while(col < clen){
56013                     if(fn.call(scope || this, row, col, cm) === true){
56014                         return [row, col];
56015                     }
56016                     col++;
56017                 }
56018                 row++;
56019             }
56020         }
56021         return null;
56022     },
56023
56024     // private
56025     getSelections : function(){
56026         return this.selModel.getSelections();
56027     },
56028
56029     /**
56030      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56031      * but if manual update is required this method will initiate it.
56032      */
56033     autoSize : function(){
56034         if(this.rendered){
56035             this.view.layout();
56036             if(this.view.adjustForScroll){
56037                 this.view.adjustForScroll();
56038             }
56039         }
56040     },
56041
56042     /**
56043      * Returns the grid's underlying element.
56044      * @return {Element} The element
56045      */
56046     getGridEl : function(){
56047         return this.container;
56048     },
56049
56050     // private for compatibility, overridden by editor grid
56051     stopEditing : function(){},
56052
56053     /**
56054      * Returns the grid's SelectionModel.
56055      * @return {SelectionModel}
56056      */
56057     getSelectionModel : function(){
56058         if(!this.selModel){
56059             this.selModel = new Roo.grid.RowSelectionModel();
56060         }
56061         return this.selModel;
56062     },
56063
56064     /**
56065      * Returns the grid's DataSource.
56066      * @return {DataSource}
56067      */
56068     getDataSource : function(){
56069         return this.dataSource;
56070     },
56071
56072     /**
56073      * Returns the grid's ColumnModel.
56074      * @return {ColumnModel}
56075      */
56076     getColumnModel : function(){
56077         return this.colModel;
56078     },
56079
56080     /**
56081      * Returns the grid's GridView object.
56082      * @return {GridView}
56083      */
56084     getView : function(){
56085         if(!this.view){
56086             this.view = new Roo.grid.GridView(this.viewConfig);
56087             this.relayEvents(this.view, [
56088                 "beforerowremoved", "beforerowsinserted",
56089                 "beforerefresh", "rowremoved",
56090                 "rowsinserted", "rowupdated" ,"refresh"
56091             ]);
56092         }
56093         return this.view;
56094     },
56095     /**
56096      * Called to get grid's drag proxy text, by default returns this.ddText.
56097      * Override this to put something different in the dragged text.
56098      * @return {String}
56099      */
56100     getDragDropText : function(){
56101         var count = this.selModel.getCount();
56102         return String.format(this.ddText, count, count == 1 ? '' : 's');
56103     }
56104 });
56105 /*
56106  * Based on:
56107  * Ext JS Library 1.1.1
56108  * Copyright(c) 2006-2007, Ext JS, LLC.
56109  *
56110  * Originally Released Under LGPL - original licence link has changed is not relivant.
56111  *
56112  * Fork - LGPL
56113  * <script type="text/javascript">
56114  */
56115  
56116 Roo.grid.AbstractGridView = function(){
56117         this.grid = null;
56118         
56119         this.events = {
56120             "beforerowremoved" : true,
56121             "beforerowsinserted" : true,
56122             "beforerefresh" : true,
56123             "rowremoved" : true,
56124             "rowsinserted" : true,
56125             "rowupdated" : true,
56126             "refresh" : true
56127         };
56128     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56129 };
56130
56131 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56132     rowClass : "x-grid-row",
56133     cellClass : "x-grid-cell",
56134     tdClass : "x-grid-td",
56135     hdClass : "x-grid-hd",
56136     splitClass : "x-grid-hd-split",
56137     
56138     init: function(grid){
56139         this.grid = grid;
56140                 var cid = this.grid.getGridEl().id;
56141         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56142         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56143         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56144         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56145         },
56146         
56147     getColumnRenderers : function(){
56148         var renderers = [];
56149         var cm = this.grid.colModel;
56150         var colCount = cm.getColumnCount();
56151         for(var i = 0; i < colCount; i++){
56152             renderers[i] = cm.getRenderer(i);
56153         }
56154         return renderers;
56155     },
56156     
56157     getColumnIds : function(){
56158         var ids = [];
56159         var cm = this.grid.colModel;
56160         var colCount = cm.getColumnCount();
56161         for(var i = 0; i < colCount; i++){
56162             ids[i] = cm.getColumnId(i);
56163         }
56164         return ids;
56165     },
56166     
56167     getDataIndexes : function(){
56168         if(!this.indexMap){
56169             this.indexMap = this.buildIndexMap();
56170         }
56171         return this.indexMap.colToData;
56172     },
56173     
56174     getColumnIndexByDataIndex : function(dataIndex){
56175         if(!this.indexMap){
56176             this.indexMap = this.buildIndexMap();
56177         }
56178         return this.indexMap.dataToCol[dataIndex];
56179     },
56180     
56181     /**
56182      * Set a css style for a column dynamically. 
56183      * @param {Number} colIndex The index of the column
56184      * @param {String} name The css property name
56185      * @param {String} value The css value
56186      */
56187     setCSSStyle : function(colIndex, name, value){
56188         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56189         Roo.util.CSS.updateRule(selector, name, value);
56190     },
56191     
56192     generateRules : function(cm){
56193         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56194         Roo.util.CSS.removeStyleSheet(rulesId);
56195         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56196             var cid = cm.getColumnId(i);
56197             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56198                          this.tdSelector, cid, " {\n}\n",
56199                          this.hdSelector, cid, " {\n}\n",
56200                          this.splitSelector, cid, " {\n}\n");
56201         }
56202         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56203     }
56204 });/*
56205  * Based on:
56206  * Ext JS Library 1.1.1
56207  * Copyright(c) 2006-2007, Ext JS, LLC.
56208  *
56209  * Originally Released Under LGPL - original licence link has changed is not relivant.
56210  *
56211  * Fork - LGPL
56212  * <script type="text/javascript">
56213  */
56214
56215 // private
56216 // This is a support class used internally by the Grid components
56217 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56218     this.grid = grid;
56219     this.view = grid.getView();
56220     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56221     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56222     if(hd2){
56223         this.setHandleElId(Roo.id(hd));
56224         this.setOuterHandleElId(Roo.id(hd2));
56225     }
56226     this.scroll = false;
56227 };
56228 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56229     maxDragWidth: 120,
56230     getDragData : function(e){
56231         var t = Roo.lib.Event.getTarget(e);
56232         var h = this.view.findHeaderCell(t);
56233         if(h){
56234             return {ddel: h.firstChild, header:h};
56235         }
56236         return false;
56237     },
56238
56239     onInitDrag : function(e){
56240         this.view.headersDisabled = true;
56241         var clone = this.dragData.ddel.cloneNode(true);
56242         clone.id = Roo.id();
56243         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56244         this.proxy.update(clone);
56245         return true;
56246     },
56247
56248     afterValidDrop : function(){
56249         var v = this.view;
56250         setTimeout(function(){
56251             v.headersDisabled = false;
56252         }, 50);
56253     },
56254
56255     afterInvalidDrop : function(){
56256         var v = this.view;
56257         setTimeout(function(){
56258             v.headersDisabled = false;
56259         }, 50);
56260     }
56261 });
56262 /*
56263  * Based on:
56264  * Ext JS Library 1.1.1
56265  * Copyright(c) 2006-2007, Ext JS, LLC.
56266  *
56267  * Originally Released Under LGPL - original licence link has changed is not relivant.
56268  *
56269  * Fork - LGPL
56270  * <script type="text/javascript">
56271  */
56272 // private
56273 // This is a support class used internally by the Grid components
56274 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56275     this.grid = grid;
56276     this.view = grid.getView();
56277     // split the proxies so they don't interfere with mouse events
56278     this.proxyTop = Roo.DomHelper.append(document.body, {
56279         cls:"col-move-top", html:"&#160;"
56280     }, true);
56281     this.proxyBottom = Roo.DomHelper.append(document.body, {
56282         cls:"col-move-bottom", html:"&#160;"
56283     }, true);
56284     this.proxyTop.hide = this.proxyBottom.hide = function(){
56285         this.setLeftTop(-100,-100);
56286         this.setStyle("visibility", "hidden");
56287     };
56288     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56289     // temporarily disabled
56290     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56291     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56292 };
56293 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56294     proxyOffsets : [-4, -9],
56295     fly: Roo.Element.fly,
56296
56297     getTargetFromEvent : function(e){
56298         var t = Roo.lib.Event.getTarget(e);
56299         var cindex = this.view.findCellIndex(t);
56300         if(cindex !== false){
56301             return this.view.getHeaderCell(cindex);
56302         }
56303         return null;
56304     },
56305
56306     nextVisible : function(h){
56307         var v = this.view, cm = this.grid.colModel;
56308         h = h.nextSibling;
56309         while(h){
56310             if(!cm.isHidden(v.getCellIndex(h))){
56311                 return h;
56312             }
56313             h = h.nextSibling;
56314         }
56315         return null;
56316     },
56317
56318     prevVisible : function(h){
56319         var v = this.view, cm = this.grid.colModel;
56320         h = h.prevSibling;
56321         while(h){
56322             if(!cm.isHidden(v.getCellIndex(h))){
56323                 return h;
56324             }
56325             h = h.prevSibling;
56326         }
56327         return null;
56328     },
56329
56330     positionIndicator : function(h, n, e){
56331         var x = Roo.lib.Event.getPageX(e);
56332         var r = Roo.lib.Dom.getRegion(n.firstChild);
56333         var px, pt, py = r.top + this.proxyOffsets[1];
56334         if((r.right - x) <= (r.right-r.left)/2){
56335             px = r.right+this.view.borderWidth;
56336             pt = "after";
56337         }else{
56338             px = r.left;
56339             pt = "before";
56340         }
56341         var oldIndex = this.view.getCellIndex(h);
56342         var newIndex = this.view.getCellIndex(n);
56343
56344         if(this.grid.colModel.isFixed(newIndex)){
56345             return false;
56346         }
56347
56348         var locked = this.grid.colModel.isLocked(newIndex);
56349
56350         if(pt == "after"){
56351             newIndex++;
56352         }
56353         if(oldIndex < newIndex){
56354             newIndex--;
56355         }
56356         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56357             return false;
56358         }
56359         px +=  this.proxyOffsets[0];
56360         this.proxyTop.setLeftTop(px, py);
56361         this.proxyTop.show();
56362         if(!this.bottomOffset){
56363             this.bottomOffset = this.view.mainHd.getHeight();
56364         }
56365         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56366         this.proxyBottom.show();
56367         return pt;
56368     },
56369
56370     onNodeEnter : function(n, dd, e, data){
56371         if(data.header != n){
56372             this.positionIndicator(data.header, n, e);
56373         }
56374     },
56375
56376     onNodeOver : function(n, dd, e, data){
56377         var result = false;
56378         if(data.header != n){
56379             result = this.positionIndicator(data.header, n, e);
56380         }
56381         if(!result){
56382             this.proxyTop.hide();
56383             this.proxyBottom.hide();
56384         }
56385         return result ? this.dropAllowed : this.dropNotAllowed;
56386     },
56387
56388     onNodeOut : function(n, dd, e, data){
56389         this.proxyTop.hide();
56390         this.proxyBottom.hide();
56391     },
56392
56393     onNodeDrop : function(n, dd, e, data){
56394         var h = data.header;
56395         if(h != n){
56396             var cm = this.grid.colModel;
56397             var x = Roo.lib.Event.getPageX(e);
56398             var r = Roo.lib.Dom.getRegion(n.firstChild);
56399             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56400             var oldIndex = this.view.getCellIndex(h);
56401             var newIndex = this.view.getCellIndex(n);
56402             var locked = cm.isLocked(newIndex);
56403             if(pt == "after"){
56404                 newIndex++;
56405             }
56406             if(oldIndex < newIndex){
56407                 newIndex--;
56408             }
56409             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56410                 return false;
56411             }
56412             cm.setLocked(oldIndex, locked, true);
56413             cm.moveColumn(oldIndex, newIndex);
56414             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56415             return true;
56416         }
56417         return false;
56418     }
56419 });
56420 /*
56421  * Based on:
56422  * Ext JS Library 1.1.1
56423  * Copyright(c) 2006-2007, Ext JS, LLC.
56424  *
56425  * Originally Released Under LGPL - original licence link has changed is not relivant.
56426  *
56427  * Fork - LGPL
56428  * <script type="text/javascript">
56429  */
56430   
56431 /**
56432  * @class Roo.grid.GridView
56433  * @extends Roo.util.Observable
56434  *
56435  * @constructor
56436  * @param {Object} config
56437  */
56438 Roo.grid.GridView = function(config){
56439     Roo.grid.GridView.superclass.constructor.call(this);
56440     this.el = null;
56441
56442     Roo.apply(this, config);
56443 };
56444
56445 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56446
56447     unselectable :  'unselectable="on"',
56448     unselectableCls :  'x-unselectable',
56449     
56450     
56451     rowClass : "x-grid-row",
56452
56453     cellClass : "x-grid-col",
56454
56455     tdClass : "x-grid-td",
56456
56457     hdClass : "x-grid-hd",
56458
56459     splitClass : "x-grid-split",
56460
56461     sortClasses : ["sort-asc", "sort-desc"],
56462
56463     enableMoveAnim : false,
56464
56465     hlColor: "C3DAF9",
56466
56467     dh : Roo.DomHelper,
56468
56469     fly : Roo.Element.fly,
56470
56471     css : Roo.util.CSS,
56472
56473     borderWidth: 1,
56474
56475     splitOffset: 3,
56476
56477     scrollIncrement : 22,
56478
56479     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56480
56481     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56482
56483     bind : function(ds, cm){
56484         if(this.ds){
56485             this.ds.un("load", this.onLoad, this);
56486             this.ds.un("datachanged", this.onDataChange, this);
56487             this.ds.un("add", this.onAdd, this);
56488             this.ds.un("remove", this.onRemove, this);
56489             this.ds.un("update", this.onUpdate, this);
56490             this.ds.un("clear", this.onClear, this);
56491         }
56492         if(ds){
56493             ds.on("load", this.onLoad, this);
56494             ds.on("datachanged", this.onDataChange, this);
56495             ds.on("add", this.onAdd, this);
56496             ds.on("remove", this.onRemove, this);
56497             ds.on("update", this.onUpdate, this);
56498             ds.on("clear", this.onClear, this);
56499         }
56500         this.ds = ds;
56501
56502         if(this.cm){
56503             this.cm.un("widthchange", this.onColWidthChange, this);
56504             this.cm.un("headerchange", this.onHeaderChange, this);
56505             this.cm.un("hiddenchange", this.onHiddenChange, this);
56506             this.cm.un("columnmoved", this.onColumnMove, this);
56507             this.cm.un("columnlockchange", this.onColumnLock, this);
56508         }
56509         if(cm){
56510             this.generateRules(cm);
56511             cm.on("widthchange", this.onColWidthChange, this);
56512             cm.on("headerchange", this.onHeaderChange, this);
56513             cm.on("hiddenchange", this.onHiddenChange, this);
56514             cm.on("columnmoved", this.onColumnMove, this);
56515             cm.on("columnlockchange", this.onColumnLock, this);
56516         }
56517         this.cm = cm;
56518     },
56519
56520     init: function(grid){
56521         Roo.grid.GridView.superclass.init.call(this, grid);
56522
56523         this.bind(grid.dataSource, grid.colModel);
56524
56525         grid.on("headerclick", this.handleHeaderClick, this);
56526
56527         if(grid.trackMouseOver){
56528             grid.on("mouseover", this.onRowOver, this);
56529             grid.on("mouseout", this.onRowOut, this);
56530         }
56531         grid.cancelTextSelection = function(){};
56532         this.gridId = grid.id;
56533
56534         var tpls = this.templates || {};
56535
56536         if(!tpls.master){
56537             tpls.master = new Roo.Template(
56538                '<div class="x-grid" hidefocus="true">',
56539                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56540                   '<div class="x-grid-topbar"></div>',
56541                   '<div class="x-grid-scroller"><div></div></div>',
56542                   '<div class="x-grid-locked">',
56543                       '<div class="x-grid-header">{lockedHeader}</div>',
56544                       '<div class="x-grid-body">{lockedBody}</div>',
56545                   "</div>",
56546                   '<div class="x-grid-viewport">',
56547                       '<div class="x-grid-header">{header}</div>',
56548                       '<div class="x-grid-body">{body}</div>',
56549                   "</div>",
56550                   '<div class="x-grid-bottombar"></div>',
56551                  
56552                   '<div class="x-grid-resize-proxy">&#160;</div>',
56553                "</div>"
56554             );
56555             tpls.master.disableformats = true;
56556         }
56557
56558         if(!tpls.header){
56559             tpls.header = new Roo.Template(
56560                '<table border="0" cellspacing="0" cellpadding="0">',
56561                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56562                "</table>{splits}"
56563             );
56564             tpls.header.disableformats = true;
56565         }
56566         tpls.header.compile();
56567
56568         if(!tpls.hcell){
56569             tpls.hcell = new Roo.Template(
56570                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56571                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56572                 "</div></td>"
56573              );
56574              tpls.hcell.disableFormats = true;
56575         }
56576         tpls.hcell.compile();
56577
56578         if(!tpls.hsplit){
56579             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56580                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56581             tpls.hsplit.disableFormats = true;
56582         }
56583         tpls.hsplit.compile();
56584
56585         if(!tpls.body){
56586             tpls.body = new Roo.Template(
56587                '<table border="0" cellspacing="0" cellpadding="0">',
56588                "<tbody>{rows}</tbody>",
56589                "</table>"
56590             );
56591             tpls.body.disableFormats = true;
56592         }
56593         tpls.body.compile();
56594
56595         if(!tpls.row){
56596             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56597             tpls.row.disableFormats = true;
56598         }
56599         tpls.row.compile();
56600
56601         if(!tpls.cell){
56602             tpls.cell = new Roo.Template(
56603                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56604                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56605                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56606                 "</td>"
56607             );
56608             tpls.cell.disableFormats = true;
56609         }
56610         tpls.cell.compile();
56611
56612         this.templates = tpls;
56613     },
56614
56615     // remap these for backwards compat
56616     onColWidthChange : function(){
56617         this.updateColumns.apply(this, arguments);
56618     },
56619     onHeaderChange : function(){
56620         this.updateHeaders.apply(this, arguments);
56621     }, 
56622     onHiddenChange : function(){
56623         this.handleHiddenChange.apply(this, arguments);
56624     },
56625     onColumnMove : function(){
56626         this.handleColumnMove.apply(this, arguments);
56627     },
56628     onColumnLock : function(){
56629         this.handleLockChange.apply(this, arguments);
56630     },
56631
56632     onDataChange : function(){
56633         this.refresh();
56634         this.updateHeaderSortState();
56635     },
56636
56637     onClear : function(){
56638         this.refresh();
56639     },
56640
56641     onUpdate : function(ds, record){
56642         this.refreshRow(record);
56643     },
56644
56645     refreshRow : function(record){
56646         var ds = this.ds, index;
56647         if(typeof record == 'number'){
56648             index = record;
56649             record = ds.getAt(index);
56650         }else{
56651             index = ds.indexOf(record);
56652         }
56653         this.insertRows(ds, index, index, true);
56654         this.onRemove(ds, record, index+1, true);
56655         this.syncRowHeights(index, index);
56656         this.layout();
56657         this.fireEvent("rowupdated", this, index, record);
56658     },
56659
56660     onAdd : function(ds, records, index){
56661         this.insertRows(ds, index, index + (records.length-1));
56662     },
56663
56664     onRemove : function(ds, record, index, isUpdate){
56665         if(isUpdate !== true){
56666             this.fireEvent("beforerowremoved", this, index, record);
56667         }
56668         var bt = this.getBodyTable(), lt = this.getLockedTable();
56669         if(bt.rows[index]){
56670             bt.firstChild.removeChild(bt.rows[index]);
56671         }
56672         if(lt.rows[index]){
56673             lt.firstChild.removeChild(lt.rows[index]);
56674         }
56675         if(isUpdate !== true){
56676             this.stripeRows(index);
56677             this.syncRowHeights(index, index);
56678             this.layout();
56679             this.fireEvent("rowremoved", this, index, record);
56680         }
56681     },
56682
56683     onLoad : function(){
56684         this.scrollToTop();
56685     },
56686
56687     /**
56688      * Scrolls the grid to the top
56689      */
56690     scrollToTop : function(){
56691         if(this.scroller){
56692             this.scroller.dom.scrollTop = 0;
56693             this.syncScroll();
56694         }
56695     },
56696
56697     /**
56698      * Gets a panel in the header of the grid that can be used for toolbars etc.
56699      * After modifying the contents of this panel a call to grid.autoSize() may be
56700      * required to register any changes in size.
56701      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56702      * @return Roo.Element
56703      */
56704     getHeaderPanel : function(doShow){
56705         if(doShow){
56706             this.headerPanel.show();
56707         }
56708         return this.headerPanel;
56709     },
56710
56711     /**
56712      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56713      * After modifying the contents of this panel a call to grid.autoSize() may be
56714      * required to register any changes in size.
56715      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56716      * @return Roo.Element
56717      */
56718     getFooterPanel : function(doShow){
56719         if(doShow){
56720             this.footerPanel.show();
56721         }
56722         return this.footerPanel;
56723     },
56724
56725     initElements : function(){
56726         var E = Roo.Element;
56727         var el = this.grid.getGridEl().dom.firstChild;
56728         var cs = el.childNodes;
56729
56730         this.el = new E(el);
56731         
56732          this.focusEl = new E(el.firstChild);
56733         this.focusEl.swallowEvent("click", true);
56734         
56735         this.headerPanel = new E(cs[1]);
56736         this.headerPanel.enableDisplayMode("block");
56737
56738         this.scroller = new E(cs[2]);
56739         this.scrollSizer = new E(this.scroller.dom.firstChild);
56740
56741         this.lockedWrap = new E(cs[3]);
56742         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56743         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56744
56745         this.mainWrap = new E(cs[4]);
56746         this.mainHd = new E(this.mainWrap.dom.firstChild);
56747         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56748
56749         this.footerPanel = new E(cs[5]);
56750         this.footerPanel.enableDisplayMode("block");
56751
56752         this.resizeProxy = new E(cs[6]);
56753
56754         this.headerSelector = String.format(
56755            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56756            this.lockedHd.id, this.mainHd.id
56757         );
56758
56759         this.splitterSelector = String.format(
56760            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56761            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56762         );
56763     },
56764     idToCssName : function(s)
56765     {
56766         return s.replace(/[^a-z0-9]+/ig, '-');
56767     },
56768
56769     getHeaderCell : function(index){
56770         return Roo.DomQuery.select(this.headerSelector)[index];
56771     },
56772
56773     getHeaderCellMeasure : function(index){
56774         return this.getHeaderCell(index).firstChild;
56775     },
56776
56777     getHeaderCellText : function(index){
56778         return this.getHeaderCell(index).firstChild.firstChild;
56779     },
56780
56781     getLockedTable : function(){
56782         return this.lockedBody.dom.firstChild;
56783     },
56784
56785     getBodyTable : function(){
56786         return this.mainBody.dom.firstChild;
56787     },
56788
56789     getLockedRow : function(index){
56790         return this.getLockedTable().rows[index];
56791     },
56792
56793     getRow : function(index){
56794         return this.getBodyTable().rows[index];
56795     },
56796
56797     getRowComposite : function(index){
56798         if(!this.rowEl){
56799             this.rowEl = new Roo.CompositeElementLite();
56800         }
56801         var els = [], lrow, mrow;
56802         if(lrow = this.getLockedRow(index)){
56803             els.push(lrow);
56804         }
56805         if(mrow = this.getRow(index)){
56806             els.push(mrow);
56807         }
56808         this.rowEl.elements = els;
56809         return this.rowEl;
56810     },
56811     /**
56812      * Gets the 'td' of the cell
56813      * 
56814      * @param {Integer} rowIndex row to select
56815      * @param {Integer} colIndex column to select
56816      * 
56817      * @return {Object} 
56818      */
56819     getCell : function(rowIndex, colIndex){
56820         var locked = this.cm.getLockedCount();
56821         var source;
56822         if(colIndex < locked){
56823             source = this.lockedBody.dom.firstChild;
56824         }else{
56825             source = this.mainBody.dom.firstChild;
56826             colIndex -= locked;
56827         }
56828         return source.rows[rowIndex].childNodes[colIndex];
56829     },
56830
56831     getCellText : function(rowIndex, colIndex){
56832         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56833     },
56834
56835     getCellBox : function(cell){
56836         var b = this.fly(cell).getBox();
56837         if(Roo.isOpera){ // opera fails to report the Y
56838             b.y = cell.offsetTop + this.mainBody.getY();
56839         }
56840         return b;
56841     },
56842
56843     getCellIndex : function(cell){
56844         var id = String(cell.className).match(this.cellRE);
56845         if(id){
56846             return parseInt(id[1], 10);
56847         }
56848         return 0;
56849     },
56850
56851     findHeaderIndex : function(n){
56852         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56853         return r ? this.getCellIndex(r) : false;
56854     },
56855
56856     findHeaderCell : function(n){
56857         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56858         return r ? r : false;
56859     },
56860
56861     findRowIndex : function(n){
56862         if(!n){
56863             return false;
56864         }
56865         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56866         return r ? r.rowIndex : false;
56867     },
56868
56869     findCellIndex : function(node){
56870         var stop = this.el.dom;
56871         while(node && node != stop){
56872             if(this.findRE.test(node.className)){
56873                 return this.getCellIndex(node);
56874             }
56875             node = node.parentNode;
56876         }
56877         return false;
56878     },
56879
56880     getColumnId : function(index){
56881         return this.cm.getColumnId(index);
56882     },
56883
56884     getSplitters : function()
56885     {
56886         if(this.splitterSelector){
56887            return Roo.DomQuery.select(this.splitterSelector);
56888         }else{
56889             return null;
56890       }
56891     },
56892
56893     getSplitter : function(index){
56894         return this.getSplitters()[index];
56895     },
56896
56897     onRowOver : function(e, t){
56898         var row;
56899         if((row = this.findRowIndex(t)) !== false){
56900             this.getRowComposite(row).addClass("x-grid-row-over");
56901         }
56902     },
56903
56904     onRowOut : function(e, t){
56905         var row;
56906         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56907             this.getRowComposite(row).removeClass("x-grid-row-over");
56908         }
56909     },
56910
56911     renderHeaders : function(){
56912         var cm = this.cm;
56913         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56914         var cb = [], lb = [], sb = [], lsb = [], p = {};
56915         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56916             p.cellId = "x-grid-hd-0-" + i;
56917             p.splitId = "x-grid-csplit-0-" + i;
56918             p.id = cm.getColumnId(i);
56919             p.value = cm.getColumnHeader(i) || "";
56920             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56921             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56922             if(!cm.isLocked(i)){
56923                 cb[cb.length] = ct.apply(p);
56924                 sb[sb.length] = st.apply(p);
56925             }else{
56926                 lb[lb.length] = ct.apply(p);
56927                 lsb[lsb.length] = st.apply(p);
56928             }
56929         }
56930         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56931                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56932     },
56933
56934     updateHeaders : function(){
56935         var html = this.renderHeaders();
56936         this.lockedHd.update(html[0]);
56937         this.mainHd.update(html[1]);
56938     },
56939
56940     /**
56941      * Focuses the specified row.
56942      * @param {Number} row The row index
56943      */
56944     focusRow : function(row)
56945     {
56946         //Roo.log('GridView.focusRow');
56947         var x = this.scroller.dom.scrollLeft;
56948         this.focusCell(row, 0, false);
56949         this.scroller.dom.scrollLeft = x;
56950     },
56951
56952     /**
56953      * Focuses the specified cell.
56954      * @param {Number} row The row index
56955      * @param {Number} col The column index
56956      * @param {Boolean} hscroll false to disable horizontal scrolling
56957      */
56958     focusCell : function(row, col, hscroll)
56959     {
56960         //Roo.log('GridView.focusCell');
56961         var el = this.ensureVisible(row, col, hscroll);
56962         this.focusEl.alignTo(el, "tl-tl");
56963         if(Roo.isGecko){
56964             this.focusEl.focus();
56965         }else{
56966             this.focusEl.focus.defer(1, this.focusEl);
56967         }
56968     },
56969
56970     /**
56971      * Scrolls the specified cell into view
56972      * @param {Number} row The row index
56973      * @param {Number} col The column index
56974      * @param {Boolean} hscroll false to disable horizontal scrolling
56975      */
56976     ensureVisible : function(row, col, hscroll)
56977     {
56978         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56979         //return null; //disable for testing.
56980         if(typeof row != "number"){
56981             row = row.rowIndex;
56982         }
56983         if(row < 0 && row >= this.ds.getCount()){
56984             return  null;
56985         }
56986         col = (col !== undefined ? col : 0);
56987         var cm = this.grid.colModel;
56988         while(cm.isHidden(col)){
56989             col++;
56990         }
56991
56992         var el = this.getCell(row, col);
56993         if(!el){
56994             return null;
56995         }
56996         var c = this.scroller.dom;
56997
56998         var ctop = parseInt(el.offsetTop, 10);
56999         var cleft = parseInt(el.offsetLeft, 10);
57000         var cbot = ctop + el.offsetHeight;
57001         var cright = cleft + el.offsetWidth;
57002         
57003         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57004         var stop = parseInt(c.scrollTop, 10);
57005         var sleft = parseInt(c.scrollLeft, 10);
57006         var sbot = stop + ch;
57007         var sright = sleft + c.clientWidth;
57008         /*
57009         Roo.log('GridView.ensureVisible:' +
57010                 ' ctop:' + ctop +
57011                 ' c.clientHeight:' + c.clientHeight +
57012                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57013                 ' stop:' + stop +
57014                 ' cbot:' + cbot +
57015                 ' sbot:' + sbot +
57016                 ' ch:' + ch  
57017                 );
57018         */
57019         if(ctop < stop){
57020             c.scrollTop = ctop;
57021             //Roo.log("set scrolltop to ctop DISABLE?");
57022         }else if(cbot > sbot){
57023             //Roo.log("set scrolltop to cbot-ch");
57024             c.scrollTop = cbot-ch;
57025         }
57026         
57027         if(hscroll !== false){
57028             if(cleft < sleft){
57029                 c.scrollLeft = cleft;
57030             }else if(cright > sright){
57031                 c.scrollLeft = cright-c.clientWidth;
57032             }
57033         }
57034          
57035         return el;
57036     },
57037
57038     updateColumns : function(){
57039         this.grid.stopEditing();
57040         var cm = this.grid.colModel, colIds = this.getColumnIds();
57041         //var totalWidth = cm.getTotalWidth();
57042         var pos = 0;
57043         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57044             //if(cm.isHidden(i)) continue;
57045             var w = cm.getColumnWidth(i);
57046             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57047             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57048         }
57049         this.updateSplitters();
57050     },
57051
57052     generateRules : function(cm){
57053         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57054         Roo.util.CSS.removeStyleSheet(rulesId);
57055         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57056             var cid = cm.getColumnId(i);
57057             var align = '';
57058             if(cm.config[i].align){
57059                 align = 'text-align:'+cm.config[i].align+';';
57060             }
57061             var hidden = '';
57062             if(cm.isHidden(i)){
57063                 hidden = 'display:none;';
57064             }
57065             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57066             ruleBuf.push(
57067                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57068                     this.hdSelector, cid, " {\n", align, width, "}\n",
57069                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57070                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57071         }
57072         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57073     },
57074
57075     updateSplitters : function(){
57076         var cm = this.cm, s = this.getSplitters();
57077         if(s){ // splitters not created yet
57078             var pos = 0, locked = true;
57079             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57080                 if(cm.isHidden(i)) {
57081                     continue;
57082                 }
57083                 var w = cm.getColumnWidth(i); // make sure it's a number
57084                 if(!cm.isLocked(i) && locked){
57085                     pos = 0;
57086                     locked = false;
57087                 }
57088                 pos += w;
57089                 s[i].style.left = (pos-this.splitOffset) + "px";
57090             }
57091         }
57092     },
57093
57094     handleHiddenChange : function(colModel, colIndex, hidden){
57095         if(hidden){
57096             this.hideColumn(colIndex);
57097         }else{
57098             this.unhideColumn(colIndex);
57099         }
57100     },
57101
57102     hideColumn : function(colIndex){
57103         var cid = this.getColumnId(colIndex);
57104         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57105         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57106         if(Roo.isSafari){
57107             this.updateHeaders();
57108         }
57109         this.updateSplitters();
57110         this.layout();
57111     },
57112
57113     unhideColumn : function(colIndex){
57114         var cid = this.getColumnId(colIndex);
57115         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57116         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57117
57118         if(Roo.isSafari){
57119             this.updateHeaders();
57120         }
57121         this.updateSplitters();
57122         this.layout();
57123     },
57124
57125     insertRows : function(dm, firstRow, lastRow, isUpdate){
57126         if(firstRow == 0 && lastRow == dm.getCount()-1){
57127             this.refresh();
57128         }else{
57129             if(!isUpdate){
57130                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57131             }
57132             var s = this.getScrollState();
57133             var markup = this.renderRows(firstRow, lastRow);
57134             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57135             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57136             this.restoreScroll(s);
57137             if(!isUpdate){
57138                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57139                 this.syncRowHeights(firstRow, lastRow);
57140                 this.stripeRows(firstRow);
57141                 this.layout();
57142             }
57143         }
57144     },
57145
57146     bufferRows : function(markup, target, index){
57147         var before = null, trows = target.rows, tbody = target.tBodies[0];
57148         if(index < trows.length){
57149             before = trows[index];
57150         }
57151         var b = document.createElement("div");
57152         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57153         var rows = b.firstChild.rows;
57154         for(var i = 0, len = rows.length; i < len; i++){
57155             if(before){
57156                 tbody.insertBefore(rows[0], before);
57157             }else{
57158                 tbody.appendChild(rows[0]);
57159             }
57160         }
57161         b.innerHTML = "";
57162         b = null;
57163     },
57164
57165     deleteRows : function(dm, firstRow, lastRow){
57166         if(dm.getRowCount()<1){
57167             this.fireEvent("beforerefresh", this);
57168             this.mainBody.update("");
57169             this.lockedBody.update("");
57170             this.fireEvent("refresh", this);
57171         }else{
57172             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57173             var bt = this.getBodyTable();
57174             var tbody = bt.firstChild;
57175             var rows = bt.rows;
57176             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57177                 tbody.removeChild(rows[firstRow]);
57178             }
57179             this.stripeRows(firstRow);
57180             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57181         }
57182     },
57183
57184     updateRows : function(dataSource, firstRow, lastRow){
57185         var s = this.getScrollState();
57186         this.refresh();
57187         this.restoreScroll(s);
57188     },
57189
57190     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57191         if(!noRefresh){
57192            this.refresh();
57193         }
57194         this.updateHeaderSortState();
57195     },
57196
57197     getScrollState : function(){
57198         
57199         var sb = this.scroller.dom;
57200         return {left: sb.scrollLeft, top: sb.scrollTop};
57201     },
57202
57203     stripeRows : function(startRow){
57204         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57205             return;
57206         }
57207         startRow = startRow || 0;
57208         var rows = this.getBodyTable().rows;
57209         var lrows = this.getLockedTable().rows;
57210         var cls = ' x-grid-row-alt ';
57211         for(var i = startRow, len = rows.length; i < len; i++){
57212             var row = rows[i], lrow = lrows[i];
57213             var isAlt = ((i+1) % 2 == 0);
57214             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57215             if(isAlt == hasAlt){
57216                 continue;
57217             }
57218             if(isAlt){
57219                 row.className += " x-grid-row-alt";
57220             }else{
57221                 row.className = row.className.replace("x-grid-row-alt", "");
57222             }
57223             if(lrow){
57224                 lrow.className = row.className;
57225             }
57226         }
57227     },
57228
57229     restoreScroll : function(state){
57230         //Roo.log('GridView.restoreScroll');
57231         var sb = this.scroller.dom;
57232         sb.scrollLeft = state.left;
57233         sb.scrollTop = state.top;
57234         this.syncScroll();
57235     },
57236
57237     syncScroll : function(){
57238         //Roo.log('GridView.syncScroll');
57239         var sb = this.scroller.dom;
57240         var sh = this.mainHd.dom;
57241         var bs = this.mainBody.dom;
57242         var lv = this.lockedBody.dom;
57243         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57244         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57245     },
57246
57247     handleScroll : function(e){
57248         this.syncScroll();
57249         var sb = this.scroller.dom;
57250         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57251         e.stopEvent();
57252     },
57253
57254     handleWheel : function(e){
57255         var d = e.getWheelDelta();
57256         this.scroller.dom.scrollTop -= d*22;
57257         // set this here to prevent jumpy scrolling on large tables
57258         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57259         e.stopEvent();
57260     },
57261
57262     renderRows : function(startRow, endRow){
57263         // pull in all the crap needed to render rows
57264         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57265         var colCount = cm.getColumnCount();
57266
57267         if(ds.getCount() < 1){
57268             return ["", ""];
57269         }
57270
57271         // build a map for all the columns
57272         var cs = [];
57273         for(var i = 0; i < colCount; i++){
57274             var name = cm.getDataIndex(i);
57275             cs[i] = {
57276                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57277                 renderer : cm.getRenderer(i),
57278                 id : cm.getColumnId(i),
57279                 locked : cm.isLocked(i),
57280                 has_editor : cm.isCellEditable(i)
57281             };
57282         }
57283
57284         startRow = startRow || 0;
57285         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57286
57287         // records to render
57288         var rs = ds.getRange(startRow, endRow);
57289
57290         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57291     },
57292
57293     // As much as I hate to duplicate code, this was branched because FireFox really hates
57294     // [].join("") on strings. The performance difference was substantial enough to
57295     // branch this function
57296     doRender : Roo.isGecko ?
57297             function(cs, rs, ds, startRow, colCount, stripe){
57298                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57299                 // buffers
57300                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57301                 
57302                 var hasListener = this.grid.hasListener('rowclass');
57303                 var rowcfg = {};
57304                 for(var j = 0, len = rs.length; j < len; j++){
57305                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57306                     for(var i = 0; i < colCount; i++){
57307                         c = cs[i];
57308                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57309                         p.id = c.id;
57310                         p.css = p.attr = "";
57311                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57312                         if(p.value == undefined || p.value === "") {
57313                             p.value = "&#160;";
57314                         }
57315                         if(c.has_editor){
57316                             p.css += ' x-grid-editable-cell';
57317                         }
57318                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57319                             p.css +=  ' x-grid-dirty-cell';
57320                         }
57321                         var markup = ct.apply(p);
57322                         if(!c.locked){
57323                             cb+= markup;
57324                         }else{
57325                             lcb+= markup;
57326                         }
57327                     }
57328                     var alt = [];
57329                     if(stripe && ((rowIndex+1) % 2 == 0)){
57330                         alt.push("x-grid-row-alt")
57331                     }
57332                     if(r.dirty){
57333                         alt.push(  " x-grid-dirty-row");
57334                     }
57335                     rp.cells = lcb;
57336                     if(this.getRowClass){
57337                         alt.push(this.getRowClass(r, rowIndex));
57338                     }
57339                     if (hasListener) {
57340                         rowcfg = {
57341                              
57342                             record: r,
57343                             rowIndex : rowIndex,
57344                             rowClass : ''
57345                         };
57346                         this.grid.fireEvent('rowclass', this, rowcfg);
57347                         alt.push(rowcfg.rowClass);
57348                     }
57349                     rp.alt = alt.join(" ");
57350                     lbuf+= rt.apply(rp);
57351                     rp.cells = cb;
57352                     buf+=  rt.apply(rp);
57353                 }
57354                 return [lbuf, buf];
57355             } :
57356             function(cs, rs, ds, startRow, colCount, stripe){
57357                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57358                 // buffers
57359                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57360                 var hasListener = this.grid.hasListener('rowclass');
57361  
57362                 var rowcfg = {};
57363                 for(var j = 0, len = rs.length; j < len; j++){
57364                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57365                     for(var i = 0; i < colCount; i++){
57366                         c = cs[i];
57367                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57368                         p.id = c.id;
57369                         p.css = p.attr = "";
57370                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57371                         if(p.value == undefined || p.value === "") {
57372                             p.value = "&#160;";
57373                         }
57374                         //Roo.log(c);
57375                          if(c.has_editor){
57376                             p.css += ' x-grid-editable-cell';
57377                         }
57378                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57379                             p.css += ' x-grid-dirty-cell' 
57380                         }
57381                         
57382                         var markup = ct.apply(p);
57383                         if(!c.locked){
57384                             cb[cb.length] = markup;
57385                         }else{
57386                             lcb[lcb.length] = markup;
57387                         }
57388                     }
57389                     var alt = [];
57390                     if(stripe && ((rowIndex+1) % 2 == 0)){
57391                         alt.push( "x-grid-row-alt");
57392                     }
57393                     if(r.dirty){
57394                         alt.push(" x-grid-dirty-row");
57395                     }
57396                     rp.cells = lcb;
57397                     if(this.getRowClass){
57398                         alt.push( this.getRowClass(r, rowIndex));
57399                     }
57400                     if (hasListener) {
57401                         rowcfg = {
57402                              
57403                             record: r,
57404                             rowIndex : rowIndex,
57405                             rowClass : ''
57406                         };
57407                         this.grid.fireEvent('rowclass', this, rowcfg);
57408                         alt.push(rowcfg.rowClass);
57409                     }
57410                     
57411                     rp.alt = alt.join(" ");
57412                     rp.cells = lcb.join("");
57413                     lbuf[lbuf.length] = rt.apply(rp);
57414                     rp.cells = cb.join("");
57415                     buf[buf.length] =  rt.apply(rp);
57416                 }
57417                 return [lbuf.join(""), buf.join("")];
57418             },
57419
57420     renderBody : function(){
57421         var markup = this.renderRows();
57422         var bt = this.templates.body;
57423         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57424     },
57425
57426     /**
57427      * Refreshes the grid
57428      * @param {Boolean} headersToo
57429      */
57430     refresh : function(headersToo){
57431         this.fireEvent("beforerefresh", this);
57432         this.grid.stopEditing();
57433         var result = this.renderBody();
57434         this.lockedBody.update(result[0]);
57435         this.mainBody.update(result[1]);
57436         if(headersToo === true){
57437             this.updateHeaders();
57438             this.updateColumns();
57439             this.updateSplitters();
57440             this.updateHeaderSortState();
57441         }
57442         this.syncRowHeights();
57443         this.layout();
57444         this.fireEvent("refresh", this);
57445     },
57446
57447     handleColumnMove : function(cm, oldIndex, newIndex){
57448         this.indexMap = null;
57449         var s = this.getScrollState();
57450         this.refresh(true);
57451         this.restoreScroll(s);
57452         this.afterMove(newIndex);
57453     },
57454
57455     afterMove : function(colIndex){
57456         if(this.enableMoveAnim && Roo.enableFx){
57457             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57458         }
57459         // if multisort - fix sortOrder, and reload..
57460         if (this.grid.dataSource.multiSort) {
57461             // the we can call sort again..
57462             var dm = this.grid.dataSource;
57463             var cm = this.grid.colModel;
57464             var so = [];
57465             for(var i = 0; i < cm.config.length; i++ ) {
57466                 
57467                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57468                     continue; // dont' bother, it's not in sort list or being set.
57469                 }
57470                 
57471                 so.push(cm.config[i].dataIndex);
57472             };
57473             dm.sortOrder = so;
57474             dm.load(dm.lastOptions);
57475             
57476             
57477         }
57478         
57479     },
57480
57481     updateCell : function(dm, rowIndex, dataIndex){
57482         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57483         if(typeof colIndex == "undefined"){ // not present in grid
57484             return;
57485         }
57486         var cm = this.grid.colModel;
57487         var cell = this.getCell(rowIndex, colIndex);
57488         var cellText = this.getCellText(rowIndex, colIndex);
57489
57490         var p = {
57491             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57492             id : cm.getColumnId(colIndex),
57493             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57494         };
57495         var renderer = cm.getRenderer(colIndex);
57496         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57497         if(typeof val == "undefined" || val === "") {
57498             val = "&#160;";
57499         }
57500         cellText.innerHTML = val;
57501         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57502         this.syncRowHeights(rowIndex, rowIndex);
57503     },
57504
57505     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57506         var maxWidth = 0;
57507         if(this.grid.autoSizeHeaders){
57508             var h = this.getHeaderCellMeasure(colIndex);
57509             maxWidth = Math.max(maxWidth, h.scrollWidth);
57510         }
57511         var tb, index;
57512         if(this.cm.isLocked(colIndex)){
57513             tb = this.getLockedTable();
57514             index = colIndex;
57515         }else{
57516             tb = this.getBodyTable();
57517             index = colIndex - this.cm.getLockedCount();
57518         }
57519         if(tb && tb.rows){
57520             var rows = tb.rows;
57521             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57522             for(var i = 0; i < stopIndex; i++){
57523                 var cell = rows[i].childNodes[index].firstChild;
57524                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57525             }
57526         }
57527         return maxWidth + /*margin for error in IE*/ 5;
57528     },
57529     /**
57530      * Autofit a column to its content.
57531      * @param {Number} colIndex
57532      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57533      */
57534      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57535          if(this.cm.isHidden(colIndex)){
57536              return; // can't calc a hidden column
57537          }
57538         if(forceMinSize){
57539             var cid = this.cm.getColumnId(colIndex);
57540             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57541            if(this.grid.autoSizeHeaders){
57542                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57543            }
57544         }
57545         var newWidth = this.calcColumnWidth(colIndex);
57546         this.cm.setColumnWidth(colIndex,
57547             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57548         if(!suppressEvent){
57549             this.grid.fireEvent("columnresize", colIndex, newWidth);
57550         }
57551     },
57552
57553     /**
57554      * Autofits all columns to their content and then expands to fit any extra space in the grid
57555      */
57556      autoSizeColumns : function(){
57557         var cm = this.grid.colModel;
57558         var colCount = cm.getColumnCount();
57559         for(var i = 0; i < colCount; i++){
57560             this.autoSizeColumn(i, true, true);
57561         }
57562         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57563             this.fitColumns();
57564         }else{
57565             this.updateColumns();
57566             this.layout();
57567         }
57568     },
57569
57570     /**
57571      * Autofits all columns to the grid's width proportionate with their current size
57572      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57573      */
57574     fitColumns : function(reserveScrollSpace){
57575         var cm = this.grid.colModel;
57576         var colCount = cm.getColumnCount();
57577         var cols = [];
57578         var width = 0;
57579         var i, w;
57580         for (i = 0; i < colCount; i++){
57581             if(!cm.isHidden(i) && !cm.isFixed(i)){
57582                 w = cm.getColumnWidth(i);
57583                 cols.push(i);
57584                 cols.push(w);
57585                 width += w;
57586             }
57587         }
57588         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57589         if(reserveScrollSpace){
57590             avail -= 17;
57591         }
57592         var frac = (avail - cm.getTotalWidth())/width;
57593         while (cols.length){
57594             w = cols.pop();
57595             i = cols.pop();
57596             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57597         }
57598         this.updateColumns();
57599         this.layout();
57600     },
57601
57602     onRowSelect : function(rowIndex){
57603         var row = this.getRowComposite(rowIndex);
57604         row.addClass("x-grid-row-selected");
57605     },
57606
57607     onRowDeselect : function(rowIndex){
57608         var row = this.getRowComposite(rowIndex);
57609         row.removeClass("x-grid-row-selected");
57610     },
57611
57612     onCellSelect : function(row, col){
57613         var cell = this.getCell(row, col);
57614         if(cell){
57615             Roo.fly(cell).addClass("x-grid-cell-selected");
57616         }
57617     },
57618
57619     onCellDeselect : function(row, col){
57620         var cell = this.getCell(row, col);
57621         if(cell){
57622             Roo.fly(cell).removeClass("x-grid-cell-selected");
57623         }
57624     },
57625
57626     updateHeaderSortState : function(){
57627         
57628         // sort state can be single { field: xxx, direction : yyy}
57629         // or   { xxx=>ASC , yyy : DESC ..... }
57630         
57631         var mstate = {};
57632         if (!this.ds.multiSort) { 
57633             var state = this.ds.getSortState();
57634             if(!state){
57635                 return;
57636             }
57637             mstate[state.field] = state.direction;
57638             // FIXME... - this is not used here.. but might be elsewhere..
57639             this.sortState = state;
57640             
57641         } else {
57642             mstate = this.ds.sortToggle;
57643         }
57644         //remove existing sort classes..
57645         
57646         var sc = this.sortClasses;
57647         var hds = this.el.select(this.headerSelector).removeClass(sc);
57648         
57649         for(var f in mstate) {
57650         
57651             var sortColumn = this.cm.findColumnIndex(f);
57652             
57653             if(sortColumn != -1){
57654                 var sortDir = mstate[f];        
57655                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57656             }
57657         }
57658         
57659          
57660         
57661     },
57662
57663
57664     handleHeaderClick : function(g, index,e){
57665         
57666         Roo.log("header click");
57667         
57668         if (Roo.isTouch) {
57669             // touch events on header are handled by context
57670             this.handleHdCtx(g,index,e);
57671             return;
57672         }
57673         
57674         
57675         if(this.headersDisabled){
57676             return;
57677         }
57678         var dm = g.dataSource, cm = g.colModel;
57679         if(!cm.isSortable(index)){
57680             return;
57681         }
57682         g.stopEditing();
57683         
57684         if (dm.multiSort) {
57685             // update the sortOrder
57686             var so = [];
57687             for(var i = 0; i < cm.config.length; i++ ) {
57688                 
57689                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57690                     continue; // dont' bother, it's not in sort list or being set.
57691                 }
57692                 
57693                 so.push(cm.config[i].dataIndex);
57694             };
57695             dm.sortOrder = so;
57696         }
57697         
57698         
57699         dm.sort(cm.getDataIndex(index));
57700     },
57701
57702
57703     destroy : function(){
57704         if(this.colMenu){
57705             this.colMenu.removeAll();
57706             Roo.menu.MenuMgr.unregister(this.colMenu);
57707             this.colMenu.getEl().remove();
57708             delete this.colMenu;
57709         }
57710         if(this.hmenu){
57711             this.hmenu.removeAll();
57712             Roo.menu.MenuMgr.unregister(this.hmenu);
57713             this.hmenu.getEl().remove();
57714             delete this.hmenu;
57715         }
57716         if(this.grid.enableColumnMove){
57717             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57718             if(dds){
57719                 for(var dd in dds){
57720                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57721                         var elid = dds[dd].dragElId;
57722                         dds[dd].unreg();
57723                         Roo.get(elid).remove();
57724                     } else if(dds[dd].config.isTarget){
57725                         dds[dd].proxyTop.remove();
57726                         dds[dd].proxyBottom.remove();
57727                         dds[dd].unreg();
57728                     }
57729                     if(Roo.dd.DDM.locationCache[dd]){
57730                         delete Roo.dd.DDM.locationCache[dd];
57731                     }
57732                 }
57733                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57734             }
57735         }
57736         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57737         this.bind(null, null);
57738         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57739     },
57740
57741     handleLockChange : function(){
57742         this.refresh(true);
57743     },
57744
57745     onDenyColumnLock : function(){
57746
57747     },
57748
57749     onDenyColumnHide : function(){
57750
57751     },
57752
57753     handleHdMenuClick : function(item){
57754         var index = this.hdCtxIndex;
57755         var cm = this.cm, ds = this.ds;
57756         switch(item.id){
57757             case "asc":
57758                 ds.sort(cm.getDataIndex(index), "ASC");
57759                 break;
57760             case "desc":
57761                 ds.sort(cm.getDataIndex(index), "DESC");
57762                 break;
57763             case "lock":
57764                 var lc = cm.getLockedCount();
57765                 if(cm.getColumnCount(true) <= lc+1){
57766                     this.onDenyColumnLock();
57767                     return;
57768                 }
57769                 if(lc != index){
57770                     cm.setLocked(index, true, true);
57771                     cm.moveColumn(index, lc);
57772                     this.grid.fireEvent("columnmove", index, lc);
57773                 }else{
57774                     cm.setLocked(index, true);
57775                 }
57776             break;
57777             case "unlock":
57778                 var lc = cm.getLockedCount();
57779                 if((lc-1) != index){
57780                     cm.setLocked(index, false, true);
57781                     cm.moveColumn(index, lc-1);
57782                     this.grid.fireEvent("columnmove", index, lc-1);
57783                 }else{
57784                     cm.setLocked(index, false);
57785                 }
57786             break;
57787             case 'wider': // used to expand cols on touch..
57788             case 'narrow':
57789                 var cw = cm.getColumnWidth(index);
57790                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57791                 cw = Math.max(0, cw);
57792                 cw = Math.min(cw,4000);
57793                 cm.setColumnWidth(index, cw);
57794                 break;
57795                 
57796             default:
57797                 index = cm.getIndexById(item.id.substr(4));
57798                 if(index != -1){
57799                     if(item.checked && cm.getColumnCount(true) <= 1){
57800                         this.onDenyColumnHide();
57801                         return false;
57802                     }
57803                     cm.setHidden(index, item.checked);
57804                 }
57805         }
57806         return true;
57807     },
57808
57809     beforeColMenuShow : function(){
57810         var cm = this.cm,  colCount = cm.getColumnCount();
57811         this.colMenu.removeAll();
57812         for(var i = 0; i < colCount; i++){
57813             this.colMenu.add(new Roo.menu.CheckItem({
57814                 id: "col-"+cm.getColumnId(i),
57815                 text: cm.getColumnHeader(i),
57816                 checked: !cm.isHidden(i),
57817                 hideOnClick:false
57818             }));
57819         }
57820     },
57821
57822     handleHdCtx : function(g, index, e){
57823         e.stopEvent();
57824         var hd = this.getHeaderCell(index);
57825         this.hdCtxIndex = index;
57826         var ms = this.hmenu.items, cm = this.cm;
57827         ms.get("asc").setDisabled(!cm.isSortable(index));
57828         ms.get("desc").setDisabled(!cm.isSortable(index));
57829         if(this.grid.enableColLock !== false){
57830             ms.get("lock").setDisabled(cm.isLocked(index));
57831             ms.get("unlock").setDisabled(!cm.isLocked(index));
57832         }
57833         this.hmenu.show(hd, "tl-bl");
57834     },
57835
57836     handleHdOver : function(e){
57837         var hd = this.findHeaderCell(e.getTarget());
57838         if(hd && !this.headersDisabled){
57839             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57840                this.fly(hd).addClass("x-grid-hd-over");
57841             }
57842         }
57843     },
57844
57845     handleHdOut : function(e){
57846         var hd = this.findHeaderCell(e.getTarget());
57847         if(hd){
57848             this.fly(hd).removeClass("x-grid-hd-over");
57849         }
57850     },
57851
57852     handleSplitDblClick : function(e, t){
57853         var i = this.getCellIndex(t);
57854         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57855             this.autoSizeColumn(i, true);
57856             this.layout();
57857         }
57858     },
57859
57860     render : function(){
57861
57862         var cm = this.cm;
57863         var colCount = cm.getColumnCount();
57864
57865         if(this.grid.monitorWindowResize === true){
57866             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57867         }
57868         var header = this.renderHeaders();
57869         var body = this.templates.body.apply({rows:""});
57870         var html = this.templates.master.apply({
57871             lockedBody: body,
57872             body: body,
57873             lockedHeader: header[0],
57874             header: header[1]
57875         });
57876
57877         //this.updateColumns();
57878
57879         this.grid.getGridEl().dom.innerHTML = html;
57880
57881         this.initElements();
57882         
57883         // a kludge to fix the random scolling effect in webkit
57884         this.el.on("scroll", function() {
57885             this.el.dom.scrollTop=0; // hopefully not recursive..
57886         },this);
57887
57888         this.scroller.on("scroll", this.handleScroll, this);
57889         this.lockedBody.on("mousewheel", this.handleWheel, this);
57890         this.mainBody.on("mousewheel", this.handleWheel, this);
57891
57892         this.mainHd.on("mouseover", this.handleHdOver, this);
57893         this.mainHd.on("mouseout", this.handleHdOut, this);
57894         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57895                 {delegate: "."+this.splitClass});
57896
57897         this.lockedHd.on("mouseover", this.handleHdOver, this);
57898         this.lockedHd.on("mouseout", this.handleHdOut, this);
57899         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57900                 {delegate: "."+this.splitClass});
57901
57902         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57903             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57904         }
57905
57906         this.updateSplitters();
57907
57908         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57909             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57910             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57911         }
57912
57913         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57914             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57915             this.hmenu.add(
57916                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57917                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57918             );
57919             if(this.grid.enableColLock !== false){
57920                 this.hmenu.add('-',
57921                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57922                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57923                 );
57924             }
57925             if (Roo.isTouch) {
57926                  this.hmenu.add('-',
57927                     {id:"wider", text: this.columnsWiderText},
57928                     {id:"narrow", text: this.columnsNarrowText }
57929                 );
57930                 
57931                  
57932             }
57933             
57934             if(this.grid.enableColumnHide !== false){
57935
57936                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57937                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57938                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57939
57940                 this.hmenu.add('-',
57941                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57942                 );
57943             }
57944             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57945
57946             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57947         }
57948
57949         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57950             this.dd = new Roo.grid.GridDragZone(this.grid, {
57951                 ddGroup : this.grid.ddGroup || 'GridDD'
57952             });
57953             
57954         }
57955
57956         /*
57957         for(var i = 0; i < colCount; i++){
57958             if(cm.isHidden(i)){
57959                 this.hideColumn(i);
57960             }
57961             if(cm.config[i].align){
57962                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57963                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57964             }
57965         }*/
57966         
57967         this.updateHeaderSortState();
57968
57969         this.beforeInitialResize();
57970         this.layout(true);
57971
57972         // two part rendering gives faster view to the user
57973         this.renderPhase2.defer(1, this);
57974     },
57975
57976     renderPhase2 : function(){
57977         // render the rows now
57978         this.refresh();
57979         if(this.grid.autoSizeColumns){
57980             this.autoSizeColumns();
57981         }
57982     },
57983
57984     beforeInitialResize : function(){
57985
57986     },
57987
57988     onColumnSplitterMoved : function(i, w){
57989         this.userResized = true;
57990         var cm = this.grid.colModel;
57991         cm.setColumnWidth(i, w, true);
57992         var cid = cm.getColumnId(i);
57993         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57994         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57995         this.updateSplitters();
57996         this.layout();
57997         this.grid.fireEvent("columnresize", i, w);
57998     },
57999
58000     syncRowHeights : function(startIndex, endIndex){
58001         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58002             startIndex = startIndex || 0;
58003             var mrows = this.getBodyTable().rows;
58004             var lrows = this.getLockedTable().rows;
58005             var len = mrows.length-1;
58006             endIndex = Math.min(endIndex || len, len);
58007             for(var i = startIndex; i <= endIndex; i++){
58008                 var m = mrows[i], l = lrows[i];
58009                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58010                 m.style.height = l.style.height = h + "px";
58011             }
58012         }
58013     },
58014
58015     layout : function(initialRender, is2ndPass)
58016     {
58017         var g = this.grid;
58018         var auto = g.autoHeight;
58019         var scrollOffset = 16;
58020         var c = g.getGridEl(), cm = this.cm,
58021                 expandCol = g.autoExpandColumn,
58022                 gv = this;
58023         //c.beginMeasure();
58024
58025         if(!c.dom.offsetWidth){ // display:none?
58026             if(initialRender){
58027                 this.lockedWrap.show();
58028                 this.mainWrap.show();
58029             }
58030             return;
58031         }
58032
58033         var hasLock = this.cm.isLocked(0);
58034
58035         var tbh = this.headerPanel.getHeight();
58036         var bbh = this.footerPanel.getHeight();
58037
58038         if(auto){
58039             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58040             var newHeight = ch + c.getBorderWidth("tb");
58041             if(g.maxHeight){
58042                 newHeight = Math.min(g.maxHeight, newHeight);
58043             }
58044             c.setHeight(newHeight);
58045         }
58046
58047         if(g.autoWidth){
58048             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58049         }
58050
58051         var s = this.scroller;
58052
58053         var csize = c.getSize(true);
58054
58055         this.el.setSize(csize.width, csize.height);
58056
58057         this.headerPanel.setWidth(csize.width);
58058         this.footerPanel.setWidth(csize.width);
58059
58060         var hdHeight = this.mainHd.getHeight();
58061         var vw = csize.width;
58062         var vh = csize.height - (tbh + bbh);
58063
58064         s.setSize(vw, vh);
58065
58066         var bt = this.getBodyTable();
58067         
58068         if(cm.getLockedCount() == cm.config.length){
58069             bt = this.getLockedTable();
58070         }
58071         
58072         var ltWidth = hasLock ?
58073                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58074
58075         var scrollHeight = bt.offsetHeight;
58076         var scrollWidth = ltWidth + bt.offsetWidth;
58077         var vscroll = false, hscroll = false;
58078
58079         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58080
58081         var lw = this.lockedWrap, mw = this.mainWrap;
58082         var lb = this.lockedBody, mb = this.mainBody;
58083
58084         setTimeout(function(){
58085             var t = s.dom.offsetTop;
58086             var w = s.dom.clientWidth,
58087                 h = s.dom.clientHeight;
58088
58089             lw.setTop(t);
58090             lw.setSize(ltWidth, h);
58091
58092             mw.setLeftTop(ltWidth, t);
58093             mw.setSize(w-ltWidth, h);
58094
58095             lb.setHeight(h-hdHeight);
58096             mb.setHeight(h-hdHeight);
58097
58098             if(is2ndPass !== true && !gv.userResized && expandCol){
58099                 // high speed resize without full column calculation
58100                 
58101                 var ci = cm.getIndexById(expandCol);
58102                 if (ci < 0) {
58103                     ci = cm.findColumnIndex(expandCol);
58104                 }
58105                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58106                 var expandId = cm.getColumnId(ci);
58107                 var  tw = cm.getTotalWidth(false);
58108                 var currentWidth = cm.getColumnWidth(ci);
58109                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58110                 if(currentWidth != cw){
58111                     cm.setColumnWidth(ci, cw, true);
58112                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58113                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58114                     gv.updateSplitters();
58115                     gv.layout(false, true);
58116                 }
58117             }
58118
58119             if(initialRender){
58120                 lw.show();
58121                 mw.show();
58122             }
58123             //c.endMeasure();
58124         }, 10);
58125     },
58126
58127     onWindowResize : function(){
58128         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58129             return;
58130         }
58131         this.layout();
58132     },
58133
58134     appendFooter : function(parentEl){
58135         return null;
58136     },
58137
58138     sortAscText : "Sort Ascending",
58139     sortDescText : "Sort Descending",
58140     lockText : "Lock Column",
58141     unlockText : "Unlock Column",
58142     columnsText : "Columns",
58143  
58144     columnsWiderText : "Wider",
58145     columnsNarrowText : "Thinner"
58146 });
58147
58148
58149 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58150     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58151     this.proxy.el.addClass('x-grid3-col-dd');
58152 };
58153
58154 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58155     handleMouseDown : function(e){
58156
58157     },
58158
58159     callHandleMouseDown : function(e){
58160         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58161     }
58162 });
58163 /*
58164  * Based on:
58165  * Ext JS Library 1.1.1
58166  * Copyright(c) 2006-2007, Ext JS, LLC.
58167  *
58168  * Originally Released Under LGPL - original licence link has changed is not relivant.
58169  *
58170  * Fork - LGPL
58171  * <script type="text/javascript">
58172  */
58173  /**
58174  * @extends Roo.dd.DDProxy
58175  * @class Roo.grid.SplitDragZone
58176  * Support for Column Header resizing
58177  * @constructor
58178  * @param {Object} config
58179  */
58180 // private
58181 // This is a support class used internally by the Grid components
58182 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58183     this.grid = grid;
58184     this.view = grid.getView();
58185     this.proxy = this.view.resizeProxy;
58186     Roo.grid.SplitDragZone.superclass.constructor.call(
58187         this,
58188         hd, // ID
58189         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58190         {  // CONFIG
58191             dragElId : Roo.id(this.proxy.dom),
58192             resizeFrame:false
58193         }
58194     );
58195     
58196     this.setHandleElId(Roo.id(hd));
58197     if (hd2 !== false) {
58198         this.setOuterHandleElId(Roo.id(hd2));
58199     }
58200     
58201     this.scroll = false;
58202 };
58203 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58204     fly: Roo.Element.fly,
58205
58206     b4StartDrag : function(x, y){
58207         this.view.headersDisabled = true;
58208         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58209                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58210         );
58211         this.proxy.setHeight(h);
58212         
58213         // for old system colWidth really stored the actual width?
58214         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58215         // which in reality did not work.. - it worked only for fixed sizes
58216         // for resizable we need to use actual sizes.
58217         var w = this.cm.getColumnWidth(this.cellIndex);
58218         if (!this.view.mainWrap) {
58219             // bootstrap.
58220             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58221         }
58222         
58223         
58224         
58225         // this was w-this.grid.minColumnWidth;
58226         // doesnt really make sense? - w = thie curren width or the rendered one?
58227         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58228         this.resetConstraints();
58229         this.setXConstraint(minw, 1000);
58230         this.setYConstraint(0, 0);
58231         this.minX = x - minw;
58232         this.maxX = x + 1000;
58233         this.startPos = x;
58234         if (!this.view.mainWrap) { // this is Bootstrap code..
58235             this.getDragEl().style.display='block';
58236         }
58237         
58238         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58239     },
58240
58241
58242     handleMouseDown : function(e){
58243         ev = Roo.EventObject.setEvent(e);
58244         var t = this.fly(ev.getTarget());
58245         if(t.hasClass("x-grid-split")){
58246             this.cellIndex = this.view.getCellIndex(t.dom);
58247             this.split = t.dom;
58248             this.cm = this.grid.colModel;
58249             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58250                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58251             }
58252         }
58253     },
58254
58255     endDrag : function(e){
58256         this.view.headersDisabled = false;
58257         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58258         var diff = endX - this.startPos;
58259         // 
58260         var w = this.cm.getColumnWidth(this.cellIndex);
58261         if (!this.view.mainWrap) {
58262             w = 0;
58263         }
58264         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58265     },
58266
58267     autoOffset : function(){
58268         this.setDelta(0,0);
58269     }
58270 });/*
58271  * Based on:
58272  * Ext JS Library 1.1.1
58273  * Copyright(c) 2006-2007, Ext JS, LLC.
58274  *
58275  * Originally Released Under LGPL - original licence link has changed is not relivant.
58276  *
58277  * Fork - LGPL
58278  * <script type="text/javascript">
58279  */
58280  
58281 // private
58282 // This is a support class used internally by the Grid components
58283 Roo.grid.GridDragZone = function(grid, config){
58284     this.view = grid.getView();
58285     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58286     if(this.view.lockedBody){
58287         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58288         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58289     }
58290     this.scroll = false;
58291     this.grid = grid;
58292     this.ddel = document.createElement('div');
58293     this.ddel.className = 'x-grid-dd-wrap';
58294 };
58295
58296 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58297     ddGroup : "GridDD",
58298
58299     getDragData : function(e){
58300         var t = Roo.lib.Event.getTarget(e);
58301         var rowIndex = this.view.findRowIndex(t);
58302         var sm = this.grid.selModel;
58303             
58304         //Roo.log(rowIndex);
58305         
58306         if (sm.getSelectedCell) {
58307             // cell selection..
58308             if (!sm.getSelectedCell()) {
58309                 return false;
58310             }
58311             if (rowIndex != sm.getSelectedCell()[0]) {
58312                 return false;
58313             }
58314         
58315         }
58316         if (sm.getSelections && sm.getSelections().length < 1) {
58317             return false;
58318         }
58319         
58320         
58321         // before it used to all dragging of unseleted... - now we dont do that.
58322         if(rowIndex !== false){
58323             
58324             // if editorgrid.. 
58325             
58326             
58327             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58328                
58329             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58330               //  
58331             //}
58332             if (e.hasModifier()){
58333                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58334             }
58335             
58336             Roo.log("getDragData");
58337             
58338             return {
58339                 grid: this.grid,
58340                 ddel: this.ddel,
58341                 rowIndex: rowIndex,
58342                 selections: sm.getSelections ? sm.getSelections() : (
58343                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58344             };
58345         }
58346         return false;
58347     },
58348     
58349     
58350     onInitDrag : function(e){
58351         var data = this.dragData;
58352         this.ddel.innerHTML = this.grid.getDragDropText();
58353         this.proxy.update(this.ddel);
58354         // fire start drag?
58355     },
58356
58357     afterRepair : function(){
58358         this.dragging = false;
58359     },
58360
58361     getRepairXY : function(e, data){
58362         return false;
58363     },
58364
58365     onEndDrag : function(data, e){
58366         // fire end drag?
58367     },
58368
58369     onValidDrop : function(dd, e, id){
58370         // fire drag drop?
58371         this.hideProxy();
58372     },
58373
58374     beforeInvalidDrop : function(e, id){
58375
58376     }
58377 });/*
58378  * Based on:
58379  * Ext JS Library 1.1.1
58380  * Copyright(c) 2006-2007, Ext JS, LLC.
58381  *
58382  * Originally Released Under LGPL - original licence link has changed is not relivant.
58383  *
58384  * Fork - LGPL
58385  * <script type="text/javascript">
58386  */
58387  
58388
58389 /**
58390  * @class Roo.grid.ColumnModel
58391  * @extends Roo.util.Observable
58392  * This is the default implementation of a ColumnModel used by the Grid. It defines
58393  * the columns in the grid.
58394  * <br>Usage:<br>
58395  <pre><code>
58396  var colModel = new Roo.grid.ColumnModel([
58397         {header: "Ticker", width: 60, sortable: true, locked: true},
58398         {header: "Company Name", width: 150, sortable: true},
58399         {header: "Market Cap.", width: 100, sortable: true},
58400         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58401         {header: "Employees", width: 100, sortable: true, resizable: false}
58402  ]);
58403  </code></pre>
58404  * <p>
58405  
58406  * The config options listed for this class are options which may appear in each
58407  * individual column definition.
58408  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58409  * @constructor
58410  * @param {Object} config An Array of column config objects. See this class's
58411  * config objects for details.
58412 */
58413 Roo.grid.ColumnModel = function(config){
58414         /**
58415      * The config passed into the constructor
58416      */
58417     this.config = []; //config;
58418     this.lookup = {};
58419
58420     // if no id, create one
58421     // if the column does not have a dataIndex mapping,
58422     // map it to the order it is in the config
58423     for(var i = 0, len = config.length; i < len; i++){
58424         this.addColumn(config[i]);
58425         
58426     }
58427
58428     /**
58429      * The width of columns which have no width specified (defaults to 100)
58430      * @type Number
58431      */
58432     this.defaultWidth = 100;
58433
58434     /**
58435      * Default sortable of columns which have no sortable specified (defaults to false)
58436      * @type Boolean
58437      */
58438     this.defaultSortable = false;
58439
58440     this.addEvents({
58441         /**
58442              * @event widthchange
58443              * Fires when the width of a column changes.
58444              * @param {ColumnModel} this
58445              * @param {Number} columnIndex The column index
58446              * @param {Number} newWidth The new width
58447              */
58448             "widthchange": true,
58449         /**
58450              * @event headerchange
58451              * Fires when the text of a header changes.
58452              * @param {ColumnModel} this
58453              * @param {Number} columnIndex The column index
58454              * @param {Number} newText The new header text
58455              */
58456             "headerchange": true,
58457         /**
58458              * @event hiddenchange
58459              * Fires when a column is hidden or "unhidden".
58460              * @param {ColumnModel} this
58461              * @param {Number} columnIndex The column index
58462              * @param {Boolean} hidden true if hidden, false otherwise
58463              */
58464             "hiddenchange": true,
58465             /**
58466          * @event columnmoved
58467          * Fires when a column is moved.
58468          * @param {ColumnModel} this
58469          * @param {Number} oldIndex
58470          * @param {Number} newIndex
58471          */
58472         "columnmoved" : true,
58473         /**
58474          * @event columlockchange
58475          * Fires when a column's locked state is changed
58476          * @param {ColumnModel} this
58477          * @param {Number} colIndex
58478          * @param {Boolean} locked true if locked
58479          */
58480         "columnlockchange" : true
58481     });
58482     Roo.grid.ColumnModel.superclass.constructor.call(this);
58483 };
58484 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58485     /**
58486      * @cfg {String} header The header text to display in the Grid view.
58487      */
58488         /**
58489      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
58490      */
58491         /**
58492      * @cfg {String} smHeader Header at Bootsrap Small width
58493      */
58494         /**
58495      * @cfg {String} mdHeader Header at Bootsrap Medium width
58496      */
58497         /**
58498      * @cfg {String} lgHeader Header at Bootsrap Large width
58499      */
58500         /**
58501      * @cfg {String} xlHeader Header at Bootsrap extra Large width
58502      */
58503     /**
58504      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58505      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58506      * specified, the column's index is used as an index into the Record's data Array.
58507      */
58508     /**
58509      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58510      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58511      */
58512     /**
58513      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58514      * Defaults to the value of the {@link #defaultSortable} property.
58515      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58516      */
58517     /**
58518      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58519      */
58520     /**
58521      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58522      */
58523     /**
58524      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58525      */
58526     /**
58527      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58528      */
58529     /**
58530      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58531      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58532      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58533      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58534      */
58535        /**
58536      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58537      */
58538     /**
58539      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58540      */
58541     /**
58542      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58543      */
58544     /**
58545      * @cfg {String} cursor (Optional)
58546      */
58547     /**
58548      * @cfg {String} tooltip (Optional)
58549      */
58550     /**
58551      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
58552      */
58553     /**
58554      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
58555      */
58556     /**
58557      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
58558      */
58559     /**
58560      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
58561      */
58562         /**
58563      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
58564      */
58565     /**
58566      * Returns the id of the column at the specified index.
58567      * @param {Number} index The column index
58568      * @return {String} the id
58569      */
58570     getColumnId : function(index){
58571         return this.config[index].id;
58572     },
58573
58574     /**
58575      * Returns the column for a specified id.
58576      * @param {String} id The column id
58577      * @return {Object} the column
58578      */
58579     getColumnById : function(id){
58580         return this.lookup[id];
58581     },
58582
58583     
58584     /**
58585      * Returns the column Object for a specified dataIndex.
58586      * @param {String} dataIndex The column dataIndex
58587      * @return {Object|Boolean} the column or false if not found
58588      */
58589     getColumnByDataIndex: function(dataIndex){
58590         var index = this.findColumnIndex(dataIndex);
58591         return index > -1 ? this.config[index] : false;
58592     },
58593     
58594     /**
58595      * Returns the index for a specified column id.
58596      * @param {String} id The column id
58597      * @return {Number} the index, or -1 if not found
58598      */
58599     getIndexById : function(id){
58600         for(var i = 0, len = this.config.length; i < len; i++){
58601             if(this.config[i].id == id){
58602                 return i;
58603             }
58604         }
58605         return -1;
58606     },
58607     
58608     /**
58609      * Returns the index for a specified column dataIndex.
58610      * @param {String} dataIndex The column dataIndex
58611      * @return {Number} the index, or -1 if not found
58612      */
58613     
58614     findColumnIndex : function(dataIndex){
58615         for(var i = 0, len = this.config.length; i < len; i++){
58616             if(this.config[i].dataIndex == dataIndex){
58617                 return i;
58618             }
58619         }
58620         return -1;
58621     },
58622     
58623     
58624     moveColumn : function(oldIndex, newIndex){
58625         var c = this.config[oldIndex];
58626         this.config.splice(oldIndex, 1);
58627         this.config.splice(newIndex, 0, c);
58628         this.dataMap = null;
58629         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58630     },
58631
58632     isLocked : function(colIndex){
58633         return this.config[colIndex].locked === true;
58634     },
58635
58636     setLocked : function(colIndex, value, suppressEvent){
58637         if(this.isLocked(colIndex) == value){
58638             return;
58639         }
58640         this.config[colIndex].locked = value;
58641         if(!suppressEvent){
58642             this.fireEvent("columnlockchange", this, colIndex, value);
58643         }
58644     },
58645
58646     getTotalLockedWidth : function(){
58647         var totalWidth = 0;
58648         for(var i = 0; i < this.config.length; i++){
58649             if(this.isLocked(i) && !this.isHidden(i)){
58650                 this.totalWidth += this.getColumnWidth(i);
58651             }
58652         }
58653         return totalWidth;
58654     },
58655
58656     getLockedCount : function(){
58657         for(var i = 0, len = this.config.length; i < len; i++){
58658             if(!this.isLocked(i)){
58659                 return i;
58660             }
58661         }
58662         
58663         return this.config.length;
58664     },
58665
58666     /**
58667      * Returns the number of columns.
58668      * @return {Number}
58669      */
58670     getColumnCount : function(visibleOnly){
58671         if(visibleOnly === true){
58672             var c = 0;
58673             for(var i = 0, len = this.config.length; i < len; i++){
58674                 if(!this.isHidden(i)){
58675                     c++;
58676                 }
58677             }
58678             return c;
58679         }
58680         return this.config.length;
58681     },
58682
58683     /**
58684      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58685      * @param {Function} fn
58686      * @param {Object} scope (optional)
58687      * @return {Array} result
58688      */
58689     getColumnsBy : function(fn, scope){
58690         var r = [];
58691         for(var i = 0, len = this.config.length; i < len; i++){
58692             var c = this.config[i];
58693             if(fn.call(scope||this, c, i) === true){
58694                 r[r.length] = c;
58695             }
58696         }
58697         return r;
58698     },
58699
58700     /**
58701      * Returns true if the specified column is sortable.
58702      * @param {Number} col The column index
58703      * @return {Boolean}
58704      */
58705     isSortable : function(col){
58706         if(typeof this.config[col].sortable == "undefined"){
58707             return this.defaultSortable;
58708         }
58709         return this.config[col].sortable;
58710     },
58711
58712     /**
58713      * Returns the rendering (formatting) function defined for the column.
58714      * @param {Number} col The column index.
58715      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58716      */
58717     getRenderer : function(col){
58718         if(!this.config[col].renderer){
58719             return Roo.grid.ColumnModel.defaultRenderer;
58720         }
58721         return this.config[col].renderer;
58722     },
58723
58724     /**
58725      * Sets the rendering (formatting) function for a column.
58726      * @param {Number} col The column index
58727      * @param {Function} fn The function to use to process the cell's raw data
58728      * to return HTML markup for the grid view. The render function is called with
58729      * the following parameters:<ul>
58730      * <li>Data value.</li>
58731      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58732      * <li>css A CSS style string to apply to the table cell.</li>
58733      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58734      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58735      * <li>Row index</li>
58736      * <li>Column index</li>
58737      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58738      */
58739     setRenderer : function(col, fn){
58740         this.config[col].renderer = fn;
58741     },
58742
58743     /**
58744      * Returns the width for the specified column.
58745      * @param {Number} col The column index
58746      * @param (optional) {String} gridSize bootstrap width size.
58747      * @return {Number}
58748      */
58749     getColumnWidth : function(col, gridSize)
58750         {
58751                 var cfg = this.config[col];
58752                 
58753                 if (typeof(gridSize) == 'undefined') {
58754                         return cfg.width * 1 || this.defaultWidth;
58755                 }
58756                 if (gridSize === false) { // if we set it..
58757                         return cfg.width || false;
58758                 }
58759                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
58760                 
58761                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
58762                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
58763                                 continue;
58764                         }
58765                         return cfg[ sizes[i] ];
58766                 }
58767                 return 1;
58768                 
58769     },
58770
58771     /**
58772      * Sets the width for a column.
58773      * @param {Number} col The column index
58774      * @param {Number} width The new width
58775      */
58776     setColumnWidth : function(col, width, suppressEvent){
58777         this.config[col].width = width;
58778         this.totalWidth = null;
58779         if(!suppressEvent){
58780              this.fireEvent("widthchange", this, col, width);
58781         }
58782     },
58783
58784     /**
58785      * Returns the total width of all columns.
58786      * @param {Boolean} includeHidden True to include hidden column widths
58787      * @return {Number}
58788      */
58789     getTotalWidth : function(includeHidden){
58790         if(!this.totalWidth){
58791             this.totalWidth = 0;
58792             for(var i = 0, len = this.config.length; i < len; i++){
58793                 if(includeHidden || !this.isHidden(i)){
58794                     this.totalWidth += this.getColumnWidth(i);
58795                 }
58796             }
58797         }
58798         return this.totalWidth;
58799     },
58800
58801     /**
58802      * Returns the header for the specified column.
58803      * @param {Number} col The column index
58804      * @return {String}
58805      */
58806     getColumnHeader : function(col){
58807         return this.config[col].header;
58808     },
58809
58810     /**
58811      * Sets the header for a column.
58812      * @param {Number} col The column index
58813      * @param {String} header The new header
58814      */
58815     setColumnHeader : function(col, header){
58816         this.config[col].header = header;
58817         this.fireEvent("headerchange", this, col, header);
58818     },
58819
58820     /**
58821      * Returns the tooltip for the specified column.
58822      * @param {Number} col The column index
58823      * @return {String}
58824      */
58825     getColumnTooltip : function(col){
58826             return this.config[col].tooltip;
58827     },
58828     /**
58829      * Sets the tooltip for a column.
58830      * @param {Number} col The column index
58831      * @param {String} tooltip The new tooltip
58832      */
58833     setColumnTooltip : function(col, tooltip){
58834             this.config[col].tooltip = tooltip;
58835     },
58836
58837     /**
58838      * Returns the dataIndex for the specified column.
58839      * @param {Number} col The column index
58840      * @return {Number}
58841      */
58842     getDataIndex : function(col){
58843         return this.config[col].dataIndex;
58844     },
58845
58846     /**
58847      * Sets the dataIndex for a column.
58848      * @param {Number} col The column index
58849      * @param {Number} dataIndex The new dataIndex
58850      */
58851     setDataIndex : function(col, dataIndex){
58852         this.config[col].dataIndex = dataIndex;
58853     },
58854
58855     
58856     
58857     /**
58858      * Returns true if the cell is editable.
58859      * @param {Number} colIndex The column index
58860      * @param {Number} rowIndex The row index - this is nto actually used..?
58861      * @return {Boolean}
58862      */
58863     isCellEditable : function(colIndex, rowIndex){
58864         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58865     },
58866
58867     /**
58868      * Returns the editor defined for the cell/column.
58869      * return false or null to disable editing.
58870      * @param {Number} colIndex The column index
58871      * @param {Number} rowIndex The row index
58872      * @return {Object}
58873      */
58874     getCellEditor : function(colIndex, rowIndex){
58875         return this.config[colIndex].editor;
58876     },
58877
58878     /**
58879      * Sets if a column is editable.
58880      * @param {Number} col The column index
58881      * @param {Boolean} editable True if the column is editable
58882      */
58883     setEditable : function(col, editable){
58884         this.config[col].editable = editable;
58885     },
58886
58887
58888     /**
58889      * Returns true if the column is hidden.
58890      * @param {Number} colIndex The column index
58891      * @return {Boolean}
58892      */
58893     isHidden : function(colIndex){
58894         return this.config[colIndex].hidden;
58895     },
58896
58897
58898     /**
58899      * Returns true if the column width cannot be changed
58900      */
58901     isFixed : function(colIndex){
58902         return this.config[colIndex].fixed;
58903     },
58904
58905     /**
58906      * Returns true if the column can be resized
58907      * @return {Boolean}
58908      */
58909     isResizable : function(colIndex){
58910         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58911     },
58912     /**
58913      * Sets if a column is hidden.
58914      * @param {Number} colIndex The column index
58915      * @param {Boolean} hidden True if the column is hidden
58916      */
58917     setHidden : function(colIndex, hidden){
58918         this.config[colIndex].hidden = hidden;
58919         this.totalWidth = null;
58920         this.fireEvent("hiddenchange", this, colIndex, hidden);
58921     },
58922
58923     /**
58924      * Sets the editor for a column.
58925      * @param {Number} col The column index
58926      * @param {Object} editor The editor object
58927      */
58928     setEditor : function(col, editor){
58929         this.config[col].editor = editor;
58930     },
58931     /**
58932      * Add a column (experimental...) - defaults to adding to the end..
58933      * @param {Object} config 
58934     */
58935     addColumn : function(c)
58936     {
58937     
58938         var i = this.config.length;
58939         this.config[i] = c;
58940         
58941         if(typeof c.dataIndex == "undefined"){
58942             c.dataIndex = i;
58943         }
58944         if(typeof c.renderer == "string"){
58945             c.renderer = Roo.util.Format[c.renderer];
58946         }
58947         if(typeof c.id == "undefined"){
58948             c.id = Roo.id();
58949         }
58950         if(c.editor && c.editor.xtype){
58951             c.editor  = Roo.factory(c.editor, Roo.grid);
58952         }
58953         if(c.editor && c.editor.isFormField){
58954             c.editor = new Roo.grid.GridEditor(c.editor);
58955         }
58956         this.lookup[c.id] = c;
58957     }
58958     
58959 });
58960
58961 Roo.grid.ColumnModel.defaultRenderer = function(value)
58962 {
58963     if(typeof value == "object") {
58964         return value;
58965     }
58966         if(typeof value == "string" && value.length < 1){
58967             return "&#160;";
58968         }
58969     
58970         return String.format("{0}", value);
58971 };
58972
58973 // Alias for backwards compatibility
58974 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58975 /*
58976  * Based on:
58977  * Ext JS Library 1.1.1
58978  * Copyright(c) 2006-2007, Ext JS, LLC.
58979  *
58980  * Originally Released Under LGPL - original licence link has changed is not relivant.
58981  *
58982  * Fork - LGPL
58983  * <script type="text/javascript">
58984  */
58985
58986 /**
58987  * @class Roo.grid.AbstractSelectionModel
58988  * @extends Roo.util.Observable
58989  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58990  * implemented by descendant classes.  This class should not be directly instantiated.
58991  * @constructor
58992  */
58993 Roo.grid.AbstractSelectionModel = function(){
58994     this.locked = false;
58995     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58996 };
58997
58998 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58999     /** @ignore Called by the grid automatically. Do not call directly. */
59000     init : function(grid){
59001         this.grid = grid;
59002         this.initEvents();
59003     },
59004
59005     /**
59006      * Locks the selections.
59007      */
59008     lock : function(){
59009         this.locked = true;
59010     },
59011
59012     /**
59013      * Unlocks the selections.
59014      */
59015     unlock : function(){
59016         this.locked = false;
59017     },
59018
59019     /**
59020      * Returns true if the selections are locked.
59021      * @return {Boolean}
59022      */
59023     isLocked : function(){
59024         return this.locked;
59025     }
59026 });/*
59027  * Based on:
59028  * Ext JS Library 1.1.1
59029  * Copyright(c) 2006-2007, Ext JS, LLC.
59030  *
59031  * Originally Released Under LGPL - original licence link has changed is not relivant.
59032  *
59033  * Fork - LGPL
59034  * <script type="text/javascript">
59035  */
59036 /**
59037  * @extends Roo.grid.AbstractSelectionModel
59038  * @class Roo.grid.RowSelectionModel
59039  * The default SelectionModel used by {@link Roo.grid.Grid}.
59040  * It supports multiple selections and keyboard selection/navigation. 
59041  * @constructor
59042  * @param {Object} config
59043  */
59044 Roo.grid.RowSelectionModel = function(config){
59045     Roo.apply(this, config);
59046     this.selections = new Roo.util.MixedCollection(false, function(o){
59047         return o.id;
59048     });
59049
59050     this.last = false;
59051     this.lastActive = false;
59052
59053     this.addEvents({
59054         /**
59055         * @event selectionchange
59056         * Fires when the selection changes
59057         * @param {SelectionModel} this
59058         */
59059        "selectionchange" : true,
59060        /**
59061         * @event afterselectionchange
59062         * Fires after the selection changes (eg. by key press or clicking)
59063         * @param {SelectionModel} this
59064         */
59065        "afterselectionchange" : true,
59066        /**
59067         * @event beforerowselect
59068         * Fires when a row is selected being selected, return false to cancel.
59069         * @param {SelectionModel} this
59070         * @param {Number} rowIndex The selected index
59071         * @param {Boolean} keepExisting False if other selections will be cleared
59072         */
59073        "beforerowselect" : true,
59074        /**
59075         * @event rowselect
59076         * Fires when a row is selected.
59077         * @param {SelectionModel} this
59078         * @param {Number} rowIndex The selected index
59079         * @param {Roo.data.Record} r The record
59080         */
59081        "rowselect" : true,
59082        /**
59083         * @event rowdeselect
59084         * Fires when a row is deselected.
59085         * @param {SelectionModel} this
59086         * @param {Number} rowIndex The selected index
59087         */
59088         "rowdeselect" : true
59089     });
59090     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59091     this.locked = false;
59092 };
59093
59094 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59095     /**
59096      * @cfg {Boolean} singleSelect
59097      * True to allow selection of only one row at a time (defaults to false)
59098      */
59099     singleSelect : false,
59100
59101     // private
59102     initEvents : function(){
59103
59104         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59105             this.grid.on("mousedown", this.handleMouseDown, this);
59106         }else{ // allow click to work like normal
59107             this.grid.on("rowclick", this.handleDragableRowClick, this);
59108         }
59109         // bootstrap does not have a view..
59110         var view = this.grid.view ? this.grid.view : this.grid;
59111         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59112             "up" : function(e){
59113                 if(!e.shiftKey){
59114                     this.selectPrevious(e.shiftKey);
59115                 }else if(this.last !== false && this.lastActive !== false){
59116                     var last = this.last;
59117                     this.selectRange(this.last,  this.lastActive-1);
59118                     view.focusRow(this.lastActive);
59119                     if(last !== false){
59120                         this.last = last;
59121                     }
59122                 }else{
59123                     this.selectFirstRow();
59124                 }
59125                 this.fireEvent("afterselectionchange", this);
59126             },
59127             "down" : function(e){
59128                 if(!e.shiftKey){
59129                     this.selectNext(e.shiftKey);
59130                 }else if(this.last !== false && this.lastActive !== false){
59131                     var last = this.last;
59132                     this.selectRange(this.last,  this.lastActive+1);
59133                     view.focusRow(this.lastActive);
59134                     if(last !== false){
59135                         this.last = last;
59136                     }
59137                 }else{
59138                     this.selectFirstRow();
59139                 }
59140                 this.fireEvent("afterselectionchange", this);
59141             },
59142             scope: this
59143         });
59144
59145          
59146         view.on("refresh", this.onRefresh, this);
59147         view.on("rowupdated", this.onRowUpdated, this);
59148         view.on("rowremoved", this.onRemove, this);
59149     },
59150
59151     // private
59152     onRefresh : function(){
59153         var ds = this.grid.ds, i, v = this.grid.view;
59154         var s = this.selections;
59155         s.each(function(r){
59156             if((i = ds.indexOfId(r.id)) != -1){
59157                 v.onRowSelect(i);
59158                 s.add(ds.getAt(i)); // updating the selection relate data
59159             }else{
59160                 s.remove(r);
59161             }
59162         });
59163     },
59164
59165     // private
59166     onRemove : function(v, index, r){
59167         this.selections.remove(r);
59168     },
59169
59170     // private
59171     onRowUpdated : function(v, index, r){
59172         if(this.isSelected(r)){
59173             v.onRowSelect(index);
59174         }
59175     },
59176
59177     /**
59178      * Select records.
59179      * @param {Array} records The records to select
59180      * @param {Boolean} keepExisting (optional) True to keep existing selections
59181      */
59182     selectRecords : function(records, keepExisting){
59183         if(!keepExisting){
59184             this.clearSelections();
59185         }
59186         var ds = this.grid.ds;
59187         for(var i = 0, len = records.length; i < len; i++){
59188             this.selectRow(ds.indexOf(records[i]), true);
59189         }
59190     },
59191
59192     /**
59193      * Gets the number of selected rows.
59194      * @return {Number}
59195      */
59196     getCount : function(){
59197         return this.selections.length;
59198     },
59199
59200     /**
59201      * Selects the first row in the grid.
59202      */
59203     selectFirstRow : function(){
59204         this.selectRow(0);
59205     },
59206
59207     /**
59208      * Select the last row.
59209      * @param {Boolean} keepExisting (optional) True to keep existing selections
59210      */
59211     selectLastRow : function(keepExisting){
59212         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59213     },
59214
59215     /**
59216      * Selects the row immediately following the last selected row.
59217      * @param {Boolean} keepExisting (optional) True to keep existing selections
59218      */
59219     selectNext : function(keepExisting){
59220         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59221             this.selectRow(this.last+1, keepExisting);
59222             var view = this.grid.view ? this.grid.view : this.grid;
59223             view.focusRow(this.last);
59224         }
59225     },
59226
59227     /**
59228      * Selects the row that precedes the last selected row.
59229      * @param {Boolean} keepExisting (optional) True to keep existing selections
59230      */
59231     selectPrevious : function(keepExisting){
59232         if(this.last){
59233             this.selectRow(this.last-1, keepExisting);
59234             var view = this.grid.view ? this.grid.view : this.grid;
59235             view.focusRow(this.last);
59236         }
59237     },
59238
59239     /**
59240      * Returns the selected records
59241      * @return {Array} Array of selected records
59242      */
59243     getSelections : function(){
59244         return [].concat(this.selections.items);
59245     },
59246
59247     /**
59248      * Returns the first selected record.
59249      * @return {Record}
59250      */
59251     getSelected : function(){
59252         return this.selections.itemAt(0);
59253     },
59254
59255
59256     /**
59257      * Clears all selections.
59258      */
59259     clearSelections : function(fast){
59260         if(this.locked) {
59261             return;
59262         }
59263         if(fast !== true){
59264             var ds = this.grid.ds;
59265             var s = this.selections;
59266             s.each(function(r){
59267                 this.deselectRow(ds.indexOfId(r.id));
59268             }, this);
59269             s.clear();
59270         }else{
59271             this.selections.clear();
59272         }
59273         this.last = false;
59274     },
59275
59276
59277     /**
59278      * Selects all rows.
59279      */
59280     selectAll : function(){
59281         if(this.locked) {
59282             return;
59283         }
59284         this.selections.clear();
59285         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59286             this.selectRow(i, true);
59287         }
59288     },
59289
59290     /**
59291      * Returns True if there is a selection.
59292      * @return {Boolean}
59293      */
59294     hasSelection : function(){
59295         return this.selections.length > 0;
59296     },
59297
59298     /**
59299      * Returns True if the specified row is selected.
59300      * @param {Number/Record} record The record or index of the record to check
59301      * @return {Boolean}
59302      */
59303     isSelected : function(index){
59304         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59305         return (r && this.selections.key(r.id) ? true : false);
59306     },
59307
59308     /**
59309      * Returns True if the specified record id is selected.
59310      * @param {String} id The id of record to check
59311      * @return {Boolean}
59312      */
59313     isIdSelected : function(id){
59314         return (this.selections.key(id) ? true : false);
59315     },
59316
59317     // private
59318     handleMouseDown : function(e, t)
59319     {
59320         var view = this.grid.view ? this.grid.view : this.grid;
59321         var rowIndex;
59322         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59323             return;
59324         };
59325         if(e.shiftKey && this.last !== false){
59326             var last = this.last;
59327             this.selectRange(last, rowIndex, e.ctrlKey);
59328             this.last = last; // reset the last
59329             view.focusRow(rowIndex);
59330         }else{
59331             var isSelected = this.isSelected(rowIndex);
59332             if(e.button !== 0 && isSelected){
59333                 view.focusRow(rowIndex);
59334             }else if(e.ctrlKey && isSelected){
59335                 this.deselectRow(rowIndex);
59336             }else if(!isSelected){
59337                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59338                 view.focusRow(rowIndex);
59339             }
59340         }
59341         this.fireEvent("afterselectionchange", this);
59342     },
59343     // private
59344     handleDragableRowClick :  function(grid, rowIndex, e) 
59345     {
59346         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59347             this.selectRow(rowIndex, false);
59348             var view = this.grid.view ? this.grid.view : this.grid;
59349             view.focusRow(rowIndex);
59350              this.fireEvent("afterselectionchange", this);
59351         }
59352     },
59353     
59354     /**
59355      * Selects multiple rows.
59356      * @param {Array} rows Array of the indexes of the row to select
59357      * @param {Boolean} keepExisting (optional) True to keep existing selections
59358      */
59359     selectRows : function(rows, keepExisting){
59360         if(!keepExisting){
59361             this.clearSelections();
59362         }
59363         for(var i = 0, len = rows.length; i < len; i++){
59364             this.selectRow(rows[i], true);
59365         }
59366     },
59367
59368     /**
59369      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59370      * @param {Number} startRow The index of the first row in the range
59371      * @param {Number} endRow The index of the last row in the range
59372      * @param {Boolean} keepExisting (optional) True to retain existing selections
59373      */
59374     selectRange : function(startRow, endRow, keepExisting){
59375         if(this.locked) {
59376             return;
59377         }
59378         if(!keepExisting){
59379             this.clearSelections();
59380         }
59381         if(startRow <= endRow){
59382             for(var i = startRow; i <= endRow; i++){
59383                 this.selectRow(i, true);
59384             }
59385         }else{
59386             for(var i = startRow; i >= endRow; i--){
59387                 this.selectRow(i, true);
59388             }
59389         }
59390     },
59391
59392     /**
59393      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59394      * @param {Number} startRow The index of the first row in the range
59395      * @param {Number} endRow The index of the last row in the range
59396      */
59397     deselectRange : function(startRow, endRow, preventViewNotify){
59398         if(this.locked) {
59399             return;
59400         }
59401         for(var i = startRow; i <= endRow; i++){
59402             this.deselectRow(i, preventViewNotify);
59403         }
59404     },
59405
59406     /**
59407      * Selects a row.
59408      * @param {Number} row The index of the row to select
59409      * @param {Boolean} keepExisting (optional) True to keep existing selections
59410      */
59411     selectRow : function(index, keepExisting, preventViewNotify){
59412         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59413             return;
59414         }
59415         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59416             if(!keepExisting || this.singleSelect){
59417                 this.clearSelections();
59418             }
59419             var r = this.grid.ds.getAt(index);
59420             this.selections.add(r);
59421             this.last = this.lastActive = index;
59422             if(!preventViewNotify){
59423                 var view = this.grid.view ? this.grid.view : this.grid;
59424                 view.onRowSelect(index);
59425             }
59426             this.fireEvent("rowselect", this, index, r);
59427             this.fireEvent("selectionchange", this);
59428         }
59429     },
59430
59431     /**
59432      * Deselects a row.
59433      * @param {Number} row The index of the row to deselect
59434      */
59435     deselectRow : function(index, preventViewNotify){
59436         if(this.locked) {
59437             return;
59438         }
59439         if(this.last == index){
59440             this.last = false;
59441         }
59442         if(this.lastActive == index){
59443             this.lastActive = false;
59444         }
59445         var r = this.grid.ds.getAt(index);
59446         this.selections.remove(r);
59447         if(!preventViewNotify){
59448             var view = this.grid.view ? this.grid.view : this.grid;
59449             view.onRowDeselect(index);
59450         }
59451         this.fireEvent("rowdeselect", this, index);
59452         this.fireEvent("selectionchange", this);
59453     },
59454
59455     // private
59456     restoreLast : function(){
59457         if(this._last){
59458             this.last = this._last;
59459         }
59460     },
59461
59462     // private
59463     acceptsNav : function(row, col, cm){
59464         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59465     },
59466
59467     // private
59468     onEditorKey : function(field, e){
59469         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59470         if(k == e.TAB){
59471             e.stopEvent();
59472             ed.completeEdit();
59473             if(e.shiftKey){
59474                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59475             }else{
59476                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59477             }
59478         }else if(k == e.ENTER && !e.ctrlKey){
59479             e.stopEvent();
59480             ed.completeEdit();
59481             if(e.shiftKey){
59482                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59483             }else{
59484                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59485             }
59486         }else if(k == e.ESC){
59487             ed.cancelEdit();
59488         }
59489         if(newCell){
59490             g.startEditing(newCell[0], newCell[1]);
59491         }
59492     }
59493 });/*
59494  * Based on:
59495  * Ext JS Library 1.1.1
59496  * Copyright(c) 2006-2007, Ext JS, LLC.
59497  *
59498  * Originally Released Under LGPL - original licence link has changed is not relivant.
59499  *
59500  * Fork - LGPL
59501  * <script type="text/javascript">
59502  */
59503 /**
59504  * @class Roo.grid.CellSelectionModel
59505  * @extends Roo.grid.AbstractSelectionModel
59506  * This class provides the basic implementation for cell selection in a grid.
59507  * @constructor
59508  * @param {Object} config The object containing the configuration of this model.
59509  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59510  */
59511 Roo.grid.CellSelectionModel = function(config){
59512     Roo.apply(this, config);
59513
59514     this.selection = null;
59515
59516     this.addEvents({
59517         /**
59518              * @event beforerowselect
59519              * Fires before a cell is selected.
59520              * @param {SelectionModel} this
59521              * @param {Number} rowIndex The selected row index
59522              * @param {Number} colIndex The selected cell index
59523              */
59524             "beforecellselect" : true,
59525         /**
59526              * @event cellselect
59527              * Fires when a cell is selected.
59528              * @param {SelectionModel} this
59529              * @param {Number} rowIndex The selected row index
59530              * @param {Number} colIndex The selected cell index
59531              */
59532             "cellselect" : true,
59533         /**
59534              * @event selectionchange
59535              * Fires when the active selection changes.
59536              * @param {SelectionModel} this
59537              * @param {Object} selection null for no selection or an object (o) with two properties
59538                 <ul>
59539                 <li>o.record: the record object for the row the selection is in</li>
59540                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59541                 </ul>
59542              */
59543             "selectionchange" : true,
59544         /**
59545              * @event tabend
59546              * Fires when the tab (or enter) was pressed on the last editable cell
59547              * You can use this to trigger add new row.
59548              * @param {SelectionModel} this
59549              */
59550             "tabend" : true,
59551          /**
59552              * @event beforeeditnext
59553              * Fires before the next editable sell is made active
59554              * You can use this to skip to another cell or fire the tabend
59555              *    if you set cell to false
59556              * @param {Object} eventdata object : { cell : [ row, col ] } 
59557              */
59558             "beforeeditnext" : true
59559     });
59560     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59561 };
59562
59563 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59564     
59565     enter_is_tab: false,
59566
59567     /** @ignore */
59568     initEvents : function(){
59569         this.grid.on("mousedown", this.handleMouseDown, this);
59570         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59571         var view = this.grid.view;
59572         view.on("refresh", this.onViewChange, this);
59573         view.on("rowupdated", this.onRowUpdated, this);
59574         view.on("beforerowremoved", this.clearSelections, this);
59575         view.on("beforerowsinserted", this.clearSelections, this);
59576         if(this.grid.isEditor){
59577             this.grid.on("beforeedit", this.beforeEdit,  this);
59578         }
59579     },
59580
59581         //private
59582     beforeEdit : function(e){
59583         this.select(e.row, e.column, false, true, e.record);
59584     },
59585
59586         //private
59587     onRowUpdated : function(v, index, r){
59588         if(this.selection && this.selection.record == r){
59589             v.onCellSelect(index, this.selection.cell[1]);
59590         }
59591     },
59592
59593         //private
59594     onViewChange : function(){
59595         this.clearSelections(true);
59596     },
59597
59598         /**
59599          * Returns the currently selected cell,.
59600          * @return {Array} The selected cell (row, column) or null if none selected.
59601          */
59602     getSelectedCell : function(){
59603         return this.selection ? this.selection.cell : null;
59604     },
59605
59606     /**
59607      * Clears all selections.
59608      * @param {Boolean} true to prevent the gridview from being notified about the change.
59609      */
59610     clearSelections : function(preventNotify){
59611         var s = this.selection;
59612         if(s){
59613             if(preventNotify !== true){
59614                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59615             }
59616             this.selection = null;
59617             this.fireEvent("selectionchange", this, null);
59618         }
59619     },
59620
59621     /**
59622      * Returns true if there is a selection.
59623      * @return {Boolean}
59624      */
59625     hasSelection : function(){
59626         return this.selection ? true : false;
59627     },
59628
59629     /** @ignore */
59630     handleMouseDown : function(e, t){
59631         var v = this.grid.getView();
59632         if(this.isLocked()){
59633             return;
59634         };
59635         var row = v.findRowIndex(t);
59636         var cell = v.findCellIndex(t);
59637         if(row !== false && cell !== false){
59638             this.select(row, cell);
59639         }
59640     },
59641
59642     /**
59643      * Selects a cell.
59644      * @param {Number} rowIndex
59645      * @param {Number} collIndex
59646      */
59647     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59648         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59649             this.clearSelections();
59650             r = r || this.grid.dataSource.getAt(rowIndex);
59651             this.selection = {
59652                 record : r,
59653                 cell : [rowIndex, colIndex]
59654             };
59655             if(!preventViewNotify){
59656                 var v = this.grid.getView();
59657                 v.onCellSelect(rowIndex, colIndex);
59658                 if(preventFocus !== true){
59659                     v.focusCell(rowIndex, colIndex);
59660                 }
59661             }
59662             this.fireEvent("cellselect", this, rowIndex, colIndex);
59663             this.fireEvent("selectionchange", this, this.selection);
59664         }
59665     },
59666
59667         //private
59668     isSelectable : function(rowIndex, colIndex, cm){
59669         return !cm.isHidden(colIndex);
59670     },
59671
59672     /** @ignore */
59673     handleKeyDown : function(e){
59674         //Roo.log('Cell Sel Model handleKeyDown');
59675         if(!e.isNavKeyPress()){
59676             return;
59677         }
59678         var g = this.grid, s = this.selection;
59679         if(!s){
59680             e.stopEvent();
59681             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59682             if(cell){
59683                 this.select(cell[0], cell[1]);
59684             }
59685             return;
59686         }
59687         var sm = this;
59688         var walk = function(row, col, step){
59689             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59690         };
59691         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59692         var newCell;
59693
59694       
59695
59696         switch(k){
59697             case e.TAB:
59698                 // handled by onEditorKey
59699                 if (g.isEditor && g.editing) {
59700                     return;
59701                 }
59702                 if(e.shiftKey) {
59703                     newCell = walk(r, c-1, -1);
59704                 } else {
59705                     newCell = walk(r, c+1, 1);
59706                 }
59707                 break;
59708             
59709             case e.DOWN:
59710                newCell = walk(r+1, c, 1);
59711                 break;
59712             
59713             case e.UP:
59714                 newCell = walk(r-1, c, -1);
59715                 break;
59716             
59717             case e.RIGHT:
59718                 newCell = walk(r, c+1, 1);
59719                 break;
59720             
59721             case e.LEFT:
59722                 newCell = walk(r, c-1, -1);
59723                 break;
59724             
59725             case e.ENTER:
59726                 
59727                 if(g.isEditor && !g.editing){
59728                    g.startEditing(r, c);
59729                    e.stopEvent();
59730                    return;
59731                 }
59732                 
59733                 
59734              break;
59735         };
59736         if(newCell){
59737             this.select(newCell[0], newCell[1]);
59738             e.stopEvent();
59739             
59740         }
59741     },
59742
59743     acceptsNav : function(row, col, cm){
59744         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59745     },
59746     /**
59747      * Selects a cell.
59748      * @param {Number} field (not used) - as it's normally used as a listener
59749      * @param {Number} e - event - fake it by using
59750      *
59751      * var e = Roo.EventObjectImpl.prototype;
59752      * e.keyCode = e.TAB
59753      *
59754      * 
59755      */
59756     onEditorKey : function(field, e){
59757         
59758         var k = e.getKey(),
59759             newCell,
59760             g = this.grid,
59761             ed = g.activeEditor,
59762             forward = false;
59763         ///Roo.log('onEditorKey' + k);
59764         
59765         
59766         if (this.enter_is_tab && k == e.ENTER) {
59767             k = e.TAB;
59768         }
59769         
59770         if(k == e.TAB){
59771             if(e.shiftKey){
59772                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59773             }else{
59774                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59775                 forward = true;
59776             }
59777             
59778             e.stopEvent();
59779             
59780         } else if(k == e.ENTER &&  !e.ctrlKey){
59781             ed.completeEdit();
59782             e.stopEvent();
59783             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59784         
59785                 } else if(k == e.ESC){
59786             ed.cancelEdit();
59787         }
59788                 
59789         if (newCell) {
59790             var ecall = { cell : newCell, forward : forward };
59791             this.fireEvent('beforeeditnext', ecall );
59792             newCell = ecall.cell;
59793                         forward = ecall.forward;
59794         }
59795                 
59796         if(newCell){
59797             //Roo.log('next cell after edit');
59798             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59799         } else if (forward) {
59800             // tabbed past last
59801             this.fireEvent.defer(100, this, ['tabend',this]);
59802         }
59803     }
59804 });/*
59805  * Based on:
59806  * Ext JS Library 1.1.1
59807  * Copyright(c) 2006-2007, Ext JS, LLC.
59808  *
59809  * Originally Released Under LGPL - original licence link has changed is not relivant.
59810  *
59811  * Fork - LGPL
59812  * <script type="text/javascript">
59813  */
59814  
59815 /**
59816  * @class Roo.grid.EditorGrid
59817  * @extends Roo.grid.Grid
59818  * Class for creating and editable grid.
59819  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59820  * The container MUST have some type of size defined for the grid to fill. The container will be 
59821  * automatically set to position relative if it isn't already.
59822  * @param {Object} dataSource The data model to bind to
59823  * @param {Object} colModel The column model with info about this grid's columns
59824  */
59825 Roo.grid.EditorGrid = function(container, config){
59826     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59827     this.getGridEl().addClass("xedit-grid");
59828
59829     if(!this.selModel){
59830         this.selModel = new Roo.grid.CellSelectionModel();
59831     }
59832
59833     this.activeEditor = null;
59834
59835         this.addEvents({
59836             /**
59837              * @event beforeedit
59838              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59839              * <ul style="padding:5px;padding-left:16px;">
59840              * <li>grid - This grid</li>
59841              * <li>record - The record being edited</li>
59842              * <li>field - The field name being edited</li>
59843              * <li>value - The value for the field being edited.</li>
59844              * <li>row - The grid row index</li>
59845              * <li>column - The grid column index</li>
59846              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59847              * </ul>
59848              * @param {Object} e An edit event (see above for description)
59849              */
59850             "beforeedit" : true,
59851             /**
59852              * @event afteredit
59853              * Fires after a cell is edited. <br />
59854              * <ul style="padding:5px;padding-left:16px;">
59855              * <li>grid - This grid</li>
59856              * <li>record - The record being edited</li>
59857              * <li>field - The field name being edited</li>
59858              * <li>value - The value being set</li>
59859              * <li>originalValue - The original value for the field, before the edit.</li>
59860              * <li>row - The grid row index</li>
59861              * <li>column - The grid column index</li>
59862              * </ul>
59863              * @param {Object} e An edit event (see above for description)
59864              */
59865             "afteredit" : true,
59866             /**
59867              * @event validateedit
59868              * Fires after a cell is edited, but before the value is set in the record. 
59869          * You can use this to modify the value being set in the field, Return false
59870              * to cancel the change. The edit event object has the following properties <br />
59871              * <ul style="padding:5px;padding-left:16px;">
59872          * <li>editor - This editor</li>
59873              * <li>grid - This grid</li>
59874              * <li>record - The record being edited</li>
59875              * <li>field - The field name being edited</li>
59876              * <li>value - The value being set</li>
59877              * <li>originalValue - The original value for the field, before the edit.</li>
59878              * <li>row - The grid row index</li>
59879              * <li>column - The grid column index</li>
59880              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59881              * </ul>
59882              * @param {Object} e An edit event (see above for description)
59883              */
59884             "validateedit" : true
59885         });
59886     this.on("bodyscroll", this.stopEditing,  this);
59887     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59888 };
59889
59890 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59891     /**
59892      * @cfg {Number} clicksToEdit
59893      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59894      */
59895     clicksToEdit: 2,
59896
59897     // private
59898     isEditor : true,
59899     // private
59900     trackMouseOver: false, // causes very odd FF errors
59901
59902     onCellDblClick : function(g, row, col){
59903         this.startEditing(row, col);
59904     },
59905
59906     onEditComplete : function(ed, value, startValue){
59907         this.editing = false;
59908         this.activeEditor = null;
59909         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59910         var r = ed.record;
59911         var field = this.colModel.getDataIndex(ed.col);
59912         var e = {
59913             grid: this,
59914             record: r,
59915             field: field,
59916             originalValue: startValue,
59917             value: value,
59918             row: ed.row,
59919             column: ed.col,
59920             cancel:false,
59921             editor: ed
59922         };
59923         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59924         cell.show();
59925           
59926         if(String(value) !== String(startValue)){
59927             
59928             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59929                 r.set(field, e.value);
59930                 // if we are dealing with a combo box..
59931                 // then we also set the 'name' colum to be the displayField
59932                 if (ed.field.displayField && ed.field.name) {
59933                     r.set(ed.field.name, ed.field.el.dom.value);
59934                 }
59935                 
59936                 delete e.cancel; //?? why!!!
59937                 this.fireEvent("afteredit", e);
59938             }
59939         } else {
59940             this.fireEvent("afteredit", e); // always fire it!
59941         }
59942         this.view.focusCell(ed.row, ed.col);
59943     },
59944
59945     /**
59946      * Starts editing the specified for the specified row/column
59947      * @param {Number} rowIndex
59948      * @param {Number} colIndex
59949      */
59950     startEditing : function(row, col){
59951         this.stopEditing();
59952         if(this.colModel.isCellEditable(col, row)){
59953             this.view.ensureVisible(row, col, true);
59954           
59955             var r = this.dataSource.getAt(row);
59956             var field = this.colModel.getDataIndex(col);
59957             var cell = Roo.get(this.view.getCell(row,col));
59958             var e = {
59959                 grid: this,
59960                 record: r,
59961                 field: field,
59962                 value: r.data[field],
59963                 row: row,
59964                 column: col,
59965                 cancel:false 
59966             };
59967             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59968                 this.editing = true;
59969                 var ed = this.colModel.getCellEditor(col, row);
59970                 
59971                 if (!ed) {
59972                     return;
59973                 }
59974                 if(!ed.rendered){
59975                     ed.render(ed.parentEl || document.body);
59976                 }
59977                 ed.field.reset();
59978                
59979                 cell.hide();
59980                 
59981                 (function(){ // complex but required for focus issues in safari, ie and opera
59982                     ed.row = row;
59983                     ed.col = col;
59984                     ed.record = r;
59985                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59986                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59987                     this.activeEditor = ed;
59988                     var v = r.data[field];
59989                     ed.startEdit(this.view.getCell(row, col), v);
59990                     // combo's with 'displayField and name set
59991                     if (ed.field.displayField && ed.field.name) {
59992                         ed.field.el.dom.value = r.data[ed.field.name];
59993                     }
59994                     
59995                     
59996                 }).defer(50, this);
59997             }
59998         }
59999     },
60000         
60001     /**
60002      * Stops any active editing
60003      */
60004     stopEditing : function(){
60005         if(this.activeEditor){
60006             this.activeEditor.completeEdit();
60007         }
60008         this.activeEditor = null;
60009     },
60010         
60011          /**
60012      * Called to get grid's drag proxy text, by default returns this.ddText.
60013      * @return {String}
60014      */
60015     getDragDropText : function(){
60016         var count = this.selModel.getSelectedCell() ? 1 : 0;
60017         return String.format(this.ddText, count, count == 1 ? '' : 's');
60018     }
60019         
60020 });/*
60021  * Based on:
60022  * Ext JS Library 1.1.1
60023  * Copyright(c) 2006-2007, Ext JS, LLC.
60024  *
60025  * Originally Released Under LGPL - original licence link has changed is not relivant.
60026  *
60027  * Fork - LGPL
60028  * <script type="text/javascript">
60029  */
60030
60031 // private - not really -- you end up using it !
60032 // This is a support class used internally by the Grid components
60033
60034 /**
60035  * @class Roo.grid.GridEditor
60036  * @extends Roo.Editor
60037  * Class for creating and editable grid elements.
60038  * @param {Object} config any settings (must include field)
60039  */
60040 Roo.grid.GridEditor = function(field, config){
60041     if (!config && field.field) {
60042         config = field;
60043         field = Roo.factory(config.field, Roo.form);
60044     }
60045     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60046     field.monitorTab = false;
60047 };
60048
60049 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60050     
60051     /**
60052      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60053      */
60054     
60055     alignment: "tl-tl",
60056     autoSize: "width",
60057     hideEl : false,
60058     cls: "x-small-editor x-grid-editor",
60059     shim:false,
60060     shadow:"frame"
60061 });/*
60062  * Based on:
60063  * Ext JS Library 1.1.1
60064  * Copyright(c) 2006-2007, Ext JS, LLC.
60065  *
60066  * Originally Released Under LGPL - original licence link has changed is not relivant.
60067  *
60068  * Fork - LGPL
60069  * <script type="text/javascript">
60070  */
60071   
60072
60073   
60074 Roo.grid.PropertyRecord = Roo.data.Record.create([
60075     {name:'name',type:'string'},  'value'
60076 ]);
60077
60078
60079 Roo.grid.PropertyStore = function(grid, source){
60080     this.grid = grid;
60081     this.store = new Roo.data.Store({
60082         recordType : Roo.grid.PropertyRecord
60083     });
60084     this.store.on('update', this.onUpdate,  this);
60085     if(source){
60086         this.setSource(source);
60087     }
60088     Roo.grid.PropertyStore.superclass.constructor.call(this);
60089 };
60090
60091
60092
60093 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60094     setSource : function(o){
60095         this.source = o;
60096         this.store.removeAll();
60097         var data = [];
60098         for(var k in o){
60099             if(this.isEditableValue(o[k])){
60100                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60101             }
60102         }
60103         this.store.loadRecords({records: data}, {}, true);
60104     },
60105
60106     onUpdate : function(ds, record, type){
60107         if(type == Roo.data.Record.EDIT){
60108             var v = record.data['value'];
60109             var oldValue = record.modified['value'];
60110             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60111                 this.source[record.id] = v;
60112                 record.commit();
60113                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60114             }else{
60115                 record.reject();
60116             }
60117         }
60118     },
60119
60120     getProperty : function(row){
60121        return this.store.getAt(row);
60122     },
60123
60124     isEditableValue: function(val){
60125         if(val && val instanceof Date){
60126             return true;
60127         }else if(typeof val == 'object' || typeof val == 'function'){
60128             return false;
60129         }
60130         return true;
60131     },
60132
60133     setValue : function(prop, value){
60134         this.source[prop] = value;
60135         this.store.getById(prop).set('value', value);
60136     },
60137
60138     getSource : function(){
60139         return this.source;
60140     }
60141 });
60142
60143 Roo.grid.PropertyColumnModel = function(grid, store){
60144     this.grid = grid;
60145     var g = Roo.grid;
60146     g.PropertyColumnModel.superclass.constructor.call(this, [
60147         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60148         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60149     ]);
60150     this.store = store;
60151     this.bselect = Roo.DomHelper.append(document.body, {
60152         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60153             {tag: 'option', value: 'true', html: 'true'},
60154             {tag: 'option', value: 'false', html: 'false'}
60155         ]
60156     });
60157     Roo.id(this.bselect);
60158     var f = Roo.form;
60159     this.editors = {
60160         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60161         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60162         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60163         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60164         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60165     };
60166     this.renderCellDelegate = this.renderCell.createDelegate(this);
60167     this.renderPropDelegate = this.renderProp.createDelegate(this);
60168 };
60169
60170 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60171     
60172     
60173     nameText : 'Name',
60174     valueText : 'Value',
60175     
60176     dateFormat : 'm/j/Y',
60177     
60178     
60179     renderDate : function(dateVal){
60180         return dateVal.dateFormat(this.dateFormat);
60181     },
60182
60183     renderBool : function(bVal){
60184         return bVal ? 'true' : 'false';
60185     },
60186
60187     isCellEditable : function(colIndex, rowIndex){
60188         return colIndex == 1;
60189     },
60190
60191     getRenderer : function(col){
60192         return col == 1 ?
60193             this.renderCellDelegate : this.renderPropDelegate;
60194     },
60195
60196     renderProp : function(v){
60197         return this.getPropertyName(v);
60198     },
60199
60200     renderCell : function(val){
60201         var rv = val;
60202         if(val instanceof Date){
60203             rv = this.renderDate(val);
60204         }else if(typeof val == 'boolean'){
60205             rv = this.renderBool(val);
60206         }
60207         return Roo.util.Format.htmlEncode(rv);
60208     },
60209
60210     getPropertyName : function(name){
60211         var pn = this.grid.propertyNames;
60212         return pn && pn[name] ? pn[name] : name;
60213     },
60214
60215     getCellEditor : function(colIndex, rowIndex){
60216         var p = this.store.getProperty(rowIndex);
60217         var n = p.data['name'], val = p.data['value'];
60218         
60219         if(typeof(this.grid.customEditors[n]) == 'string'){
60220             return this.editors[this.grid.customEditors[n]];
60221         }
60222         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60223             return this.grid.customEditors[n];
60224         }
60225         if(val instanceof Date){
60226             return this.editors['date'];
60227         }else if(typeof val == 'number'){
60228             return this.editors['number'];
60229         }else if(typeof val == 'boolean'){
60230             return this.editors['boolean'];
60231         }else{
60232             return this.editors['string'];
60233         }
60234     }
60235 });
60236
60237 /**
60238  * @class Roo.grid.PropertyGrid
60239  * @extends Roo.grid.EditorGrid
60240  * This class represents the  interface of a component based property grid control.
60241  * <br><br>Usage:<pre><code>
60242  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60243       
60244  });
60245  // set any options
60246  grid.render();
60247  * </code></pre>
60248   
60249  * @constructor
60250  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60251  * The container MUST have some type of size defined for the grid to fill. The container will be
60252  * automatically set to position relative if it isn't already.
60253  * @param {Object} config A config object that sets properties on this grid.
60254  */
60255 Roo.grid.PropertyGrid = function(container, config){
60256     config = config || {};
60257     var store = new Roo.grid.PropertyStore(this);
60258     this.store = store;
60259     var cm = new Roo.grid.PropertyColumnModel(this, store);
60260     store.store.sort('name', 'ASC');
60261     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60262         ds: store.store,
60263         cm: cm,
60264         enableColLock:false,
60265         enableColumnMove:false,
60266         stripeRows:false,
60267         trackMouseOver: false,
60268         clicksToEdit:1
60269     }, config));
60270     this.getGridEl().addClass('x-props-grid');
60271     this.lastEditRow = null;
60272     this.on('columnresize', this.onColumnResize, this);
60273     this.addEvents({
60274          /**
60275              * @event beforepropertychange
60276              * Fires before a property changes (return false to stop?)
60277              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60278              * @param {String} id Record Id
60279              * @param {String} newval New Value
60280          * @param {String} oldval Old Value
60281              */
60282         "beforepropertychange": true,
60283         /**
60284              * @event propertychange
60285              * Fires after a property changes
60286              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60287              * @param {String} id Record Id
60288              * @param {String} newval New Value
60289          * @param {String} oldval Old Value
60290              */
60291         "propertychange": true
60292     });
60293     this.customEditors = this.customEditors || {};
60294 };
60295 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60296     
60297      /**
60298      * @cfg {Object} customEditors map of colnames=> custom editors.
60299      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60300      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60301      * false disables editing of the field.
60302          */
60303     
60304       /**
60305      * @cfg {Object} propertyNames map of property Names to their displayed value
60306          */
60307     
60308     render : function(){
60309         Roo.grid.PropertyGrid.superclass.render.call(this);
60310         this.autoSize.defer(100, this);
60311     },
60312
60313     autoSize : function(){
60314         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60315         if(this.view){
60316             this.view.fitColumns();
60317         }
60318     },
60319
60320     onColumnResize : function(){
60321         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60322         this.autoSize();
60323     },
60324     /**
60325      * Sets the data for the Grid
60326      * accepts a Key => Value object of all the elements avaiable.
60327      * @param {Object} data  to appear in grid.
60328      */
60329     setSource : function(source){
60330         this.store.setSource(source);
60331         //this.autoSize();
60332     },
60333     /**
60334      * Gets all the data from the grid.
60335      * @return {Object} data  data stored in grid
60336      */
60337     getSource : function(){
60338         return this.store.getSource();
60339     }
60340 });/*
60341   
60342  * Licence LGPL
60343  
60344  */
60345  
60346 /**
60347  * @class Roo.grid.Calendar
60348  * @extends Roo.util.Grid
60349  * This class extends the Grid to provide a calendar widget
60350  * <br><br>Usage:<pre><code>
60351  var grid = new Roo.grid.Calendar("my-container-id", {
60352      ds: myDataStore,
60353      cm: myColModel,
60354      selModel: mySelectionModel,
60355      autoSizeColumns: true,
60356      monitorWindowResize: false,
60357      trackMouseOver: true
60358      eventstore : real data store..
60359  });
60360  // set any options
60361  grid.render();
60362   
60363   * @constructor
60364  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60365  * The container MUST have some type of size defined for the grid to fill. The container will be
60366  * automatically set to position relative if it isn't already.
60367  * @param {Object} config A config object that sets properties on this grid.
60368  */
60369 Roo.grid.Calendar = function(container, config){
60370         // initialize the container
60371         this.container = Roo.get(container);
60372         this.container.update("");
60373         this.container.setStyle("overflow", "hidden");
60374     this.container.addClass('x-grid-container');
60375
60376     this.id = this.container.id;
60377
60378     Roo.apply(this, config);
60379     // check and correct shorthanded configs
60380     
60381     var rows = [];
60382     var d =1;
60383     for (var r = 0;r < 6;r++) {
60384         
60385         rows[r]=[];
60386         for (var c =0;c < 7;c++) {
60387             rows[r][c]= '';
60388         }
60389     }
60390     if (this.eventStore) {
60391         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60392         this.eventStore.on('load',this.onLoad, this);
60393         this.eventStore.on('beforeload',this.clearEvents, this);
60394          
60395     }
60396     
60397     this.dataSource = new Roo.data.Store({
60398             proxy: new Roo.data.MemoryProxy(rows),
60399             reader: new Roo.data.ArrayReader({}, [
60400                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60401     });
60402
60403     this.dataSource.load();
60404     this.ds = this.dataSource;
60405     this.ds.xmodule = this.xmodule || false;
60406     
60407     
60408     var cellRender = function(v,x,r)
60409     {
60410         return String.format(
60411             '<div class="fc-day  fc-widget-content"><div>' +
60412                 '<div class="fc-event-container"></div>' +
60413                 '<div class="fc-day-number">{0}</div>'+
60414                 
60415                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60416             '</div></div>', v);
60417     
60418     }
60419     
60420     
60421     this.colModel = new Roo.grid.ColumnModel( [
60422         {
60423             xtype: 'ColumnModel',
60424             xns: Roo.grid,
60425             dataIndex : 'weekday0',
60426             header : 'Sunday',
60427             renderer : cellRender
60428         },
60429         {
60430             xtype: 'ColumnModel',
60431             xns: Roo.grid,
60432             dataIndex : 'weekday1',
60433             header : 'Monday',
60434             renderer : cellRender
60435         },
60436         {
60437             xtype: 'ColumnModel',
60438             xns: Roo.grid,
60439             dataIndex : 'weekday2',
60440             header : 'Tuesday',
60441             renderer : cellRender
60442         },
60443         {
60444             xtype: 'ColumnModel',
60445             xns: Roo.grid,
60446             dataIndex : 'weekday3',
60447             header : 'Wednesday',
60448             renderer : cellRender
60449         },
60450         {
60451             xtype: 'ColumnModel',
60452             xns: Roo.grid,
60453             dataIndex : 'weekday4',
60454             header : 'Thursday',
60455             renderer : cellRender
60456         },
60457         {
60458             xtype: 'ColumnModel',
60459             xns: Roo.grid,
60460             dataIndex : 'weekday5',
60461             header : 'Friday',
60462             renderer : cellRender
60463         },
60464         {
60465             xtype: 'ColumnModel',
60466             xns: Roo.grid,
60467             dataIndex : 'weekday6',
60468             header : 'Saturday',
60469             renderer : cellRender
60470         }
60471     ]);
60472     this.cm = this.colModel;
60473     this.cm.xmodule = this.xmodule || false;
60474  
60475         
60476           
60477     //this.selModel = new Roo.grid.CellSelectionModel();
60478     //this.sm = this.selModel;
60479     //this.selModel.init(this);
60480     
60481     
60482     if(this.width){
60483         this.container.setWidth(this.width);
60484     }
60485
60486     if(this.height){
60487         this.container.setHeight(this.height);
60488     }
60489     /** @private */
60490         this.addEvents({
60491         // raw events
60492         /**
60493          * @event click
60494          * The raw click event for the entire grid.
60495          * @param {Roo.EventObject} e
60496          */
60497         "click" : true,
60498         /**
60499          * @event dblclick
60500          * The raw dblclick event for the entire grid.
60501          * @param {Roo.EventObject} e
60502          */
60503         "dblclick" : true,
60504         /**
60505          * @event contextmenu
60506          * The raw contextmenu event for the entire grid.
60507          * @param {Roo.EventObject} e
60508          */
60509         "contextmenu" : true,
60510         /**
60511          * @event mousedown
60512          * The raw mousedown event for the entire grid.
60513          * @param {Roo.EventObject} e
60514          */
60515         "mousedown" : true,
60516         /**
60517          * @event mouseup
60518          * The raw mouseup event for the entire grid.
60519          * @param {Roo.EventObject} e
60520          */
60521         "mouseup" : true,
60522         /**
60523          * @event mouseover
60524          * The raw mouseover event for the entire grid.
60525          * @param {Roo.EventObject} e
60526          */
60527         "mouseover" : true,
60528         /**
60529          * @event mouseout
60530          * The raw mouseout event for the entire grid.
60531          * @param {Roo.EventObject} e
60532          */
60533         "mouseout" : true,
60534         /**
60535          * @event keypress
60536          * The raw keypress event for the entire grid.
60537          * @param {Roo.EventObject} e
60538          */
60539         "keypress" : true,
60540         /**
60541          * @event keydown
60542          * The raw keydown event for the entire grid.
60543          * @param {Roo.EventObject} e
60544          */
60545         "keydown" : true,
60546
60547         // custom events
60548
60549         /**
60550          * @event cellclick
60551          * Fires when a cell is clicked
60552          * @param {Grid} this
60553          * @param {Number} rowIndex
60554          * @param {Number} columnIndex
60555          * @param {Roo.EventObject} e
60556          */
60557         "cellclick" : true,
60558         /**
60559          * @event celldblclick
60560          * Fires when a cell is double clicked
60561          * @param {Grid} this
60562          * @param {Number} rowIndex
60563          * @param {Number} columnIndex
60564          * @param {Roo.EventObject} e
60565          */
60566         "celldblclick" : true,
60567         /**
60568          * @event rowclick
60569          * Fires when a row is clicked
60570          * @param {Grid} this
60571          * @param {Number} rowIndex
60572          * @param {Roo.EventObject} e
60573          */
60574         "rowclick" : true,
60575         /**
60576          * @event rowdblclick
60577          * Fires when a row is double clicked
60578          * @param {Grid} this
60579          * @param {Number} rowIndex
60580          * @param {Roo.EventObject} e
60581          */
60582         "rowdblclick" : true,
60583         /**
60584          * @event headerclick
60585          * Fires when a header is clicked
60586          * @param {Grid} this
60587          * @param {Number} columnIndex
60588          * @param {Roo.EventObject} e
60589          */
60590         "headerclick" : true,
60591         /**
60592          * @event headerdblclick
60593          * Fires when a header cell is double clicked
60594          * @param {Grid} this
60595          * @param {Number} columnIndex
60596          * @param {Roo.EventObject} e
60597          */
60598         "headerdblclick" : true,
60599         /**
60600          * @event rowcontextmenu
60601          * Fires when a row is right clicked
60602          * @param {Grid} this
60603          * @param {Number} rowIndex
60604          * @param {Roo.EventObject} e
60605          */
60606         "rowcontextmenu" : true,
60607         /**
60608          * @event cellcontextmenu
60609          * Fires when a cell is right clicked
60610          * @param {Grid} this
60611          * @param {Number} rowIndex
60612          * @param {Number} cellIndex
60613          * @param {Roo.EventObject} e
60614          */
60615          "cellcontextmenu" : true,
60616         /**
60617          * @event headercontextmenu
60618          * Fires when a header is right clicked
60619          * @param {Grid} this
60620          * @param {Number} columnIndex
60621          * @param {Roo.EventObject} e
60622          */
60623         "headercontextmenu" : true,
60624         /**
60625          * @event bodyscroll
60626          * Fires when the body element is scrolled
60627          * @param {Number} scrollLeft
60628          * @param {Number} scrollTop
60629          */
60630         "bodyscroll" : true,
60631         /**
60632          * @event columnresize
60633          * Fires when the user resizes a column
60634          * @param {Number} columnIndex
60635          * @param {Number} newSize
60636          */
60637         "columnresize" : true,
60638         /**
60639          * @event columnmove
60640          * Fires when the user moves a column
60641          * @param {Number} oldIndex
60642          * @param {Number} newIndex
60643          */
60644         "columnmove" : true,
60645         /**
60646          * @event startdrag
60647          * Fires when row(s) start being dragged
60648          * @param {Grid} this
60649          * @param {Roo.GridDD} dd The drag drop object
60650          * @param {event} e The raw browser event
60651          */
60652         "startdrag" : true,
60653         /**
60654          * @event enddrag
60655          * Fires when a drag operation is complete
60656          * @param {Grid} this
60657          * @param {Roo.GridDD} dd The drag drop object
60658          * @param {event} e The raw browser event
60659          */
60660         "enddrag" : true,
60661         /**
60662          * @event dragdrop
60663          * Fires when dragged row(s) are dropped on a valid DD target
60664          * @param {Grid} this
60665          * @param {Roo.GridDD} dd The drag drop object
60666          * @param {String} targetId The target drag drop object
60667          * @param {event} e The raw browser event
60668          */
60669         "dragdrop" : true,
60670         /**
60671          * @event dragover
60672          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60673          * @param {Grid} this
60674          * @param {Roo.GridDD} dd The drag drop object
60675          * @param {String} targetId The target drag drop object
60676          * @param {event} e The raw browser event
60677          */
60678         "dragover" : true,
60679         /**
60680          * @event dragenter
60681          *  Fires when the dragged row(s) first cross another DD target while being dragged
60682          * @param {Grid} this
60683          * @param {Roo.GridDD} dd The drag drop object
60684          * @param {String} targetId The target drag drop object
60685          * @param {event} e The raw browser event
60686          */
60687         "dragenter" : true,
60688         /**
60689          * @event dragout
60690          * Fires when the dragged row(s) leave another DD target while being dragged
60691          * @param {Grid} this
60692          * @param {Roo.GridDD} dd The drag drop object
60693          * @param {String} targetId The target drag drop object
60694          * @param {event} e The raw browser event
60695          */
60696         "dragout" : true,
60697         /**
60698          * @event rowclass
60699          * Fires when a row is rendered, so you can change add a style to it.
60700          * @param {GridView} gridview   The grid view
60701          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60702          */
60703         'rowclass' : true,
60704
60705         /**
60706          * @event render
60707          * Fires when the grid is rendered
60708          * @param {Grid} grid
60709          */
60710         'render' : true,
60711             /**
60712              * @event select
60713              * Fires when a date is selected
60714              * @param {DatePicker} this
60715              * @param {Date} date The selected date
60716              */
60717         'select': true,
60718         /**
60719              * @event monthchange
60720              * Fires when the displayed month changes 
60721              * @param {DatePicker} this
60722              * @param {Date} date The selected month
60723              */
60724         'monthchange': true,
60725         /**
60726              * @event evententer
60727              * Fires when mouse over an event
60728              * @param {Calendar} this
60729              * @param {event} Event
60730              */
60731         'evententer': true,
60732         /**
60733              * @event eventleave
60734              * Fires when the mouse leaves an
60735              * @param {Calendar} this
60736              * @param {event}
60737              */
60738         'eventleave': true,
60739         /**
60740              * @event eventclick
60741              * Fires when the mouse click an
60742              * @param {Calendar} this
60743              * @param {event}
60744              */
60745         'eventclick': true,
60746         /**
60747              * @event eventrender
60748              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60749              * @param {Calendar} this
60750              * @param {data} data to be modified
60751              */
60752         'eventrender': true
60753         
60754     });
60755
60756     Roo.grid.Grid.superclass.constructor.call(this);
60757     this.on('render', function() {
60758         this.view.el.addClass('x-grid-cal'); 
60759         
60760         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60761
60762     },this);
60763     
60764     if (!Roo.grid.Calendar.style) {
60765         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60766             
60767             
60768             '.x-grid-cal .x-grid-col' :  {
60769                 height: 'auto !important',
60770                 'vertical-align': 'top'
60771             },
60772             '.x-grid-cal  .fc-event-hori' : {
60773                 height: '14px'
60774             }
60775              
60776             
60777         }, Roo.id());
60778     }
60779
60780     
60781     
60782 };
60783 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60784     /**
60785      * @cfg {Store} eventStore The store that loads events.
60786      */
60787     eventStore : 25,
60788
60789      
60790     activeDate : false,
60791     startDay : 0,
60792     autoWidth : true,
60793     monitorWindowResize : false,
60794
60795     
60796     resizeColumns : function() {
60797         var col = (this.view.el.getWidth() / 7) - 3;
60798         // loop through cols, and setWidth
60799         for(var i =0 ; i < 7 ; i++){
60800             this.cm.setColumnWidth(i, col);
60801         }
60802     },
60803      setDate :function(date) {
60804         
60805         Roo.log('setDate?');
60806         
60807         this.resizeColumns();
60808         var vd = this.activeDate;
60809         this.activeDate = date;
60810 //        if(vd && this.el){
60811 //            var t = date.getTime();
60812 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60813 //                Roo.log('using add remove');
60814 //                
60815 //                this.fireEvent('monthchange', this, date);
60816 //                
60817 //                this.cells.removeClass("fc-state-highlight");
60818 //                this.cells.each(function(c){
60819 //                   if(c.dateValue == t){
60820 //                       c.addClass("fc-state-highlight");
60821 //                       setTimeout(function(){
60822 //                            try{c.dom.firstChild.focus();}catch(e){}
60823 //                       }, 50);
60824 //                       return false;
60825 //                   }
60826 //                   return true;
60827 //                });
60828 //                return;
60829 //            }
60830 //        }
60831         
60832         var days = date.getDaysInMonth();
60833         
60834         var firstOfMonth = date.getFirstDateOfMonth();
60835         var startingPos = firstOfMonth.getDay()-this.startDay;
60836         
60837         if(startingPos < this.startDay){
60838             startingPos += 7;
60839         }
60840         
60841         var pm = date.add(Date.MONTH, -1);
60842         var prevStart = pm.getDaysInMonth()-startingPos;
60843 //        
60844         
60845         
60846         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60847         
60848         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60849         //this.cells.addClassOnOver('fc-state-hover');
60850         
60851         var cells = this.cells.elements;
60852         var textEls = this.textNodes;
60853         
60854         //Roo.each(cells, function(cell){
60855         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60856         //});
60857         
60858         days += startingPos;
60859
60860         // convert everything to numbers so it's fast
60861         var day = 86400000;
60862         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60863         //Roo.log(d);
60864         //Roo.log(pm);
60865         //Roo.log(prevStart);
60866         
60867         var today = new Date().clearTime().getTime();
60868         var sel = date.clearTime().getTime();
60869         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60870         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60871         var ddMatch = this.disabledDatesRE;
60872         var ddText = this.disabledDatesText;
60873         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60874         var ddaysText = this.disabledDaysText;
60875         var format = this.format;
60876         
60877         var setCellClass = function(cal, cell){
60878             
60879             //Roo.log('set Cell Class');
60880             cell.title = "";
60881             var t = d.getTime();
60882             
60883             //Roo.log(d);
60884             
60885             
60886             cell.dateValue = t;
60887             if(t == today){
60888                 cell.className += " fc-today";
60889                 cell.className += " fc-state-highlight";
60890                 cell.title = cal.todayText;
60891             }
60892             if(t == sel){
60893                 // disable highlight in other month..
60894                 cell.className += " fc-state-highlight";
60895                 
60896             }
60897             // disabling
60898             if(t < min) {
60899                 //cell.className = " fc-state-disabled";
60900                 cell.title = cal.minText;
60901                 return;
60902             }
60903             if(t > max) {
60904                 //cell.className = " fc-state-disabled";
60905                 cell.title = cal.maxText;
60906                 return;
60907             }
60908             if(ddays){
60909                 if(ddays.indexOf(d.getDay()) != -1){
60910                     // cell.title = ddaysText;
60911                    // cell.className = " fc-state-disabled";
60912                 }
60913             }
60914             if(ddMatch && format){
60915                 var fvalue = d.dateFormat(format);
60916                 if(ddMatch.test(fvalue)){
60917                     cell.title = ddText.replace("%0", fvalue);
60918                    cell.className = " fc-state-disabled";
60919                 }
60920             }
60921             
60922             if (!cell.initialClassName) {
60923                 cell.initialClassName = cell.dom.className;
60924             }
60925             
60926             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60927         };
60928
60929         var i = 0;
60930         
60931         for(; i < startingPos; i++) {
60932             cells[i].dayName =  (++prevStart);
60933             Roo.log(textEls[i]);
60934             d.setDate(d.getDate()+1);
60935             
60936             //cells[i].className = "fc-past fc-other-month";
60937             setCellClass(this, cells[i]);
60938         }
60939         
60940         var intDay = 0;
60941         
60942         for(; i < days; i++){
60943             intDay = i - startingPos + 1;
60944             cells[i].dayName =  (intDay);
60945             d.setDate(d.getDate()+1);
60946             
60947             cells[i].className = ''; // "x-date-active";
60948             setCellClass(this, cells[i]);
60949         }
60950         var extraDays = 0;
60951         
60952         for(; i < 42; i++) {
60953             //textEls[i].innerHTML = (++extraDays);
60954             
60955             d.setDate(d.getDate()+1);
60956             cells[i].dayName = (++extraDays);
60957             cells[i].className = "fc-future fc-other-month";
60958             setCellClass(this, cells[i]);
60959         }
60960         
60961         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60962         
60963         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60964         
60965         // this will cause all the cells to mis
60966         var rows= [];
60967         var i =0;
60968         for (var r = 0;r < 6;r++) {
60969             for (var c =0;c < 7;c++) {
60970                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60971             }    
60972         }
60973         
60974         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60975         for(i=0;i<cells.length;i++) {
60976             
60977             this.cells.elements[i].dayName = cells[i].dayName ;
60978             this.cells.elements[i].className = cells[i].className;
60979             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60980             this.cells.elements[i].title = cells[i].title ;
60981             this.cells.elements[i].dateValue = cells[i].dateValue ;
60982         }
60983         
60984         
60985         
60986         
60987         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60988         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60989         
60990         ////if(totalRows != 6){
60991             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60992            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60993        // }
60994         
60995         this.fireEvent('monthchange', this, date);
60996         
60997         
60998     },
60999  /**
61000      * Returns the grid's SelectionModel.
61001      * @return {SelectionModel}
61002      */
61003     getSelectionModel : function(){
61004         if(!this.selModel){
61005             this.selModel = new Roo.grid.CellSelectionModel();
61006         }
61007         return this.selModel;
61008     },
61009
61010     load: function() {
61011         this.eventStore.load()
61012         
61013         
61014         
61015     },
61016     
61017     findCell : function(dt) {
61018         dt = dt.clearTime().getTime();
61019         var ret = false;
61020         this.cells.each(function(c){
61021             //Roo.log("check " +c.dateValue + '?=' + dt);
61022             if(c.dateValue == dt){
61023                 ret = c;
61024                 return false;
61025             }
61026             return true;
61027         });
61028         
61029         return ret;
61030     },
61031     
61032     findCells : function(rec) {
61033         var s = rec.data.start_dt.clone().clearTime().getTime();
61034        // Roo.log(s);
61035         var e= rec.data.end_dt.clone().clearTime().getTime();
61036        // Roo.log(e);
61037         var ret = [];
61038         this.cells.each(function(c){
61039              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61040             
61041             if(c.dateValue > e){
61042                 return ;
61043             }
61044             if(c.dateValue < s){
61045                 return ;
61046             }
61047             ret.push(c);
61048         });
61049         
61050         return ret;    
61051     },
61052     
61053     findBestRow: function(cells)
61054     {
61055         var ret = 0;
61056         
61057         for (var i =0 ; i < cells.length;i++) {
61058             ret  = Math.max(cells[i].rows || 0,ret);
61059         }
61060         return ret;
61061         
61062     },
61063     
61064     
61065     addItem : function(rec)
61066     {
61067         // look for vertical location slot in
61068         var cells = this.findCells(rec);
61069         
61070         rec.row = this.findBestRow(cells);
61071         
61072         // work out the location.
61073         
61074         var crow = false;
61075         var rows = [];
61076         for(var i =0; i < cells.length; i++) {
61077             if (!crow) {
61078                 crow = {
61079                     start : cells[i],
61080                     end :  cells[i]
61081                 };
61082                 continue;
61083             }
61084             if (crow.start.getY() == cells[i].getY()) {
61085                 // on same row.
61086                 crow.end = cells[i];
61087                 continue;
61088             }
61089             // different row.
61090             rows.push(crow);
61091             crow = {
61092                 start: cells[i],
61093                 end : cells[i]
61094             };
61095             
61096         }
61097         
61098         rows.push(crow);
61099         rec.els = [];
61100         rec.rows = rows;
61101         rec.cells = cells;
61102         for (var i = 0; i < cells.length;i++) {
61103             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61104             
61105         }
61106         
61107         
61108     },
61109     
61110     clearEvents: function() {
61111         
61112         if (!this.eventStore.getCount()) {
61113             return;
61114         }
61115         // reset number of rows in cells.
61116         Roo.each(this.cells.elements, function(c){
61117             c.rows = 0;
61118         });
61119         
61120         this.eventStore.each(function(e) {
61121             this.clearEvent(e);
61122         },this);
61123         
61124     },
61125     
61126     clearEvent : function(ev)
61127     {
61128         if (ev.els) {
61129             Roo.each(ev.els, function(el) {
61130                 el.un('mouseenter' ,this.onEventEnter, this);
61131                 el.un('mouseleave' ,this.onEventLeave, this);
61132                 el.remove();
61133             },this);
61134             ev.els = [];
61135         }
61136     },
61137     
61138     
61139     renderEvent : function(ev,ctr) {
61140         if (!ctr) {
61141              ctr = this.view.el.select('.fc-event-container',true).first();
61142         }
61143         
61144          
61145         this.clearEvent(ev);
61146             //code
61147        
61148         
61149         
61150         ev.els = [];
61151         var cells = ev.cells;
61152         var rows = ev.rows;
61153         this.fireEvent('eventrender', this, ev);
61154         
61155         for(var i =0; i < rows.length; i++) {
61156             
61157             cls = '';
61158             if (i == 0) {
61159                 cls += ' fc-event-start';
61160             }
61161             if ((i+1) == rows.length) {
61162                 cls += ' fc-event-end';
61163             }
61164             
61165             //Roo.log(ev.data);
61166             // how many rows should it span..
61167             var cg = this.eventTmpl.append(ctr,Roo.apply({
61168                 fccls : cls
61169                 
61170             }, ev.data) , true);
61171             
61172             
61173             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61174             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61175             cg.on('click', this.onEventClick, this, ev);
61176             
61177             ev.els.push(cg);
61178             
61179             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61180             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61181             //Roo.log(cg);
61182              
61183             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61184             cg.setWidth(ebox.right - sbox.x -2);
61185         }
61186     },
61187     
61188     renderEvents: function()
61189     {   
61190         // first make sure there is enough space..
61191         
61192         if (!this.eventTmpl) {
61193             this.eventTmpl = new Roo.Template(
61194                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61195                     '<div class="fc-event-inner">' +
61196                         '<span class="fc-event-time">{time}</span>' +
61197                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61198                     '</div>' +
61199                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61200                 '</div>'
61201             );
61202                 
61203         }
61204                
61205         
61206         
61207         this.cells.each(function(c) {
61208             //Roo.log(c.select('.fc-day-content div',true).first());
61209             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61210         });
61211         
61212         var ctr = this.view.el.select('.fc-event-container',true).first();
61213         
61214         var cls;
61215         this.eventStore.each(function(ev){
61216             
61217             this.renderEvent(ev);
61218              
61219              
61220         }, this);
61221         this.view.layout();
61222         
61223     },
61224     
61225     onEventEnter: function (e, el,event,d) {
61226         this.fireEvent('evententer', this, el, event);
61227     },
61228     
61229     onEventLeave: function (e, el,event,d) {
61230         this.fireEvent('eventleave', this, el, event);
61231     },
61232     
61233     onEventClick: function (e, el,event,d) {
61234         this.fireEvent('eventclick', this, el, event);
61235     },
61236     
61237     onMonthChange: function () {
61238         this.store.load();
61239     },
61240     
61241     onLoad: function () {
61242         
61243         //Roo.log('calendar onload');
61244 //         
61245         if(this.eventStore.getCount() > 0){
61246             
61247            
61248             
61249             this.eventStore.each(function(d){
61250                 
61251                 
61252                 // FIXME..
61253                 var add =   d.data;
61254                 if (typeof(add.end_dt) == 'undefined')  {
61255                     Roo.log("Missing End time in calendar data: ");
61256                     Roo.log(d);
61257                     return;
61258                 }
61259                 if (typeof(add.start_dt) == 'undefined')  {
61260                     Roo.log("Missing Start time in calendar data: ");
61261                     Roo.log(d);
61262                     return;
61263                 }
61264                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61265                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61266                 add.id = add.id || d.id;
61267                 add.title = add.title || '??';
61268                 
61269                 this.addItem(d);
61270                 
61271              
61272             },this);
61273         }
61274         
61275         this.renderEvents();
61276     }
61277     
61278
61279 });
61280 /*
61281  grid : {
61282                 xtype: 'Grid',
61283                 xns: Roo.grid,
61284                 listeners : {
61285                     render : function ()
61286                     {
61287                         _this.grid = this;
61288                         
61289                         if (!this.view.el.hasClass('course-timesheet')) {
61290                             this.view.el.addClass('course-timesheet');
61291                         }
61292                         if (this.tsStyle) {
61293                             this.ds.load({});
61294                             return; 
61295                         }
61296                         Roo.log('width');
61297                         Roo.log(_this.grid.view.el.getWidth());
61298                         
61299                         
61300                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61301                             '.course-timesheet .x-grid-row' : {
61302                                 height: '80px'
61303                             },
61304                             '.x-grid-row td' : {
61305                                 'vertical-align' : 0
61306                             },
61307                             '.course-edit-link' : {
61308                                 'color' : 'blue',
61309                                 'text-overflow' : 'ellipsis',
61310                                 'overflow' : 'hidden',
61311                                 'white-space' : 'nowrap',
61312                                 'cursor' : 'pointer'
61313                             },
61314                             '.sub-link' : {
61315                                 'color' : 'green'
61316                             },
61317                             '.de-act-sup-link' : {
61318                                 'color' : 'purple',
61319                                 'text-decoration' : 'line-through'
61320                             },
61321                             '.de-act-link' : {
61322                                 'color' : 'red',
61323                                 'text-decoration' : 'line-through'
61324                             },
61325                             '.course-timesheet .course-highlight' : {
61326                                 'border-top-style': 'dashed !important',
61327                                 'border-bottom-bottom': 'dashed !important'
61328                             },
61329                             '.course-timesheet .course-item' : {
61330                                 'font-family'   : 'tahoma, arial, helvetica',
61331                                 'font-size'     : '11px',
61332                                 'overflow'      : 'hidden',
61333                                 'padding-left'  : '10px',
61334                                 'padding-right' : '10px',
61335                                 'padding-top' : '10px' 
61336                             }
61337                             
61338                         }, Roo.id());
61339                                 this.ds.load({});
61340                     }
61341                 },
61342                 autoWidth : true,
61343                 monitorWindowResize : false,
61344                 cellrenderer : function(v,x,r)
61345                 {
61346                     return v;
61347                 },
61348                 sm : {
61349                     xtype: 'CellSelectionModel',
61350                     xns: Roo.grid
61351                 },
61352                 dataSource : {
61353                     xtype: 'Store',
61354                     xns: Roo.data,
61355                     listeners : {
61356                         beforeload : function (_self, options)
61357                         {
61358                             options.params = options.params || {};
61359                             options.params._month = _this.monthField.getValue();
61360                             options.params.limit = 9999;
61361                             options.params['sort'] = 'when_dt';    
61362                             options.params['dir'] = 'ASC';    
61363                             this.proxy.loadResponse = this.loadResponse;
61364                             Roo.log("load?");
61365                             //this.addColumns();
61366                         },
61367                         load : function (_self, records, options)
61368                         {
61369                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61370                                 // if you click on the translation.. you can edit it...
61371                                 var el = Roo.get(this);
61372                                 var id = el.dom.getAttribute('data-id');
61373                                 var d = el.dom.getAttribute('data-date');
61374                                 var t = el.dom.getAttribute('data-time');
61375                                 //var id = this.child('span').dom.textContent;
61376                                 
61377                                 //Roo.log(this);
61378                                 Pman.Dialog.CourseCalendar.show({
61379                                     id : id,
61380                                     when_d : d,
61381                                     when_t : t,
61382                                     productitem_active : id ? 1 : 0
61383                                 }, function() {
61384                                     _this.grid.ds.load({});
61385                                 });
61386                            
61387                            });
61388                            
61389                            _this.panel.fireEvent('resize', [ '', '' ]);
61390                         }
61391                     },
61392                     loadResponse : function(o, success, response){
61393                             // this is overridden on before load..
61394                             
61395                             Roo.log("our code?");       
61396                             //Roo.log(success);
61397                             //Roo.log(response)
61398                             delete this.activeRequest;
61399                             if(!success){
61400                                 this.fireEvent("loadexception", this, o, response);
61401                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61402                                 return;
61403                             }
61404                             var result;
61405                             try {
61406                                 result = o.reader.read(response);
61407                             }catch(e){
61408                                 Roo.log("load exception?");
61409                                 this.fireEvent("loadexception", this, o, response, e);
61410                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61411                                 return;
61412                             }
61413                             Roo.log("ready...");        
61414                             // loop through result.records;
61415                             // and set this.tdate[date] = [] << array of records..
61416                             _this.tdata  = {};
61417                             Roo.each(result.records, function(r){
61418                                 //Roo.log(r.data);
61419                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61420                                     _this.tdata[r.data.when_dt.format('j')] = [];
61421                                 }
61422                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61423                             });
61424                             
61425                             //Roo.log(_this.tdata);
61426                             
61427                             result.records = [];
61428                             result.totalRecords = 6;
61429                     
61430                             // let's generate some duumy records for the rows.
61431                             //var st = _this.dateField.getValue();
61432                             
61433                             // work out monday..
61434                             //st = st.add(Date.DAY, -1 * st.format('w'));
61435                             
61436                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61437                             
61438                             var firstOfMonth = date.getFirstDayOfMonth();
61439                             var days = date.getDaysInMonth();
61440                             var d = 1;
61441                             var firstAdded = false;
61442                             for (var i = 0; i < result.totalRecords ; i++) {
61443                                 //var d= st.add(Date.DAY, i);
61444                                 var row = {};
61445                                 var added = 0;
61446                                 for(var w = 0 ; w < 7 ; w++){
61447                                     if(!firstAdded && firstOfMonth != w){
61448                                         continue;
61449                                     }
61450                                     if(d > days){
61451                                         continue;
61452                                     }
61453                                     firstAdded = true;
61454                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61455                                     row['weekday'+w] = String.format(
61456                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61457                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61458                                                     d,
61459                                                     date.format('Y-m-')+dd
61460                                                 );
61461                                     added++;
61462                                     if(typeof(_this.tdata[d]) != 'undefined'){
61463                                         Roo.each(_this.tdata[d], function(r){
61464                                             var is_sub = '';
61465                                             var deactive = '';
61466                                             var id = r.id;
61467                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61468                                             if(r.parent_id*1>0){
61469                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61470                                                 id = r.parent_id;
61471                                             }
61472                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61473                                                 deactive = 'de-act-link';
61474                                             }
61475                                             
61476                                             row['weekday'+w] += String.format(
61477                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61478                                                     id, //0
61479                                                     r.product_id_name, //1
61480                                                     r.when_dt.format('h:ia'), //2
61481                                                     is_sub, //3
61482                                                     deactive, //4
61483                                                     desc // 5
61484                                             );
61485                                         });
61486                                     }
61487                                     d++;
61488                                 }
61489                                 
61490                                 // only do this if something added..
61491                                 if(added > 0){ 
61492                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61493                                 }
61494                                 
61495                                 
61496                                 // push it twice. (second one with an hour..
61497                                 
61498                             }
61499                             //Roo.log(result);
61500                             this.fireEvent("load", this, o, o.request.arg);
61501                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61502                         },
61503                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61504                     proxy : {
61505                         xtype: 'HttpProxy',
61506                         xns: Roo.data,
61507                         method : 'GET',
61508                         url : baseURL + '/Roo/Shop_course.php'
61509                     },
61510                     reader : {
61511                         xtype: 'JsonReader',
61512                         xns: Roo.data,
61513                         id : 'id',
61514                         fields : [
61515                             {
61516                                 'name': 'id',
61517                                 'type': 'int'
61518                             },
61519                             {
61520                                 'name': 'when_dt',
61521                                 'type': 'string'
61522                             },
61523                             {
61524                                 'name': 'end_dt',
61525                                 'type': 'string'
61526                             },
61527                             {
61528                                 'name': 'parent_id',
61529                                 'type': 'int'
61530                             },
61531                             {
61532                                 'name': 'product_id',
61533                                 'type': 'int'
61534                             },
61535                             {
61536                                 'name': 'productitem_id',
61537                                 'type': 'int'
61538                             },
61539                             {
61540                                 'name': 'guid',
61541                                 'type': 'int'
61542                             }
61543                         ]
61544                     }
61545                 },
61546                 toolbar : {
61547                     xtype: 'Toolbar',
61548                     xns: Roo,
61549                     items : [
61550                         {
61551                             xtype: 'Button',
61552                             xns: Roo.Toolbar,
61553                             listeners : {
61554                                 click : function (_self, e)
61555                                 {
61556                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61557                                     sd.setMonth(sd.getMonth()-1);
61558                                     _this.monthField.setValue(sd.format('Y-m-d'));
61559                                     _this.grid.ds.load({});
61560                                 }
61561                             },
61562                             text : "Back"
61563                         },
61564                         {
61565                             xtype: 'Separator',
61566                             xns: Roo.Toolbar
61567                         },
61568                         {
61569                             xtype: 'MonthField',
61570                             xns: Roo.form,
61571                             listeners : {
61572                                 render : function (_self)
61573                                 {
61574                                     _this.monthField = _self;
61575                                    // _this.monthField.set  today
61576                                 },
61577                                 select : function (combo, date)
61578                                 {
61579                                     _this.grid.ds.load({});
61580                                 }
61581                             },
61582                             value : (function() { return new Date(); })()
61583                         },
61584                         {
61585                             xtype: 'Separator',
61586                             xns: Roo.Toolbar
61587                         },
61588                         {
61589                             xtype: 'TextItem',
61590                             xns: Roo.Toolbar,
61591                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61592                         },
61593                         {
61594                             xtype: 'Fill',
61595                             xns: Roo.Toolbar
61596                         },
61597                         {
61598                             xtype: 'Button',
61599                             xns: Roo.Toolbar,
61600                             listeners : {
61601                                 click : function (_self, e)
61602                                 {
61603                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61604                                     sd.setMonth(sd.getMonth()+1);
61605                                     _this.monthField.setValue(sd.format('Y-m-d'));
61606                                     _this.grid.ds.load({});
61607                                 }
61608                             },
61609                             text : "Next"
61610                         }
61611                     ]
61612                 },
61613                  
61614             }
61615         };
61616         
61617         *//*
61618  * Based on:
61619  * Ext JS Library 1.1.1
61620  * Copyright(c) 2006-2007, Ext JS, LLC.
61621  *
61622  * Originally Released Under LGPL - original licence link has changed is not relivant.
61623  *
61624  * Fork - LGPL
61625  * <script type="text/javascript">
61626  */
61627  
61628 /**
61629  * @class Roo.LoadMask
61630  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61631  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61632  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61633  * element's UpdateManager load indicator and will be destroyed after the initial load.
61634  * @constructor
61635  * Create a new LoadMask
61636  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61637  * @param {Object} config The config object
61638  */
61639 Roo.LoadMask = function(el, config){
61640     this.el = Roo.get(el);
61641     Roo.apply(this, config);
61642     if(this.store){
61643         this.store.on('beforeload', this.onBeforeLoad, this);
61644         this.store.on('load', this.onLoad, this);
61645         this.store.on('loadexception', this.onLoadException, this);
61646         this.removeMask = false;
61647     }else{
61648         var um = this.el.getUpdateManager();
61649         um.showLoadIndicator = false; // disable the default indicator
61650         um.on('beforeupdate', this.onBeforeLoad, this);
61651         um.on('update', this.onLoad, this);
61652         um.on('failure', this.onLoad, this);
61653         this.removeMask = true;
61654     }
61655 };
61656
61657 Roo.LoadMask.prototype = {
61658     /**
61659      * @cfg {Boolean} removeMask
61660      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61661      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61662      */
61663     removeMask : false,
61664     /**
61665      * @cfg {String} msg
61666      * The text to display in a centered loading message box (defaults to 'Loading...')
61667      */
61668     msg : 'Loading...',
61669     /**
61670      * @cfg {String} msgCls
61671      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61672      */
61673     msgCls : 'x-mask-loading',
61674
61675     /**
61676      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61677      * @type Boolean
61678      */
61679     disabled: false,
61680
61681     /**
61682      * Disables the mask to prevent it from being displayed
61683      */
61684     disable : function(){
61685        this.disabled = true;
61686     },
61687
61688     /**
61689      * Enables the mask so that it can be displayed
61690      */
61691     enable : function(){
61692         this.disabled = false;
61693     },
61694     
61695     onLoadException : function()
61696     {
61697         Roo.log(arguments);
61698         
61699         if (typeof(arguments[3]) != 'undefined') {
61700             Roo.MessageBox.alert("Error loading",arguments[3]);
61701         } 
61702         /*
61703         try {
61704             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61705                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61706             }   
61707         } catch(e) {
61708             
61709         }
61710         */
61711     
61712         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61713     },
61714     // private
61715     onLoad : function()
61716     {
61717         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61718     },
61719
61720     // private
61721     onBeforeLoad : function(){
61722         if(!this.disabled){
61723             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61724         }
61725     },
61726
61727     // private
61728     destroy : function(){
61729         if(this.store){
61730             this.store.un('beforeload', this.onBeforeLoad, this);
61731             this.store.un('load', this.onLoad, this);
61732             this.store.un('loadexception', this.onLoadException, this);
61733         }else{
61734             var um = this.el.getUpdateManager();
61735             um.un('beforeupdate', this.onBeforeLoad, this);
61736             um.un('update', this.onLoad, this);
61737             um.un('failure', this.onLoad, this);
61738         }
61739     }
61740 };/*
61741  * Based on:
61742  * Ext JS Library 1.1.1
61743  * Copyright(c) 2006-2007, Ext JS, LLC.
61744  *
61745  * Originally Released Under LGPL - original licence link has changed is not relivant.
61746  *
61747  * Fork - LGPL
61748  * <script type="text/javascript">
61749  */
61750
61751
61752 /**
61753  * @class Roo.XTemplate
61754  * @extends Roo.Template
61755  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61756 <pre><code>
61757 var t = new Roo.XTemplate(
61758         '&lt;select name="{name}"&gt;',
61759                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61760         '&lt;/select&gt;'
61761 );
61762  
61763 // then append, applying the master template values
61764  </code></pre>
61765  *
61766  * Supported features:
61767  *
61768  *  Tags:
61769
61770 <pre><code>
61771       {a_variable} - output encoded.
61772       {a_variable.format:("Y-m-d")} - call a method on the variable
61773       {a_variable:raw} - unencoded output
61774       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61775       {a_variable:this.method_on_template(...)} - call a method on the template object.
61776  
61777 </code></pre>
61778  *  The tpl tag:
61779 <pre><code>
61780         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61781         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61782         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61783         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61784   
61785         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61786         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61787 </code></pre>
61788  *      
61789  */
61790 Roo.XTemplate = function()
61791 {
61792     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61793     if (this.html) {
61794         this.compile();
61795     }
61796 };
61797
61798
61799 Roo.extend(Roo.XTemplate, Roo.Template, {
61800
61801     /**
61802      * The various sub templates
61803      */
61804     tpls : false,
61805     /**
61806      *
61807      * basic tag replacing syntax
61808      * WORD:WORD()
61809      *
61810      * // you can fake an object call by doing this
61811      *  x.t:(test,tesT) 
61812      * 
61813      */
61814     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61815
61816     /**
61817      * compile the template
61818      *
61819      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61820      *
61821      */
61822     compile: function()
61823     {
61824         var s = this.html;
61825      
61826         s = ['<tpl>', s, '</tpl>'].join('');
61827     
61828         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61829             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61830             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61831             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61832             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61833             m,
61834             id     = 0,
61835             tpls   = [];
61836     
61837         while(true == !!(m = s.match(re))){
61838             var forMatch   = m[0].match(nameRe),
61839                 ifMatch   = m[0].match(ifRe),
61840                 execMatch   = m[0].match(execRe),
61841                 namedMatch   = m[0].match(namedRe),
61842                 
61843                 exp  = null, 
61844                 fn   = null,
61845                 exec = null,
61846                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61847                 
61848             if (ifMatch) {
61849                 // if - puts fn into test..
61850                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61851                 if(exp){
61852                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61853                 }
61854             }
61855             
61856             if (execMatch) {
61857                 // exec - calls a function... returns empty if true is  returned.
61858                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61859                 if(exp){
61860                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61861                 }
61862             }
61863             
61864             
61865             if (name) {
61866                 // for = 
61867                 switch(name){
61868                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61869                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61870                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61871                 }
61872             }
61873             var uid = namedMatch ? namedMatch[1] : id;
61874             
61875             
61876             tpls.push({
61877                 id:     namedMatch ? namedMatch[1] : id,
61878                 target: name,
61879                 exec:   exec,
61880                 test:   fn,
61881                 body:   m[1] || ''
61882             });
61883             if (namedMatch) {
61884                 s = s.replace(m[0], '');
61885             } else { 
61886                 s = s.replace(m[0], '{xtpl'+ id + '}');
61887             }
61888             ++id;
61889         }
61890         this.tpls = [];
61891         for(var i = tpls.length-1; i >= 0; --i){
61892             this.compileTpl(tpls[i]);
61893             this.tpls[tpls[i].id] = tpls[i];
61894         }
61895         this.master = tpls[tpls.length-1];
61896         return this;
61897     },
61898     /**
61899      * same as applyTemplate, except it's done to one of the subTemplates
61900      * when using named templates, you can do:
61901      *
61902      * var str = pl.applySubTemplate('your-name', values);
61903      *
61904      * 
61905      * @param {Number} id of the template
61906      * @param {Object} values to apply to template
61907      * @param {Object} parent (normaly the instance of this object)
61908      */
61909     applySubTemplate : function(id, values, parent)
61910     {
61911         
61912         
61913         var t = this.tpls[id];
61914         
61915         
61916         try { 
61917             if(t.test && !t.test.call(this, values, parent)){
61918                 return '';
61919             }
61920         } catch(e) {
61921             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61922             Roo.log(e.toString());
61923             Roo.log(t.test);
61924             return ''
61925         }
61926         try { 
61927             
61928             if(t.exec && t.exec.call(this, values, parent)){
61929                 return '';
61930             }
61931         } catch(e) {
61932             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61933             Roo.log(e.toString());
61934             Roo.log(t.exec);
61935             return ''
61936         }
61937         try {
61938             var vs = t.target ? t.target.call(this, values, parent) : values;
61939             parent = t.target ? values : parent;
61940             if(t.target && vs instanceof Array){
61941                 var buf = [];
61942                 for(var i = 0, len = vs.length; i < len; i++){
61943                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61944                 }
61945                 return buf.join('');
61946             }
61947             return t.compiled.call(this, vs, parent);
61948         } catch (e) {
61949             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61950             Roo.log(e.toString());
61951             Roo.log(t.compiled);
61952             return '';
61953         }
61954     },
61955
61956     compileTpl : function(tpl)
61957     {
61958         var fm = Roo.util.Format;
61959         var useF = this.disableFormats !== true;
61960         var sep = Roo.isGecko ? "+" : ",";
61961         var undef = function(str) {
61962             Roo.log("Property not found :"  + str);
61963             return '';
61964         };
61965         
61966         var fn = function(m, name, format, args)
61967         {
61968             //Roo.log(arguments);
61969             args = args ? args.replace(/\\'/g,"'") : args;
61970             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61971             if (typeof(format) == 'undefined') {
61972                 format= 'htmlEncode';
61973             }
61974             if (format == 'raw' ) {
61975                 format = false;
61976             }
61977             
61978             if(name.substr(0, 4) == 'xtpl'){
61979                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61980             }
61981             
61982             // build an array of options to determine if value is undefined..
61983             
61984             // basically get 'xxxx.yyyy' then do
61985             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61986             //    (function () { Roo.log("Property not found"); return ''; })() :
61987             //    ......
61988             
61989             var udef_ar = [];
61990             var lookfor = '';
61991             Roo.each(name.split('.'), function(st) {
61992                 lookfor += (lookfor.length ? '.': '') + st;
61993                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61994             });
61995             
61996             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61997             
61998             
61999             if(format && useF){
62000                 
62001                 args = args ? ',' + args : "";
62002                  
62003                 if(format.substr(0, 5) != "this."){
62004                     format = "fm." + format + '(';
62005                 }else{
62006                     format = 'this.call("'+ format.substr(5) + '", ';
62007                     args = ", values";
62008                 }
62009                 
62010                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62011             }
62012              
62013             if (args.length) {
62014                 // called with xxyx.yuu:(test,test)
62015                 // change to ()
62016                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62017             }
62018             // raw.. - :raw modifier..
62019             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62020             
62021         };
62022         var body;
62023         // branched to use + in gecko and [].join() in others
62024         if(Roo.isGecko){
62025             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62026                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62027                     "';};};";
62028         }else{
62029             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62030             body.push(tpl.body.replace(/(\r\n|\n)/g,
62031                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62032             body.push("'].join('');};};");
62033             body = body.join('');
62034         }
62035         
62036         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62037        
62038         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62039         eval(body);
62040         
62041         return this;
62042     },
62043
62044     applyTemplate : function(values){
62045         return this.master.compiled.call(this, values, {});
62046         //var s = this.subs;
62047     },
62048
62049     apply : function(){
62050         return this.applyTemplate.apply(this, arguments);
62051     }
62052
62053  });
62054
62055 Roo.XTemplate.from = function(el){
62056     el = Roo.getDom(el);
62057     return new Roo.XTemplate(el.value || el.innerHTML);
62058 };