empty mask on tables
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052          }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059       
1060         // sort?? a.sort().equals(b.sort());
1061       
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064                 return false;
1065             }
1066         }
1067         return true;
1068     }
1069 });
1070
1071
1072  
1073 /*
1074  * 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  * @class Roo.lib.Dom
1883  * @licence LGPL
1884  * @static
1885  * 
1886  * Dom utils (from YIU afaik)
1887  *
1888  * 
1889  **/
1890 Roo.lib.Dom = {
1891     /**
1892      * Get the view width
1893      * @param {Boolean} full True will get the full document, otherwise it's the view width
1894      * @return {Number} The width
1895      */
1896      
1897     getViewWidth : function(full) {
1898         return full ? this.getDocumentWidth() : this.getViewportWidth();
1899     },
1900     /**
1901      * Get the view height
1902      * @param {Boolean} full True will get the full document, otherwise it's the view height
1903      * @return {Number} The height
1904      */
1905     getViewHeight : function(full) {
1906         return full ? this.getDocumentHeight() : this.getViewportHeight();
1907     },
1908     /**
1909      * Get the Full Document height 
1910      * @return {Number} The height
1911      */
1912     getDocumentHeight: function() {
1913         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1914         return Math.max(scrollHeight, this.getViewportHeight());
1915     },
1916     /**
1917      * Get the Full Document width
1918      * @return {Number} The width
1919      */
1920     getDocumentWidth: function() {
1921         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1922         return Math.max(scrollWidth, this.getViewportWidth());
1923     },
1924     /**
1925      * Get the Window Viewport height
1926      * @return {Number} The height
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      * Get the Window Viewport width
1942      * @return {Number} The width
1943      */
1944     getViewportWidth: function() {
1945         var width = self.innerWidth;
1946         var mode = document.compatMode;
1947
1948         if (mode || Roo.isIE) {
1949             width = (mode == "CSS1Compat") ?
1950                     document.documentElement.clientWidth :
1951                     document.body.clientWidth;
1952         }
1953         return width;
1954     },
1955
1956     isAncestor : function(p, c) {
1957         p = Roo.getDom(p);
1958         c = Roo.getDom(c);
1959         if (!p || !c) {
1960             return false;
1961         }
1962
1963         if (p.contains && !Roo.isSafari) {
1964             return p.contains(c);
1965         } else if (p.compareDocumentPosition) {
1966             return !!(p.compareDocumentPosition(c) & 16);
1967         } else {
1968             var parent = c.parentNode;
1969             while (parent) {
1970                 if (parent == p) {
1971                     return true;
1972                 }
1973                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1974                     return false;
1975                 }
1976                 parent = parent.parentNode;
1977             }
1978             return false;
1979         }
1980     },
1981
1982     getRegion : function(el) {
1983         return Roo.lib.Region.getRegion(el);
1984     },
1985
1986     getY : function(el) {
1987         return this.getXY(el)[1];
1988     },
1989
1990     getX : function(el) {
1991         return this.getXY(el)[0];
1992     },
1993
1994     getXY : function(el) {
1995         var p, pe, b, scroll, bd = document.body;
1996         el = Roo.getDom(el);
1997         var fly = Roo.lib.AnimBase.fly;
1998         if (el.getBoundingClientRect) {
1999             b = el.getBoundingClientRect();
2000             scroll = fly(document).getScroll();
2001             return [b.left + scroll.left, b.top + scroll.top];
2002         }
2003         var x = 0, y = 0;
2004
2005         p = el;
2006
2007         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2008
2009         while (p) {
2010
2011             x += p.offsetLeft;
2012             y += p.offsetTop;
2013
2014             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2015                 hasAbsolute = true;
2016             }
2017
2018             if (Roo.isGecko) {
2019                 pe = fly(p);
2020
2021                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2022                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2023
2024
2025                 x += bl;
2026                 y += bt;
2027
2028
2029                 if (p != el && pe.getStyle('overflow') != 'visible') {
2030                     x += bl;
2031                     y += bt;
2032                 }
2033             }
2034             p = p.offsetParent;
2035         }
2036
2037         if (Roo.isSafari && hasAbsolute) {
2038             x -= bd.offsetLeft;
2039             y -= bd.offsetTop;
2040         }
2041
2042         if (Roo.isGecko && !hasAbsolute) {
2043             var dbd = fly(bd);
2044             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2045             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2046         }
2047
2048         p = el.parentNode;
2049         while (p && p != bd) {
2050             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2051                 x -= p.scrollLeft;
2052                 y -= p.scrollTop;
2053             }
2054             p = p.parentNode;
2055         }
2056         return [x, y];
2057     },
2058  
2059   
2060
2061
2062     setXY : function(el, xy) {
2063         el = Roo.fly(el, '_setXY');
2064         el.position();
2065         var pts = el.translatePoints(xy);
2066         if (xy[0] !== false) {
2067             el.dom.style.left = pts.left + "px";
2068         }
2069         if (xy[1] !== false) {
2070             el.dom.style.top = pts.top + "px";
2071         }
2072     },
2073
2074     setX : function(el, x) {
2075         this.setXY(el, [x, false]);
2076     },
2077
2078     setY : function(el, y) {
2079         this.setXY(el, [false, y]);
2080     }
2081 };
2082 /*
2083  * Portions of this file are based on pieces of Yahoo User Interface Library
2084  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2085  * YUI licensed under the BSD License:
2086  * http://developer.yahoo.net/yui/license.txt
2087  * <script type="text/javascript">
2088  *
2089  */
2090
2091 Roo.lib.Event = function() {
2092     var loadComplete = false;
2093     var listeners = [];
2094     var unloadListeners = [];
2095     var retryCount = 0;
2096     var onAvailStack = [];
2097     var counter = 0;
2098     var lastError = null;
2099
2100     return {
2101         POLL_RETRYS: 200,
2102         POLL_INTERVAL: 20,
2103         EL: 0,
2104         TYPE: 1,
2105         FN: 2,
2106         WFN: 3,
2107         OBJ: 3,
2108         ADJ_SCOPE: 4,
2109         _interval: null,
2110
2111         startInterval: function() {
2112             if (!this._interval) {
2113                 var self = this;
2114                 var callback = function() {
2115                     self._tryPreloadAttach();
2116                 };
2117                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2118
2119             }
2120         },
2121
2122         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2123             onAvailStack.push({ id:         p_id,
2124                 fn:         p_fn,
2125                 obj:        p_obj,
2126                 override:   p_override,
2127                 checkReady: false    });
2128
2129             retryCount = this.POLL_RETRYS;
2130             this.startInterval();
2131         },
2132
2133
2134         addListener: function(el, eventName, fn) {
2135             el = Roo.getDom(el);
2136             if (!el || !fn) {
2137                 return false;
2138             }
2139
2140             if ("unload" == eventName) {
2141                 unloadListeners[unloadListeners.length] =
2142                 [el, eventName, fn];
2143                 return true;
2144             }
2145
2146             var wrappedFn = function(e) {
2147                 return fn(Roo.lib.Event.getEvent(e));
2148             };
2149
2150             var li = [el, eventName, fn, wrappedFn];
2151
2152             var index = listeners.length;
2153             listeners[index] = li;
2154
2155             this.doAdd(el, eventName, wrappedFn, false);
2156             return true;
2157
2158         },
2159
2160
2161         removeListener: function(el, eventName, fn) {
2162             var i, len;
2163
2164             el = Roo.getDom(el);
2165
2166             if(!fn) {
2167                 return this.purgeElement(el, false, eventName);
2168             }
2169
2170
2171             if ("unload" == eventName) {
2172
2173                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2174                     var li = unloadListeners[i];
2175                     if (li &&
2176                         li[0] == el &&
2177                         li[1] == eventName &&
2178                         li[2] == fn) {
2179                         unloadListeners.splice(i, 1);
2180                         return true;
2181                     }
2182                 }
2183
2184                 return false;
2185             }
2186
2187             var cacheItem = null;
2188
2189
2190             var index = arguments[3];
2191
2192             if ("undefined" == typeof index) {
2193                 index = this._getCacheIndex(el, eventName, fn);
2194             }
2195
2196             if (index >= 0) {
2197                 cacheItem = listeners[index];
2198             }
2199
2200             if (!el || !cacheItem) {
2201                 return false;
2202             }
2203
2204             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2205
2206             delete listeners[index][this.WFN];
2207             delete listeners[index][this.FN];
2208             listeners.splice(index, 1);
2209
2210             return true;
2211
2212         },
2213
2214
2215         getTarget: function(ev, resolveTextNode) {
2216             ev = ev.browserEvent || ev;
2217             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2218             var t = ev.target || ev.srcElement;
2219             return this.resolveTextNode(t);
2220         },
2221
2222
2223         resolveTextNode: function(node) {
2224             if (Roo.isSafari && node && 3 == node.nodeType) {
2225                 return node.parentNode;
2226             } else {
2227                 return node;
2228             }
2229         },
2230
2231
2232         getPageX: function(ev) {
2233             ev = ev.browserEvent || ev;
2234             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2235             var x = ev.pageX;
2236             if (!x && 0 !== x) {
2237                 x = ev.clientX || 0;
2238
2239                 if (Roo.isIE) {
2240                     x += this.getScroll()[1];
2241                 }
2242             }
2243
2244             return x;
2245         },
2246
2247
2248         getPageY: function(ev) {
2249             ev = ev.browserEvent || ev;
2250             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2251             var y = ev.pageY;
2252             if (!y && 0 !== y) {
2253                 y = ev.clientY || 0;
2254
2255                 if (Roo.isIE) {
2256                     y += this.getScroll()[0];
2257                 }
2258             }
2259
2260
2261             return y;
2262         },
2263
2264
2265         getXY: function(ev) {
2266             ev = ev.browserEvent || ev;
2267             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2268             return [this.getPageX(ev), this.getPageY(ev)];
2269         },
2270
2271
2272         getRelatedTarget: function(ev) {
2273             ev = ev.browserEvent || ev;
2274             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2275             var t = ev.relatedTarget;
2276             if (!t) {
2277                 if (ev.type == "mouseout") {
2278                     t = ev.toElement;
2279                 } else if (ev.type == "mouseover") {
2280                     t = ev.fromElement;
2281                 }
2282             }
2283
2284             return this.resolveTextNode(t);
2285         },
2286
2287
2288         getTime: function(ev) {
2289             ev = ev.browserEvent || ev;
2290             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2291             if (!ev.time) {
2292                 var t = new Date().getTime();
2293                 try {
2294                     ev.time = t;
2295                 } catch(ex) {
2296                     this.lastError = ex;
2297                     return t;
2298                 }
2299             }
2300
2301             return ev.time;
2302         },
2303
2304
2305         stopEvent: function(ev) {
2306             this.stopPropagation(ev);
2307             this.preventDefault(ev);
2308         },
2309
2310
2311         stopPropagation: function(ev) {
2312             ev = ev.browserEvent || ev;
2313             if (ev.stopPropagation) {
2314                 ev.stopPropagation();
2315             } else {
2316                 ev.cancelBubble = true;
2317             }
2318         },
2319
2320
2321         preventDefault: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             if(ev.preventDefault) {
2324                 ev.preventDefault();
2325             } else {
2326                 ev.returnValue = false;
2327             }
2328         },
2329
2330
2331         getEvent: function(e) {
2332             var ev = e || window.event;
2333             if (!ev) {
2334                 var c = this.getEvent.caller;
2335                 while (c) {
2336                     ev = c.arguments[0];
2337                     if (ev && Event == ev.constructor) {
2338                         break;
2339                     }
2340                     c = c.caller;
2341                 }
2342             }
2343             return ev;
2344         },
2345
2346
2347         getCharCode: function(ev) {
2348             ev = ev.browserEvent || ev;
2349             return ev.charCode || ev.keyCode || 0;
2350         },
2351
2352
2353         _getCacheIndex: function(el, eventName, fn) {
2354             for (var i = 0,len = listeners.length; i < len; ++i) {
2355                 var li = listeners[i];
2356                 if (li &&
2357                     li[this.FN] == fn &&
2358                     li[this.EL] == el &&
2359                     li[this.TYPE] == eventName) {
2360                     return i;
2361                 }
2362             }
2363
2364             return -1;
2365         },
2366
2367
2368         elCache: {},
2369
2370
2371         getEl: function(id) {
2372             return document.getElementById(id);
2373         },
2374
2375
2376         clearCache: function() {
2377         },
2378
2379
2380         _load: function(e) {
2381             loadComplete = true;
2382             var EU = Roo.lib.Event;
2383
2384
2385             if (Roo.isIE) {
2386                 EU.doRemove(window, "load", EU._load);
2387             }
2388         },
2389
2390
2391         _tryPreloadAttach: function() {
2392
2393             if (this.locked) {
2394                 return false;
2395             }
2396
2397             this.locked = true;
2398
2399
2400             var tryAgain = !loadComplete;
2401             if (!tryAgain) {
2402                 tryAgain = (retryCount > 0);
2403             }
2404
2405
2406             var notAvail = [];
2407             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2408                 var item = onAvailStack[i];
2409                 if (item) {
2410                     var el = this.getEl(item.id);
2411
2412                     if (el) {
2413                         if (!item.checkReady ||
2414                             loadComplete ||
2415                             el.nextSibling ||
2416                             (document && document.body)) {
2417
2418                             var scope = el;
2419                             if (item.override) {
2420                                 if (item.override === true) {
2421                                     scope = item.obj;
2422                                 } else {
2423                                     scope = item.override;
2424                                 }
2425                             }
2426                             item.fn.call(scope, item.obj);
2427                             onAvailStack[i] = null;
2428                         }
2429                     } else {
2430                         notAvail.push(item);
2431                     }
2432                 }
2433             }
2434
2435             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2436
2437             if (tryAgain) {
2438
2439                 this.startInterval();
2440             } else {
2441                 clearInterval(this._interval);
2442                 this._interval = null;
2443             }
2444
2445             this.locked = false;
2446
2447             return true;
2448
2449         },
2450
2451
2452         purgeElement: function(el, recurse, eventName) {
2453             var elListeners = this.getListeners(el, eventName);
2454             if (elListeners) {
2455                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2456                     var l = elListeners[i];
2457                     this.removeListener(el, l.type, l.fn);
2458                 }
2459             }
2460
2461             if (recurse && el && el.childNodes) {
2462                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2463                     this.purgeElement(el.childNodes[i], recurse, eventName);
2464                 }
2465             }
2466         },
2467
2468
2469         getListeners: function(el, eventName) {
2470             var results = [], searchLists;
2471             if (!eventName) {
2472                 searchLists = [listeners, unloadListeners];
2473             } else if (eventName == "unload") {
2474                 searchLists = [unloadListeners];
2475             } else {
2476                 searchLists = [listeners];
2477             }
2478
2479             for (var j = 0; j < searchLists.length; ++j) {
2480                 var searchList = searchLists[j];
2481                 if (searchList && searchList.length > 0) {
2482                     for (var i = 0,len = searchList.length; i < len; ++i) {
2483                         var l = searchList[i];
2484                         if (l && l[this.EL] === el &&
2485                             (!eventName || eventName === l[this.TYPE])) {
2486                             results.push({
2487                                 type:   l[this.TYPE],
2488                                 fn:     l[this.FN],
2489                                 obj:    l[this.OBJ],
2490                                 adjust: l[this.ADJ_SCOPE],
2491                                 index:  i
2492                             });
2493                         }
2494                     }
2495                 }
2496             }
2497
2498             return (results.length) ? results : null;
2499         },
2500
2501
2502         _unload: function(e) {
2503
2504             var EU = Roo.lib.Event, i, j, l, len, index;
2505
2506             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2507                 l = unloadListeners[i];
2508                 if (l) {
2509                     var scope = window;
2510                     if (l[EU.ADJ_SCOPE]) {
2511                         if (l[EU.ADJ_SCOPE] === true) {
2512                             scope = l[EU.OBJ];
2513                         } else {
2514                             scope = l[EU.ADJ_SCOPE];
2515                         }
2516                     }
2517                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2518                     unloadListeners[i] = null;
2519                     l = null;
2520                     scope = null;
2521                 }
2522             }
2523
2524             unloadListeners = null;
2525
2526             if (listeners && listeners.length > 0) {
2527                 j = listeners.length;
2528                 while (j) {
2529                     index = j - 1;
2530                     l = listeners[index];
2531                     if (l) {
2532                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2533                                 l[EU.FN], index);
2534                     }
2535                     j = j - 1;
2536                 }
2537                 l = null;
2538
2539                 EU.clearCache();
2540             }
2541
2542             EU.doRemove(window, "unload", EU._unload);
2543
2544         },
2545
2546
2547         getScroll: function() {
2548             var dd = document.documentElement, db = document.body;
2549             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2550                 return [dd.scrollTop, dd.scrollLeft];
2551             } else if (db) {
2552                 return [db.scrollTop, db.scrollLeft];
2553             } else {
2554                 return [0, 0];
2555             }
2556         },
2557
2558
2559         doAdd: function () {
2560             if (window.addEventListener) {
2561                 return function(el, eventName, fn, capture) {
2562                     el.addEventListener(eventName, fn, (capture));
2563                 };
2564             } else if (window.attachEvent) {
2565                 return function(el, eventName, fn, capture) {
2566                     el.attachEvent("on" + eventName, fn);
2567                 };
2568             } else {
2569                 return function() {
2570                 };
2571             }
2572         }(),
2573
2574
2575         doRemove: function() {
2576             if (window.removeEventListener) {
2577                 return function (el, eventName, fn, capture) {
2578                     el.removeEventListener(eventName, fn, (capture));
2579                 };
2580             } else if (window.detachEvent) {
2581                 return function (el, eventName, fn) {
2582                     el.detachEvent("on" + eventName, fn);
2583                 };
2584             } else {
2585                 return function() {
2586                 };
2587             }
2588         }()
2589     };
2590     
2591 }();
2592 (function() {     
2593    
2594     var E = Roo.lib.Event;
2595     E.on = E.addListener;
2596     E.un = E.removeListener;
2597
2598     if (document && document.body) {
2599         E._load();
2600     } else {
2601         E.doAdd(window, "load", E._load);
2602     }
2603     E.doAdd(window, "unload", E._unload);
2604     E._tryPreloadAttach();
2605 })();
2606
2607  
2608
2609 (function() {
2610     /**
2611      * @class Roo.lib.Ajax
2612      *
2613      * provide a simple Ajax request utility functions
2614      * 
2615      * Portions of this file are based on pieces of Yahoo User Interface Library
2616     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2617     * YUI licensed under the BSD License:
2618     * http://developer.yahoo.net/yui/license.txt
2619     * <script type="text/javascript">
2620     *
2621      *
2622      */
2623     Roo.lib.Ajax = {
2624         /**
2625          * @static 
2626          */
2627         request : function(method, uri, cb, data, options) {
2628             if(options){
2629                 var hs = options.headers;
2630                 if(hs){
2631                     for(var h in hs){
2632                         if(hs.hasOwnProperty(h)){
2633                             this.initHeader(h, hs[h], false);
2634                         }
2635                     }
2636                 }
2637                 if(options.xmlData){
2638                     this.initHeader('Content-Type', 'text/xml', false);
2639                     method = 'POST';
2640                     data = options.xmlData;
2641                 }
2642             }
2643
2644             return this.asyncRequest(method, uri, cb, data);
2645         },
2646         /**
2647          * serialize a form
2648          *
2649          * @static
2650          * @param {DomForm} form element
2651          * @return {String} urlencode form output.
2652          */
2653         serializeForm : function(form) {
2654             if(typeof form == 'string') {
2655                 form = (document.getElementById(form) || document.forms[form]);
2656             }
2657
2658             var el, name, val, disabled, data = '', hasSubmit = false;
2659             for (var i = 0; i < form.elements.length; i++) {
2660                 el = form.elements[i];
2661                 disabled = form.elements[i].disabled;
2662                 name = form.elements[i].name;
2663                 val = form.elements[i].value;
2664
2665                 if (!disabled && name){
2666                     switch (el.type)
2667                             {
2668                         case 'select-one':
2669                         case 'select-multiple':
2670                             for (var j = 0; j < el.options.length; j++) {
2671                                 if (el.options[j].selected) {
2672                                     if (Roo.isIE) {
2673                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2674                                     }
2675                                     else {
2676                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2677                                     }
2678                                 }
2679                             }
2680                             break;
2681                         case 'radio':
2682                         case 'checkbox':
2683                             if (el.checked) {
2684                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2685                             }
2686                             break;
2687                         case 'file':
2688
2689                         case undefined:
2690
2691                         case 'reset':
2692
2693                         case 'button':
2694
2695                             break;
2696                         case 'submit':
2697                             if(hasSubmit == false) {
2698                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2699                                 hasSubmit = true;
2700                             }
2701                             break;
2702                         default:
2703                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             break;
2705                     }
2706                 }
2707             }
2708             data = data.substr(0, data.length - 1);
2709             return data;
2710         },
2711
2712         headers:{},
2713
2714         hasHeaders:false,
2715
2716         useDefaultHeader:true,
2717
2718         defaultPostHeader:'application/x-www-form-urlencoded',
2719
2720         useDefaultXhrHeader:true,
2721
2722         defaultXhrHeader:'XMLHttpRequest',
2723
2724         hasDefaultHeaders:true,
2725
2726         defaultHeaders:{},
2727
2728         poll:{},
2729
2730         timeout:{},
2731
2732         pollInterval:50,
2733
2734         transactionId:0,
2735
2736         setProgId:function(id)
2737         {
2738             this.activeX.unshift(id);
2739         },
2740
2741         setDefaultPostHeader:function(b)
2742         {
2743             this.useDefaultHeader = b;
2744         },
2745
2746         setDefaultXhrHeader:function(b)
2747         {
2748             this.useDefaultXhrHeader = b;
2749         },
2750
2751         setPollingInterval:function(i)
2752         {
2753             if (typeof i == 'number' && isFinite(i)) {
2754                 this.pollInterval = i;
2755             }
2756         },
2757
2758         createXhrObject:function(transactionId)
2759         {
2760             var obj,http;
2761             try
2762             {
2763
2764                 http = new XMLHttpRequest();
2765
2766                 obj = { conn:http, tId:transactionId };
2767             }
2768             catch(e)
2769             {
2770                 for (var i = 0; i < this.activeX.length; ++i) {
2771                     try
2772                     {
2773
2774                         http = new ActiveXObject(this.activeX[i]);
2775
2776                         obj = { conn:http, tId:transactionId };
2777                         break;
2778                     }
2779                     catch(e) {
2780                     }
2781                 }
2782             }
2783             finally
2784             {
2785                 return obj;
2786             }
2787         },
2788
2789         getConnectionObject:function()
2790         {
2791             var o;
2792             var tId = this.transactionId;
2793
2794             try
2795             {
2796                 o = this.createXhrObject(tId);
2797                 if (o) {
2798                     this.transactionId++;
2799                 }
2800             }
2801             catch(e) {
2802             }
2803             finally
2804             {
2805                 return o;
2806             }
2807         },
2808
2809         asyncRequest:function(method, uri, callback, postData)
2810         {
2811             var o = this.getConnectionObject();
2812
2813             if (!o) {
2814                 return null;
2815             }
2816             else {
2817                 o.conn.open(method, uri, true);
2818
2819                 if (this.useDefaultXhrHeader) {
2820                     if (!this.defaultHeaders['X-Requested-With']) {
2821                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2822                     }
2823                 }
2824
2825                 if(postData && this.useDefaultHeader){
2826                     this.initHeader('Content-Type', this.defaultPostHeader);
2827                 }
2828
2829                  if (this.hasDefaultHeaders || this.hasHeaders) {
2830                     this.setHeader(o);
2831                 }
2832
2833                 this.handleReadyState(o, callback);
2834                 o.conn.send(postData || null);
2835
2836                 return o;
2837             }
2838         },
2839
2840         handleReadyState:function(o, callback)
2841         {
2842             var oConn = this;
2843
2844             if (callback && callback.timeout) {
2845                 
2846                 this.timeout[o.tId] = window.setTimeout(function() {
2847                     oConn.abort(o, callback, true);
2848                 }, callback.timeout);
2849             }
2850
2851             this.poll[o.tId] = window.setInterval(
2852                     function() {
2853                         if (o.conn && o.conn.readyState == 4) {
2854                             window.clearInterval(oConn.poll[o.tId]);
2855                             delete oConn.poll[o.tId];
2856
2857                             if(callback && callback.timeout) {
2858                                 window.clearTimeout(oConn.timeout[o.tId]);
2859                                 delete oConn.timeout[o.tId];
2860                             }
2861
2862                             oConn.handleTransactionResponse(o, callback);
2863                         }
2864                     }
2865                     , this.pollInterval);
2866         },
2867
2868         handleTransactionResponse:function(o, callback, isAbort)
2869         {
2870
2871             if (!callback) {
2872                 this.releaseObject(o);
2873                 return;
2874             }
2875
2876             var httpStatus, responseObject;
2877
2878             try
2879             {
2880                 if (o.conn.status !== undefined && o.conn.status != 0) {
2881                     httpStatus = o.conn.status;
2882                 }
2883                 else {
2884                     httpStatus = 13030;
2885                 }
2886             }
2887             catch(e) {
2888
2889
2890                 httpStatus = 13030;
2891             }
2892
2893             if (httpStatus >= 200 && httpStatus < 300) {
2894                 responseObject = this.createResponseObject(o, callback.argument);
2895                 if (callback.success) {
2896                     if (!callback.scope) {
2897                         callback.success(responseObject);
2898                     }
2899                     else {
2900
2901
2902                         callback.success.apply(callback.scope, [responseObject]);
2903                     }
2904                 }
2905             }
2906             else {
2907                 switch (httpStatus) {
2908
2909                     case 12002:
2910                     case 12029:
2911                     case 12030:
2912                     case 12031:
2913                     case 12152:
2914                     case 13030:
2915                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
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                         break;
2925                     default:
2926                         responseObject = this.createResponseObject(o, callback.argument);
2927                         if (callback.failure) {
2928                             if (!callback.scope) {
2929                                 callback.failure(responseObject);
2930                             }
2931                             else {
2932                                 callback.failure.apply(callback.scope, [responseObject]);
2933                             }
2934                         }
2935                 }
2936             }
2937
2938             this.releaseObject(o);
2939             responseObject = null;
2940         },
2941
2942         createResponseObject:function(o, callbackArg)
2943         {
2944             var obj = {};
2945             var headerObj = {};
2946
2947             try
2948             {
2949                 var headerStr = o.conn.getAllResponseHeaders();
2950                 var header = headerStr.split('\n');
2951                 for (var i = 0; i < header.length; i++) {
2952                     var delimitPos = header[i].indexOf(':');
2953                     if (delimitPos != -1) {
2954                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2955                     }
2956                 }
2957             }
2958             catch(e) {
2959             }
2960
2961             obj.tId = o.tId;
2962             obj.status = o.conn.status;
2963             obj.statusText = o.conn.statusText;
2964             obj.getResponseHeader = headerObj;
2965             obj.getAllResponseHeaders = headerStr;
2966             obj.responseText = o.conn.responseText;
2967             obj.responseXML = o.conn.responseXML;
2968
2969             if (typeof callbackArg !== undefined) {
2970                 obj.argument = callbackArg;
2971             }
2972
2973             return obj;
2974         },
2975
2976         createExceptionObject:function(tId, callbackArg, isAbort)
2977         {
2978             var COMM_CODE = 0;
2979             var COMM_ERROR = 'communication failure';
2980             var ABORT_CODE = -1;
2981             var ABORT_ERROR = 'transaction aborted';
2982
2983             var obj = {};
2984
2985             obj.tId = tId;
2986             if (isAbort) {
2987                 obj.status = ABORT_CODE;
2988                 obj.statusText = ABORT_ERROR;
2989             }
2990             else {
2991                 obj.status = COMM_CODE;
2992                 obj.statusText = COMM_ERROR;
2993             }
2994
2995             if (callbackArg) {
2996                 obj.argument = callbackArg;
2997             }
2998
2999             return obj;
3000         },
3001
3002         initHeader:function(label, value, isDefault)
3003         {
3004             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3005
3006             if (headerObj[label] === undefined) {
3007                 headerObj[label] = value;
3008             }
3009             else {
3010
3011
3012                 headerObj[label] = value + "," + headerObj[label];
3013             }
3014
3015             if (isDefault) {
3016                 this.hasDefaultHeaders = true;
3017             }
3018             else {
3019                 this.hasHeaders = true;
3020             }
3021         },
3022
3023
3024         setHeader:function(o)
3025         {
3026             if (this.hasDefaultHeaders) {
3027                 for (var prop in this.defaultHeaders) {
3028                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3029                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3030                     }
3031                 }
3032             }
3033
3034             if (this.hasHeaders) {
3035                 for (var prop in this.headers) {
3036                     if (this.headers.hasOwnProperty(prop)) {
3037                         o.conn.setRequestHeader(prop, this.headers[prop]);
3038                     }
3039                 }
3040                 this.headers = {};
3041                 this.hasHeaders = false;
3042             }
3043         },
3044
3045         resetDefaultHeaders:function() {
3046             delete this.defaultHeaders;
3047             this.defaultHeaders = {};
3048             this.hasDefaultHeaders = false;
3049         },
3050
3051         abort:function(o, callback, isTimeout)
3052         {
3053             if(this.isCallInProgress(o)) {
3054                 o.conn.abort();
3055                 window.clearInterval(this.poll[o.tId]);
3056                 delete this.poll[o.tId];
3057                 if (isTimeout) {
3058                     delete this.timeout[o.tId];
3059                 }
3060
3061                 this.handleTransactionResponse(o, callback, true);
3062
3063                 return true;
3064             }
3065             else {
3066                 return false;
3067             }
3068         },
3069
3070
3071         isCallInProgress:function(o)
3072         {
3073             if (o && o.conn) {
3074                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3075             }
3076             else {
3077
3078                 return false;
3079             }
3080         },
3081
3082
3083         releaseObject:function(o)
3084         {
3085
3086             o.conn = null;
3087
3088             o = null;
3089         },
3090
3091         activeX:[
3092         'MSXML2.XMLHTTP.3.0',
3093         'MSXML2.XMLHTTP',
3094         'Microsoft.XMLHTTP'
3095         ]
3096
3097
3098     };
3099 })();/*
3100  * Portions of this file are based on pieces of Yahoo User Interface Library
3101  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3102  * YUI licensed under the BSD License:
3103  * http://developer.yahoo.net/yui/license.txt
3104  * <script type="text/javascript">
3105  *
3106  */
3107
3108 Roo.lib.Region = function(t, r, b, l) {
3109     this.top = t;
3110     this[1] = t;
3111     this.right = r;
3112     this.bottom = b;
3113     this.left = l;
3114     this[0] = l;
3115 };
3116
3117
3118 Roo.lib.Region.prototype = {
3119     contains : function(region) {
3120         return ( region.left >= this.left &&
3121                  region.right <= this.right &&
3122                  region.top >= this.top &&
3123                  region.bottom <= this.bottom    );
3124
3125     },
3126
3127     getArea : function() {
3128         return ( (this.bottom - this.top) * (this.right - this.left) );
3129     },
3130
3131     intersect : function(region) {
3132         var t = Math.max(this.top, region.top);
3133         var r = Math.min(this.right, region.right);
3134         var b = Math.min(this.bottom, region.bottom);
3135         var l = Math.max(this.left, region.left);
3136
3137         if (b >= t && r >= l) {
3138             return new Roo.lib.Region(t, r, b, l);
3139         } else {
3140             return null;
3141         }
3142     },
3143     union : function(region) {
3144         var t = Math.min(this.top, region.top);
3145         var r = Math.max(this.right, region.right);
3146         var b = Math.max(this.bottom, region.bottom);
3147         var l = Math.min(this.left, region.left);
3148
3149         return new Roo.lib.Region(t, r, b, l);
3150     },
3151
3152     adjust : function(t, l, b, r) {
3153         this.top += t;
3154         this.left += l;
3155         this.right += r;
3156         this.bottom += b;
3157         return this;
3158     }
3159 };
3160
3161 Roo.lib.Region.getRegion = function(el) {
3162     var p = Roo.lib.Dom.getXY(el);
3163
3164     var t = p[1];
3165     var r = p[0] + el.offsetWidth;
3166     var b = p[1] + el.offsetHeight;
3167     var l = p[0];
3168
3169     return new Roo.lib.Region(t, r, b, l);
3170 };
3171 /*
3172  * Portions of this file are based on pieces of Yahoo User Interface Library
3173  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3174  * YUI licensed under the BSD License:
3175  * http://developer.yahoo.net/yui/license.txt
3176  * <script type="text/javascript">
3177  *
3178  */
3179 //@@dep Roo.lib.Region
3180
3181
3182 Roo.lib.Point = function(x, y) {
3183     if (x instanceof Array) {
3184         y = x[1];
3185         x = x[0];
3186     }
3187     this.x = this.right = this.left = this[0] = x;
3188     this.y = this.top = this.bottom = this[1] = y;
3189 };
3190
3191 Roo.lib.Point.prototype = new Roo.lib.Region();
3192 /*
3193  * Portions of this file are based on pieces of Yahoo User Interface Library
3194  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3195  * YUI licensed under the BSD License:
3196  * http://developer.yahoo.net/yui/license.txt
3197  * <script type="text/javascript">
3198  *
3199  */
3200  
3201 (function() {   
3202
3203     Roo.lib.Anim = {
3204         scroll : function(el, args, duration, easing, cb, scope) {
3205             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3206         },
3207
3208         motion : function(el, args, duration, easing, cb, scope) {
3209             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3210         },
3211
3212         color : function(el, args, duration, easing, cb, scope) {
3213             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3214         },
3215
3216         run : function(el, args, duration, easing, cb, scope, type) {
3217             type = type || Roo.lib.AnimBase;
3218             if (typeof easing == "string") {
3219                 easing = Roo.lib.Easing[easing];
3220             }
3221             var anim = new type(el, args, duration, easing);
3222             anim.animateX(function() {
3223                 Roo.callback(cb, scope);
3224             });
3225             return anim;
3226         }
3227     };
3228 })();/*
3229  * Portions of this file are based on pieces of Yahoo User Interface Library
3230  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3231  * YUI licensed under the BSD License:
3232  * http://developer.yahoo.net/yui/license.txt
3233  * <script type="text/javascript">
3234  *
3235  */
3236
3237 (function() {    
3238     var libFlyweight;
3239     
3240     function fly(el) {
3241         if (!libFlyweight) {
3242             libFlyweight = new Roo.Element.Flyweight();
3243         }
3244         libFlyweight.dom = el;
3245         return libFlyweight;
3246     }
3247
3248     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3249     
3250    
3251     
3252     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3253         if (el) {
3254             this.init(el, attributes, duration, method);
3255         }
3256     };
3257
3258     Roo.lib.AnimBase.fly = fly;
3259     
3260     
3261     
3262     Roo.lib.AnimBase.prototype = {
3263
3264         toString: function() {
3265             var el = this.getEl();
3266             var id = el.id || el.tagName;
3267             return ("Anim " + id);
3268         },
3269
3270         patterns: {
3271             noNegatives:        /width|height|opacity|padding/i,
3272             offsetAttribute:  /^((width|height)|(top|left))$/,
3273             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3274             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3275         },
3276
3277
3278         doMethod: function(attr, start, end) {
3279             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3280         },
3281
3282
3283         setAttribute: function(attr, val, unit) {
3284             if (this.patterns.noNegatives.test(attr)) {
3285                 val = (val > 0) ? val : 0;
3286             }
3287
3288             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3289         },
3290
3291
3292         getAttribute: function(attr) {
3293             var el = this.getEl();
3294             var val = fly(el).getStyle(attr);
3295
3296             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3297                 return parseFloat(val);
3298             }
3299
3300             var a = this.patterns.offsetAttribute.exec(attr) || [];
3301             var pos = !!( a[3] );
3302             var box = !!( a[2] );
3303
3304
3305             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3306                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3307             } else {
3308                 val = 0;
3309             }
3310
3311             return val;
3312         },
3313
3314
3315         getDefaultUnit: function(attr) {
3316             if (this.patterns.defaultUnit.test(attr)) {
3317                 return 'px';
3318             }
3319
3320             return '';
3321         },
3322
3323         animateX : function(callback, scope) {
3324             var f = function() {
3325                 this.onComplete.removeListener(f);
3326                 if (typeof callback == "function") {
3327                     callback.call(scope || this, this);
3328                 }
3329             };
3330             this.onComplete.addListener(f, this);
3331             this.animate();
3332         },
3333
3334
3335         setRuntimeAttribute: function(attr) {
3336             var start;
3337             var end;
3338             var attributes = this.attributes;
3339
3340             this.runtimeAttributes[attr] = {};
3341
3342             var isset = function(prop) {
3343                 return (typeof prop !== 'undefined');
3344             };
3345
3346             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3347                 return false;
3348             }
3349
3350             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3351
3352
3353             if (isset(attributes[attr]['to'])) {
3354                 end = attributes[attr]['to'];
3355             } else if (isset(attributes[attr]['by'])) {
3356                 if (start.constructor == Array) {
3357                     end = [];
3358                     for (var i = 0, len = start.length; i < len; ++i) {
3359                         end[i] = start[i] + attributes[attr]['by'][i];
3360                     }
3361                 } else {
3362                     end = start + attributes[attr]['by'];
3363                 }
3364             }
3365
3366             this.runtimeAttributes[attr].start = start;
3367             this.runtimeAttributes[attr].end = end;
3368
3369
3370             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3371         },
3372
3373
3374         init: function(el, attributes, duration, method) {
3375
3376             var isAnimated = false;
3377
3378
3379             var startTime = null;
3380
3381
3382             var actualFrames = 0;
3383
3384
3385             el = Roo.getDom(el);
3386
3387
3388             this.attributes = attributes || {};
3389
3390
3391             this.duration = duration || 1;
3392
3393
3394             this.method = method || Roo.lib.Easing.easeNone;
3395
3396
3397             this.useSeconds = true;
3398
3399
3400             this.currentFrame = 0;
3401
3402
3403             this.totalFrames = Roo.lib.AnimMgr.fps;
3404
3405
3406             this.getEl = function() {
3407                 return el;
3408             };
3409
3410
3411             this.isAnimated = function() {
3412                 return isAnimated;
3413             };
3414
3415
3416             this.getStartTime = function() {
3417                 return startTime;
3418             };
3419
3420             this.runtimeAttributes = {};
3421
3422
3423             this.animate = function() {
3424                 if (this.isAnimated()) {
3425                     return false;
3426                 }
3427
3428                 this.currentFrame = 0;
3429
3430                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3431
3432                 Roo.lib.AnimMgr.registerElement(this);
3433             };
3434
3435
3436             this.stop = function(finish) {
3437                 if (finish) {
3438                     this.currentFrame = this.totalFrames;
3439                     this._onTween.fire();
3440                 }
3441                 Roo.lib.AnimMgr.stop(this);
3442             };
3443
3444             var onStart = function() {
3445                 this.onStart.fire();
3446
3447                 this.runtimeAttributes = {};
3448                 for (var attr in this.attributes) {
3449                     this.setRuntimeAttribute(attr);
3450                 }
3451
3452                 isAnimated = true;
3453                 actualFrames = 0;
3454                 startTime = new Date();
3455             };
3456
3457
3458             var onTween = function() {
3459                 var data = {
3460                     duration: new Date() - this.getStartTime(),
3461                     currentFrame: this.currentFrame
3462                 };
3463
3464                 data.toString = function() {
3465                     return (
3466                             'duration: ' + data.duration +
3467                             ', currentFrame: ' + data.currentFrame
3468                             );
3469                 };
3470
3471                 this.onTween.fire(data);
3472
3473                 var runtimeAttributes = this.runtimeAttributes;
3474
3475                 for (var attr in runtimeAttributes) {
3476                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3477                 }
3478
3479                 actualFrames += 1;
3480             };
3481
3482             var onComplete = function() {
3483                 var actual_duration = (new Date() - startTime) / 1000 ;
3484
3485                 var data = {
3486                     duration: actual_duration,
3487                     frames: actualFrames,
3488                     fps: actualFrames / actual_duration
3489                 };
3490
3491                 data.toString = function() {
3492                     return (
3493                             'duration: ' + data.duration +
3494                             ', frames: ' + data.frames +
3495                             ', fps: ' + data.fps
3496                             );
3497                 };
3498
3499                 isAnimated = false;
3500                 actualFrames = 0;
3501                 this.onComplete.fire(data);
3502             };
3503
3504
3505             this._onStart = new Roo.util.Event(this);
3506             this.onStart = new Roo.util.Event(this);
3507             this.onTween = new Roo.util.Event(this);
3508             this._onTween = new Roo.util.Event(this);
3509             this.onComplete = new Roo.util.Event(this);
3510             this._onComplete = new Roo.util.Event(this);
3511             this._onStart.addListener(onStart);
3512             this._onTween.addListener(onTween);
3513             this._onComplete.addListener(onComplete);
3514         }
3515     };
3516 })();
3517 /*
3518  * Portions of this file are based on pieces of Yahoo User Interface Library
3519  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3520  * YUI licensed under the BSD License:
3521  * http://developer.yahoo.net/yui/license.txt
3522  * <script type="text/javascript">
3523  *
3524  */
3525
3526 Roo.lib.AnimMgr = new function() {
3527
3528     var thread = null;
3529
3530
3531     var queue = [];
3532
3533
3534     var tweenCount = 0;
3535
3536
3537     this.fps = 1000;
3538
3539
3540     this.delay = 1;
3541
3542
3543     this.registerElement = function(tween) {
3544         queue[queue.length] = tween;
3545         tweenCount += 1;
3546         tween._onStart.fire();
3547         this.start();
3548     };
3549
3550
3551     this.unRegister = function(tween, index) {
3552         tween._onComplete.fire();
3553         index = index || getIndex(tween);
3554         if (index != -1) {
3555             queue.splice(index, 1);
3556         }
3557
3558         tweenCount -= 1;
3559         if (tweenCount <= 0) {
3560             this.stop();
3561         }
3562     };
3563
3564
3565     this.start = function() {
3566         if (thread === null) {
3567             thread = setInterval(this.run, this.delay);
3568         }
3569     };
3570
3571
3572     this.stop = function(tween) {
3573         if (!tween) {
3574             clearInterval(thread);
3575
3576             for (var i = 0, len = queue.length; i < len; ++i) {
3577                 if (queue[0].isAnimated()) {
3578                     this.unRegister(queue[0], 0);
3579                 }
3580             }
3581
3582             queue = [];
3583             thread = null;
3584             tweenCount = 0;
3585         }
3586         else {
3587             this.unRegister(tween);
3588         }
3589     };
3590
3591
3592     this.run = function() {
3593         for (var i = 0, len = queue.length; i < len; ++i) {
3594             var tween = queue[i];
3595             if (!tween || !tween.isAnimated()) {
3596                 continue;
3597             }
3598
3599             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3600             {
3601                 tween.currentFrame += 1;
3602
3603                 if (tween.useSeconds) {
3604                     correctFrame(tween);
3605                 }
3606                 tween._onTween.fire();
3607             }
3608             else {
3609                 Roo.lib.AnimMgr.stop(tween, i);
3610             }
3611         }
3612     };
3613
3614     var getIndex = function(anim) {
3615         for (var i = 0, len = queue.length; i < len; ++i) {
3616             if (queue[i] == anim) {
3617                 return i;
3618             }
3619         }
3620         return -1;
3621     };
3622
3623
3624     var correctFrame = function(tween) {
3625         var frames = tween.totalFrames;
3626         var frame = tween.currentFrame;
3627         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3628         var elapsed = (new Date() - tween.getStartTime());
3629         var tweak = 0;
3630
3631         if (elapsed < tween.duration * 1000) {
3632             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3633         } else {
3634             tweak = frames - (frame + 1);
3635         }
3636         if (tweak > 0 && isFinite(tweak)) {
3637             if (tween.currentFrame + tweak >= frames) {
3638                 tweak = frames - (frame + 1);
3639             }
3640
3641             tween.currentFrame += tweak;
3642         }
3643     };
3644 };
3645
3646     /*
3647  * Portions of this file are based on pieces of Yahoo User Interface Library
3648  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3649  * YUI licensed under the BSD License:
3650  * http://developer.yahoo.net/yui/license.txt
3651  * <script type="text/javascript">
3652  *
3653  */
3654 Roo.lib.Bezier = new function() {
3655
3656         this.getPosition = function(points, t) {
3657             var n = points.length;
3658             var tmp = [];
3659
3660             for (var i = 0; i < n; ++i) {
3661                 tmp[i] = [points[i][0], points[i][1]];
3662             }
3663
3664             for (var j = 1; j < n; ++j) {
3665                 for (i = 0; i < n - j; ++i) {
3666                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3667                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3668                 }
3669             }
3670
3671             return [ tmp[0][0], tmp[0][1] ];
3672
3673         };
3674     }; 
3675
3676 /**
3677  * @class Roo.lib.Color
3678  * @constructor
3679  * An abstract Color implementation. Concrete Color implementations should use
3680  * an instance of this function as their prototype, and implement the getRGB and
3681  * getHSL functions. getRGB should return an object representing the RGB
3682  * components of this Color, with the red, green, and blue components in the
3683  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3684  * return an object representing the HSL components of this Color, with the hue
3685  * component in the range [0,360), the saturation and lightness components in
3686  * the range [0,100], and the alpha component in the range [0,1].
3687  *
3688  *
3689  * Color.js
3690  *
3691  * Functions for Color handling and processing.
3692  *
3693  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3694  *
3695  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3696  * rights to this program, with the intention of it becoming part of the public
3697  * domain. Because this program is released into the public domain, it comes with
3698  * no warranty either expressed or implied, to the extent permitted by law.
3699  * 
3700  * For more free and public domain JavaScript code by the same author, visit:
3701  * http://www.safalra.com/web-design/javascript/
3702  * 
3703  */
3704 Roo.lib.Color = function() { }
3705
3706
3707 Roo.apply(Roo.lib.Color.prototype, {
3708   
3709   rgb : null,
3710   hsv : null,
3711   hsl : null,
3712   
3713   /**
3714    * getIntegerRGB
3715    * @return {Object} an object representing the RGBA components of this Color. The red,
3716    * green, and blue components are converted to integers in the range [0,255].
3717    * The alpha is a value in the range [0,1].
3718    */
3719   getIntegerRGB : function(){
3720
3721     // get the RGB components of this Color
3722     var rgb = this.getRGB();
3723
3724     // return the integer components
3725     return {
3726       'r' : Math.round(rgb.r),
3727       'g' : Math.round(rgb.g),
3728       'b' : Math.round(rgb.b),
3729       'a' : rgb.a
3730     };
3731
3732   },
3733
3734   /**
3735    * getPercentageRGB
3736    * @return {Object} an object representing the RGBA components of this Color. The red,
3737    * green, and blue components are converted to numbers in the range [0,100].
3738    * The alpha is a value in the range [0,1].
3739    */
3740   getPercentageRGB : function(){
3741
3742     // get the RGB components of this Color
3743     var rgb = this.getRGB();
3744
3745     // return the percentage components
3746     return {
3747       'r' : 100 * rgb.r / 255,
3748       'g' : 100 * rgb.g / 255,
3749       'b' : 100 * rgb.b / 255,
3750       'a' : rgb.a
3751     };
3752
3753   },
3754
3755   /**
3756    * getCSSHexadecimalRGB
3757    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3758    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3759    * are two-digit hexadecimal numbers.
3760    */
3761   getCSSHexadecimalRGB : function()
3762   {
3763
3764     // get the integer RGB components
3765     var rgb = this.getIntegerRGB();
3766
3767     // determine the hexadecimal equivalents
3768     var r16 = rgb.r.toString(16);
3769     var g16 = rgb.g.toString(16);
3770     var b16 = rgb.b.toString(16);
3771
3772     // return the CSS RGB Color value
3773     return '#'
3774         + (r16.length == 2 ? r16 : '0' + r16)
3775         + (g16.length == 2 ? g16 : '0' + g16)
3776         + (b16.length == 2 ? b16 : '0' + b16);
3777
3778   },
3779
3780   /**
3781    * getCSSIntegerRGB
3782    * @return {String} a string representing this Color as a CSS integer RGB Color
3783    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3784    * are integers in the range [0,255].
3785    */
3786   getCSSIntegerRGB : function(){
3787
3788     // get the integer RGB components
3789     var rgb = this.getIntegerRGB();
3790
3791     // return the CSS RGB Color value
3792     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3793
3794   },
3795
3796   /**
3797    * getCSSIntegerRGBA
3798    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3799    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3800    * b are integers in the range [0,255] and a is in the range [0,1].
3801    */
3802   getCSSIntegerRGBA : function(){
3803
3804     // get the integer RGB components
3805     var rgb = this.getIntegerRGB();
3806
3807     // return the CSS integer RGBA Color value
3808     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3809
3810   },
3811
3812   /**
3813    * getCSSPercentageRGB
3814    * @return {String} a string representing this Color as a CSS percentage RGB Color
3815    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3816    * b are in the range [0,100].
3817    */
3818   getCSSPercentageRGB : function(){
3819
3820     // get the percentage RGB components
3821     var rgb = this.getPercentageRGB();
3822
3823     // return the CSS RGB Color value
3824     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3825
3826   },
3827
3828   /**
3829    * getCSSPercentageRGBA
3830    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3831    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3832    * and b are in the range [0,100] and a is in the range [0,1].
3833    */
3834   getCSSPercentageRGBA : function(){
3835
3836     // get the percentage RGB components
3837     var rgb = this.getPercentageRGB();
3838
3839     // return the CSS percentage RGBA Color value
3840     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3841
3842   },
3843
3844   /**
3845    * getCSSHSL
3846    * @return {String} a string representing this Color as a CSS HSL Color value - that
3847    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3848    * s and l are in the range [0,100].
3849    */
3850   getCSSHSL : function(){
3851
3852     // get the HSL components
3853     var hsl = this.getHSL();
3854
3855     // return the CSS HSL Color value
3856     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3857
3858   },
3859
3860   /**
3861    * getCSSHSLA
3862    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3863    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3864    * s and l are in the range [0,100], and a is in the range [0,1].
3865    */
3866   getCSSHSLA : function(){
3867
3868     // get the HSL components
3869     var hsl = this.getHSL();
3870
3871     // return the CSS HSL Color value
3872     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3873
3874   },
3875
3876   /**
3877    * Sets the Color of the specified node to this Color. This functions sets
3878    * the CSS 'color' property for the node. The parameter is:
3879    * 
3880    * @param {DomElement} node - the node whose Color should be set
3881    */
3882   setNodeColor : function(node){
3883
3884     // set the Color of the node
3885     node.style.color = this.getCSSHexadecimalRGB();
3886
3887   },
3888
3889   /**
3890    * Sets the background Color of the specified node to this Color. This
3891    * functions sets the CSS 'background-color' property for the node. The
3892    * parameter is:
3893    *
3894    * @param {DomElement} node - the node whose background Color should be set
3895    */
3896   setNodeBackgroundColor : function(node){
3897
3898     // set the background Color of the node
3899     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3900
3901   },
3902   // convert between formats..
3903   toRGB: function()
3904   {
3905     var r = this.getIntegerRGB();
3906     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3907     
3908   },
3909   toHSL : function()
3910   {
3911      var hsl = this.getHSL();
3912   // return the CSS HSL Color value
3913     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3914     
3915   },
3916   
3917   toHSV : function()
3918   {
3919     var rgb = this.toRGB();
3920     var hsv = rgb.getHSV();
3921    // return the CSS HSL Color value
3922     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3923     
3924   },
3925   
3926   // modify  v = 0 ... 1 (eg. 0.5)
3927   saturate : function(v)
3928   {
3929       var rgb = this.toRGB();
3930       var hsv = rgb.getHSV();
3931       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3932       
3933   
3934   },
3935   
3936    
3937   /**
3938    * getRGB
3939    * @return {Object} the RGB and alpha components of this Color as an object with r,
3940    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3941    * the range [0,1].
3942    */
3943   getRGB: function(){
3944    
3945     // return the RGB components
3946     return {
3947       'r' : this.rgb.r,
3948       'g' : this.rgb.g,
3949       'b' : this.rgb.b,
3950       'a' : this.alpha
3951     };
3952
3953   },
3954
3955   /**
3956    * getHSV
3957    * @return {Object} the HSV and alpha components of this Color as an object with h,
3958    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3959    * [0,100], and a is in the range [0,1].
3960    */
3961   getHSV : function()
3962   {
3963     
3964     // calculate the HSV components if necessary
3965     if (this.hsv == null) {
3966       this.calculateHSV();
3967     }
3968
3969     // return the HSV components
3970     return {
3971       'h' : this.hsv.h,
3972       's' : this.hsv.s,
3973       'v' : this.hsv.v,
3974       'a' : this.alpha
3975     };
3976
3977   },
3978
3979   /**
3980    * getHSL
3981    * @return {Object} the HSL and alpha components of this Color as an object with h,
3982    * s, l, and a properties. h is in the range [0,360), s and l are in the range
3983    * [0,100], and a is in the range [0,1].
3984    */
3985   getHSL : function(){
3986     
3987      
3988     // calculate the HSV components if necessary
3989     if (this.hsl == null) { this.calculateHSL(); }
3990
3991     // return the HSL components
3992     return {
3993       'h' : this.hsl.h,
3994       's' : this.hsl.s,
3995       'l' : this.hsl.l,
3996       'a' : this.alpha
3997     };
3998
3999   }
4000   
4001
4002 });
4003
4004
4005 /**
4006  * @class Roo.lib.RGBColor
4007  * @extends Roo.lib.Color
4008  * Creates a Color specified in the RGB Color space, with an optional alpha
4009  * component. The parameters are:
4010  * @constructor
4011  * 
4012
4013  * @param {Number} r - the red component, clipped to the range [0,255]
4014  * @param {Number} g - the green component, clipped to the range [0,255]
4015  * @param {Number} b - the blue component, clipped to the range [0,255]
4016  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4017  *     optional and defaults to 1
4018  */
4019 Roo.lib.RGBColor = function (r, g, b, a){
4020
4021   // store the alpha component after clipping it if necessary
4022   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4023
4024   // store the RGB components after clipping them if necessary
4025   this.rgb =
4026       {
4027         'r' : Math.max(0, Math.min(255, r)),
4028         'g' : Math.max(0, Math.min(255, g)),
4029         'b' : Math.max(0, Math.min(255, b))
4030       };
4031
4032   // initialise the HSV and HSL components to null
4033   
4034
4035   /* 
4036    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4037    * range [0,360). The parameters are:
4038    *
4039    * maximum - the maximum of the RGB component values
4040    * range   - the range of the RGB component values
4041    */
4042    
4043
4044 }
4045 // this does an 'exteds'
4046 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4047
4048   
4049     getHue  : function(maximum, range)
4050     {
4051       var rgb = this.rgb;
4052        
4053       // check whether the range is zero
4054       if (range == 0){
4055   
4056         // set the hue to zero (any hue is acceptable as the Color is grey)
4057         var hue = 0;
4058   
4059       }else{
4060   
4061         // determine which of the components has the highest value and set the hue
4062         switch (maximum){
4063   
4064           // red has the highest value
4065           case rgb.r:
4066             var hue = (rgb.g - rgb.b) / range * 60;
4067             if (hue < 0) { hue += 360; }
4068             break;
4069   
4070           // green has the highest value
4071           case rgb.g:
4072             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4073             break;
4074   
4075           // blue has the highest value
4076           case rgb.b:
4077             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4078             break;
4079   
4080         }
4081   
4082       }
4083   
4084       // return the hue
4085       return hue;
4086   
4087     },
4088
4089   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4090    * be returned be the getHSV function.
4091    */
4092    calculateHSV : function(){
4093     var rgb = this.rgb;
4094     // get the maximum and range of the RGB component values
4095     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4096     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4097
4098     // store the HSV components
4099     this.hsv =
4100         {
4101           'h' : this.getHue(maximum, range),
4102           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4103           'v' : maximum / 2.55
4104         };
4105
4106   },
4107
4108   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4109    * be returned be the getHSL function.
4110    */
4111    calculateHSL : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // determine the lightness in the range [0,1]
4118     var l = maximum / 255 - range / 510;
4119
4120     // store the HSL components
4121     this.hsl =
4122         {
4123           'h' : this.getHue(maximum, range),
4124           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4125           'l' : 100 * l
4126         };
4127
4128   }
4129
4130 });
4131
4132 /**
4133  * @class Roo.lib.HSVColor
4134  * @extends Roo.lib.Color
4135  * Creates a Color specified in the HSV Color space, with an optional alpha
4136  * component. The parameters are:
4137  * @constructor
4138  *
4139  * @param {Number} h - the hue component, wrapped to the range [0,360)
4140  * @param {Number} s - the saturation component, clipped to the range [0,100]
4141  * @param {Number} v - the value component, clipped to the range [0,100]
4142  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4143  *     optional and defaults to 1
4144  */
4145 Roo.lib.HSVColor = function (h, s, v, a){
4146
4147   // store the alpha component after clipping it if necessary
4148   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4149
4150   // store the HSV components after clipping or wrapping them if necessary
4151   this.hsv =
4152       {
4153         'h' : (h % 360 + 360) % 360,
4154         's' : Math.max(0, Math.min(100, s)),
4155         'v' : Math.max(0, Math.min(100, v))
4156       };
4157
4158   // initialise the RGB and HSL components to null
4159   this.rgb = null;
4160   this.hsl = null;
4161 }
4162
4163 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4164   /* Calculates and stores the RGB components of this HSVColor so that they can
4165    * be returned be the getRGB function.
4166    */
4167   calculateRGB: function ()
4168   {
4169     var hsv = this.hsv;
4170     // check whether the saturation is zero
4171     if (hsv.s == 0){
4172
4173       // set the Color to the appropriate shade of grey
4174       var r = hsv.v;
4175       var g = hsv.v;
4176       var b = hsv.v;
4177
4178     }else{
4179
4180       // set some temporary values
4181       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4182       var p  = hsv.v * (1 - hsv.s / 100);
4183       var q  = hsv.v * (1 - hsv.s / 100 * f);
4184       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4185
4186       // set the RGB Color components to their temporary values
4187       switch (Math.floor(hsv.h / 60)){
4188         case 0: var r = hsv.v; var g = t; var b = p; break;
4189         case 1: var r = q; var g = hsv.v; var b = p; break;
4190         case 2: var r = p; var g = hsv.v; var b = t; break;
4191         case 3: var r = p; var g = q; var b = hsv.v; break;
4192         case 4: var r = t; var g = p; var b = hsv.v; break;
4193         case 5: var r = hsv.v; var g = p; var b = q; break;
4194       }
4195
4196     }
4197
4198     // store the RGB components
4199     this.rgb =
4200         {
4201           'r' : r * 2.55,
4202           'g' : g * 2.55,
4203           'b' : b * 2.55
4204         };
4205
4206   },
4207
4208   /* Calculates and stores the HSL components of this HSVColor so that they can
4209    * be returned be the getHSL function.
4210    */
4211   calculateHSL : function (){
4212
4213     var hsv = this.hsv;
4214     // determine the lightness in the range [0,100]
4215     var l = (2 - hsv.s / 100) * hsv.v / 2;
4216
4217     // store the HSL components
4218     this.hsl =
4219         {
4220           'h' : hsv.h,
4221           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4222           'l' : l
4223         };
4224
4225     // correct a division-by-zero error
4226     if (isNaN(hsl.s)) { hsl.s = 0; }
4227
4228   } 
4229  
4230
4231 });
4232  
4233
4234 /**
4235  * @class Roo.lib.HSLColor
4236  * @extends Roo.lib.Color
4237  *
4238  * @constructor
4239  * Creates a Color specified in the HSL Color space, with an optional alpha
4240  * component. The parameters are:
4241  *
4242  * @param {Number} h - the hue component, wrapped to the range [0,360)
4243  * @param {Number} s - the saturation component, clipped to the range [0,100]
4244  * @param {Number} l - the lightness component, clipped to the range [0,100]
4245  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4246  *     optional and defaults to 1
4247  */
4248
4249 Roo.lib.HSLColor = function(h, s, l, a){
4250
4251   // store the alpha component after clipping it if necessary
4252   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4253
4254   // store the HSL components after clipping or wrapping them if necessary
4255   this.hsl =
4256       {
4257         'h' : (h % 360 + 360) % 360,
4258         's' : Math.max(0, Math.min(100, s)),
4259         'l' : Math.max(0, Math.min(100, l))
4260       };
4261
4262   // initialise the RGB and HSV components to null
4263 }
4264
4265 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4266
4267   /* Calculates and stores the RGB components of this HSLColor so that they can
4268    * be returned be the getRGB function.
4269    */
4270   calculateRGB: function (){
4271
4272     // check whether the saturation is zero
4273     if (this.hsl.s == 0){
4274
4275       // store the RGB components representing the appropriate shade of grey
4276       this.rgb =
4277           {
4278             'r' : this.hsl.l * 2.55,
4279             'g' : this.hsl.l * 2.55,
4280             'b' : this.hsl.l * 2.55
4281           };
4282
4283     }else{
4284
4285       // set some temporary values
4286       var p = this.hsl.l < 50
4287             ? this.hsl.l * (1 + hsl.s / 100)
4288             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4289       var q = 2 * hsl.l - p;
4290
4291       // initialise the RGB components
4292       this.rgb =
4293           {
4294             'r' : (h + 120) / 60 % 6,
4295             'g' : h / 60,
4296             'b' : (h + 240) / 60 % 6
4297           };
4298
4299       // loop over the RGB components
4300       for (var key in this.rgb){
4301
4302         // ensure that the property is not inherited from the root object
4303         if (this.rgb.hasOwnProperty(key)){
4304
4305           // set the component to its value in the range [0,100]
4306           if (this.rgb[key] < 1){
4307             this.rgb[key] = q + (p - q) * this.rgb[key];
4308           }else if (this.rgb[key] < 3){
4309             this.rgb[key] = p;
4310           }else if (this.rgb[key] < 4){
4311             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4312           }else{
4313             this.rgb[key] = q;
4314           }
4315
4316           // set the component to its value in the range [0,255]
4317           this.rgb[key] *= 2.55;
4318
4319         }
4320
4321       }
4322
4323     }
4324
4325   },
4326
4327   /* Calculates and stores the HSV components of this HSLColor so that they can
4328    * be returned be the getHSL function.
4329    */
4330    calculateHSV : function(){
4331
4332     // set a temporary value
4333     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4334
4335     // store the HSV components
4336     this.hsv =
4337         {
4338           'h' : this.hsl.h,
4339           's' : 200 * t / (this.hsl.l + t),
4340           'v' : t + this.hsl.l
4341         };
4342
4343     // correct a division-by-zero error
4344     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4345
4346   }
4347  
4348
4349 });
4350 /*
4351  * Portions of this file are based on pieces of Yahoo User Interface Library
4352  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4353  * YUI licensed under the BSD License:
4354  * http://developer.yahoo.net/yui/license.txt
4355  * <script type="text/javascript">
4356  *
4357  */
4358 (function() {
4359
4360     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4361         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4362     };
4363
4364     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4365
4366     var fly = Roo.lib.AnimBase.fly;
4367     var Y = Roo.lib;
4368     var superclass = Y.ColorAnim.superclass;
4369     var proto = Y.ColorAnim.prototype;
4370
4371     proto.toString = function() {
4372         var el = this.getEl();
4373         var id = el.id || el.tagName;
4374         return ("ColorAnim " + id);
4375     };
4376
4377     proto.patterns.color = /color$/i;
4378     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4379     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4380     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4381     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4382
4383
4384     proto.parseColor = function(s) {
4385         if (s.length == 3) {
4386             return s;
4387         }
4388
4389         var c = this.patterns.hex.exec(s);
4390         if (c && c.length == 4) {
4391             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4392         }
4393
4394         c = this.patterns.rgb.exec(s);
4395         if (c && c.length == 4) {
4396             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4397         }
4398
4399         c = this.patterns.hex3.exec(s);
4400         if (c && c.length == 4) {
4401             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4402         }
4403
4404         return null;
4405     };
4406     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4407     proto.getAttribute = function(attr) {
4408         var el = this.getEl();
4409         if (this.patterns.color.test(attr)) {
4410             var val = fly(el).getStyle(attr);
4411
4412             if (this.patterns.transparent.test(val)) {
4413                 var parent = el.parentNode;
4414                 val = fly(parent).getStyle(attr);
4415
4416                 while (parent && this.patterns.transparent.test(val)) {
4417                     parent = parent.parentNode;
4418                     val = fly(parent).getStyle(attr);
4419                     if (parent.tagName.toUpperCase() == 'HTML') {
4420                         val = '#fff';
4421                     }
4422                 }
4423             }
4424         } else {
4425             val = superclass.getAttribute.call(this, attr);
4426         }
4427
4428         return val;
4429     };
4430     proto.getAttribute = function(attr) {
4431         var el = this.getEl();
4432         if (this.patterns.color.test(attr)) {
4433             var val = fly(el).getStyle(attr);
4434
4435             if (this.patterns.transparent.test(val)) {
4436                 var parent = el.parentNode;
4437                 val = fly(parent).getStyle(attr);
4438
4439                 while (parent && this.patterns.transparent.test(val)) {
4440                     parent = parent.parentNode;
4441                     val = fly(parent).getStyle(attr);
4442                     if (parent.tagName.toUpperCase() == 'HTML') {
4443                         val = '#fff';
4444                     }
4445                 }
4446             }
4447         } else {
4448             val = superclass.getAttribute.call(this, attr);
4449         }
4450
4451         return val;
4452     };
4453
4454     proto.doMethod = function(attr, start, end) {
4455         var val;
4456
4457         if (this.patterns.color.test(attr)) {
4458             val = [];
4459             for (var i = 0, len = start.length; i < len; ++i) {
4460                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4461             }
4462
4463             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4464         }
4465         else {
4466             val = superclass.doMethod.call(this, attr, start, end);
4467         }
4468
4469         return val;
4470     };
4471
4472     proto.setRuntimeAttribute = function(attr) {
4473         superclass.setRuntimeAttribute.call(this, attr);
4474
4475         if (this.patterns.color.test(attr)) {
4476             var attributes = this.attributes;
4477             var start = this.parseColor(this.runtimeAttributes[attr].start);
4478             var end = this.parseColor(this.runtimeAttributes[attr].end);
4479
4480             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4481                 end = this.parseColor(attributes[attr].by);
4482
4483                 for (var i = 0, len = start.length; i < len; ++i) {
4484                     end[i] = start[i] + end[i];
4485                 }
4486             }
4487
4488             this.runtimeAttributes[attr].start = start;
4489             this.runtimeAttributes[attr].end = end;
4490         }
4491     };
4492 })();
4493
4494 /*
4495  * Portions of this file are based on pieces of Yahoo User Interface Library
4496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4497  * YUI licensed under the BSD License:
4498  * http://developer.yahoo.net/yui/license.txt
4499  * <script type="text/javascript">
4500  *
4501  */
4502 Roo.lib.Easing = {
4503
4504
4505     easeNone: function (t, b, c, d) {
4506         return c * t / d + b;
4507     },
4508
4509
4510     easeIn: function (t, b, c, d) {
4511         return c * (t /= d) * t + b;
4512     },
4513
4514
4515     easeOut: function (t, b, c, d) {
4516         return -c * (t /= d) * (t - 2) + b;
4517     },
4518
4519
4520     easeBoth: function (t, b, c, d) {
4521         if ((t /= d / 2) < 1) {
4522             return c / 2 * t * t + b;
4523         }
4524
4525         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4526     },
4527
4528
4529     easeInStrong: function (t, b, c, d) {
4530         return c * (t /= d) * t * t * t + b;
4531     },
4532
4533
4534     easeOutStrong: function (t, b, c, d) {
4535         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4536     },
4537
4538
4539     easeBothStrong: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t * t * t + b;
4542         }
4543
4544         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4545     },
4546
4547
4548
4549     elasticIn: function (t, b, c, d, a, p) {
4550         if (t == 0) {
4551             return b;
4552         }
4553         if ((t /= d) == 1) {
4554             return b + c;
4555         }
4556         if (!p) {
4557             p = d * .3;
4558         }
4559
4560         if (!a || a < Math.abs(c)) {
4561             a = c;
4562             var s = p / 4;
4563         }
4564         else {
4565             var s = p / (2 * Math.PI) * Math.asin(c / a);
4566         }
4567
4568         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4569     },
4570
4571
4572     elasticOut: function (t, b, c, d, a, p) {
4573         if (t == 0) {
4574             return b;
4575         }
4576         if ((t /= d) == 1) {
4577             return b + c;
4578         }
4579         if (!p) {
4580             p = d * .3;
4581         }
4582
4583         if (!a || a < Math.abs(c)) {
4584             a = c;
4585             var s = p / 4;
4586         }
4587         else {
4588             var s = p / (2 * Math.PI) * Math.asin(c / a);
4589         }
4590
4591         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4592     },
4593
4594
4595     elasticBoth: function (t, b, c, d, a, p) {
4596         if (t == 0) {
4597             return b;
4598         }
4599
4600         if ((t /= d / 2) == 2) {
4601             return b + c;
4602         }
4603
4604         if (!p) {
4605             p = d * (.3 * 1.5);
4606         }
4607
4608         if (!a || a < Math.abs(c)) {
4609             a = c;
4610             var s = p / 4;
4611         }
4612         else {
4613             var s = p / (2 * Math.PI) * Math.asin(c / a);
4614         }
4615
4616         if (t < 1) {
4617             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4618                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4619         }
4620         return a * Math.pow(2, -10 * (t -= 1)) *
4621                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4622     },
4623
4624
4625
4626     backIn: function (t, b, c, d, s) {
4627         if (typeof s == 'undefined') {
4628             s = 1.70158;
4629         }
4630         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4631     },
4632
4633
4634     backOut: function (t, b, c, d, s) {
4635         if (typeof s == 'undefined') {
4636             s = 1.70158;
4637         }
4638         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4639     },
4640
4641
4642     backBoth: function (t, b, c, d, s) {
4643         if (typeof s == 'undefined') {
4644             s = 1.70158;
4645         }
4646
4647         if ((t /= d / 2 ) < 1) {
4648             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4649         }
4650         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4651     },
4652
4653
4654     bounceIn: function (t, b, c, d) {
4655         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4656     },
4657
4658
4659     bounceOut: function (t, b, c, d) {
4660         if ((t /= d) < (1 / 2.75)) {
4661             return c * (7.5625 * t * t) + b;
4662         } else if (t < (2 / 2.75)) {
4663             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4664         } else if (t < (2.5 / 2.75)) {
4665             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4666         }
4667         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4668     },
4669
4670
4671     bounceBoth: function (t, b, c, d) {
4672         if (t < d / 2) {
4673             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4674         }
4675         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4676     }
4677 };/*
4678  * Portions of this file are based on pieces of Yahoo User Interface Library
4679  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4680  * YUI licensed under the BSD License:
4681  * http://developer.yahoo.net/yui/license.txt
4682  * <script type="text/javascript">
4683  *
4684  */
4685     (function() {
4686         Roo.lib.Motion = function(el, attributes, duration, method) {
4687             if (el) {
4688                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4689             }
4690         };
4691
4692         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4693
4694
4695         var Y = Roo.lib;
4696         var superclass = Y.Motion.superclass;
4697         var proto = Y.Motion.prototype;
4698
4699         proto.toString = function() {
4700             var el = this.getEl();
4701             var id = el.id || el.tagName;
4702             return ("Motion " + id);
4703         };
4704
4705         proto.patterns.points = /^points$/i;
4706
4707         proto.setAttribute = function(attr, val, unit) {
4708             if (this.patterns.points.test(attr)) {
4709                 unit = unit || 'px';
4710                 superclass.setAttribute.call(this, 'left', val[0], unit);
4711                 superclass.setAttribute.call(this, 'top', val[1], unit);
4712             } else {
4713                 superclass.setAttribute.call(this, attr, val, unit);
4714             }
4715         };
4716
4717         proto.getAttribute = function(attr) {
4718             if (this.patterns.points.test(attr)) {
4719                 var val = [
4720                         superclass.getAttribute.call(this, 'left'),
4721                         superclass.getAttribute.call(this, 'top')
4722                         ];
4723             } else {
4724                 val = superclass.getAttribute.call(this, attr);
4725             }
4726
4727             return val;
4728         };
4729
4730         proto.doMethod = function(attr, start, end) {
4731             var val = null;
4732
4733             if (this.patterns.points.test(attr)) {
4734                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4735                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4736             } else {
4737                 val = superclass.doMethod.call(this, attr, start, end);
4738             }
4739             return val;
4740         };
4741
4742         proto.setRuntimeAttribute = function(attr) {
4743             if (this.patterns.points.test(attr)) {
4744                 var el = this.getEl();
4745                 var attributes = this.attributes;
4746                 var start;
4747                 var control = attributes['points']['control'] || [];
4748                 var end;
4749                 var i, len;
4750
4751                 if (control.length > 0 && !(control[0] instanceof Array)) {
4752                     control = [control];
4753                 } else {
4754                     var tmp = [];
4755                     for (i = 0,len = control.length; i < len; ++i) {
4756                         tmp[i] = control[i];
4757                     }
4758                     control = tmp;
4759                 }
4760
4761                 Roo.fly(el).position();
4762
4763                 if (isset(attributes['points']['from'])) {
4764                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4765                 }
4766                 else {
4767                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4768                 }
4769
4770                 start = this.getAttribute('points');
4771
4772
4773                 if (isset(attributes['points']['to'])) {
4774                     end = translateValues.call(this, attributes['points']['to'], start);
4775
4776                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4777                     for (i = 0,len = control.length; i < len; ++i) {
4778                         control[i] = translateValues.call(this, control[i], start);
4779                     }
4780
4781
4782                 } else if (isset(attributes['points']['by'])) {
4783                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4784
4785                     for (i = 0,len = control.length; i < len; ++i) {
4786                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4787                     }
4788                 }
4789
4790                 this.runtimeAttributes[attr] = [start];
4791
4792                 if (control.length > 0) {
4793                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4794                 }
4795
4796                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4797             }
4798             else {
4799                 superclass.setRuntimeAttribute.call(this, attr);
4800             }
4801         };
4802
4803         var translateValues = function(val, start) {
4804             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4805             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4806
4807             return val;
4808         };
4809
4810         var isset = function(prop) {
4811             return (typeof prop !== 'undefined');
4812         };
4813     })();
4814 /*
4815  * Portions of this file are based on pieces of Yahoo User Interface Library
4816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4817  * YUI licensed under the BSD License:
4818  * http://developer.yahoo.net/yui/license.txt
4819  * <script type="text/javascript">
4820  *
4821  */
4822     (function() {
4823         Roo.lib.Scroll = function(el, attributes, duration, method) {
4824             if (el) {
4825                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4826             }
4827         };
4828
4829         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4830
4831
4832         var Y = Roo.lib;
4833         var superclass = Y.Scroll.superclass;
4834         var proto = Y.Scroll.prototype;
4835
4836         proto.toString = function() {
4837             var el = this.getEl();
4838             var id = el.id || el.tagName;
4839             return ("Scroll " + id);
4840         };
4841
4842         proto.doMethod = function(attr, start, end) {
4843             var val = null;
4844
4845             if (attr == 'scroll') {
4846                 val = [
4847                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4848                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4849                         ];
4850
4851             } else {
4852                 val = superclass.doMethod.call(this, attr, start, end);
4853             }
4854             return val;
4855         };
4856
4857         proto.getAttribute = function(attr) {
4858             var val = null;
4859             var el = this.getEl();
4860
4861             if (attr == 'scroll') {
4862                 val = [ el.scrollLeft, el.scrollTop ];
4863             } else {
4864                 val = superclass.getAttribute.call(this, attr);
4865             }
4866
4867             return val;
4868         };
4869
4870         proto.setAttribute = function(attr, val, unit) {
4871             var el = this.getEl();
4872
4873             if (attr == 'scroll') {
4874                 el.scrollLeft = val[0];
4875                 el.scrollTop = val[1];
4876             } else {
4877                 superclass.setAttribute.call(this, attr, val, unit);
4878             }
4879         };
4880     })();
4881 /*
4882  * Based on:
4883  * Ext JS Library 1.1.1
4884  * Copyright(c) 2006-2007, Ext JS, LLC.
4885  *
4886  * Originally Released Under LGPL - original licence link has changed is not relivant.
4887  *
4888  * Fork - LGPL
4889  * <script type="text/javascript">
4890  */
4891
4892
4893 // nasty IE9 hack - what a pile of crap that is..
4894
4895  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4896     Range.prototype.createContextualFragment = function (html) {
4897         var doc = window.document;
4898         var container = doc.createElement("div");
4899         container.innerHTML = html;
4900         var frag = doc.createDocumentFragment(), n;
4901         while ((n = container.firstChild)) {
4902             frag.appendChild(n);
4903         }
4904         return frag;
4905     };
4906 }
4907
4908 /**
4909  * @class Roo.DomHelper
4910  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4911  * 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>.
4912  * @static
4913  */
4914 Roo.DomHelper = function(){
4915     var tempTableEl = null;
4916     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4917     var tableRe = /^table|tbody|tr|td$/i;
4918     var xmlns = {};
4919     // build as innerHTML where available
4920     /** @ignore */
4921     var createHtml = function(o){
4922         if(typeof o == 'string'){
4923             return o;
4924         }
4925         var b = "";
4926         if(!o.tag){
4927             o.tag = "div";
4928         }
4929         b += "<" + o.tag;
4930         for(var attr in o){
4931             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4932             if(attr == "style"){
4933                 var s = o["style"];
4934                 if(typeof s == "function"){
4935                     s = s.call();
4936                 }
4937                 if(typeof s == "string"){
4938                     b += ' style="' + s + '"';
4939                 }else if(typeof s == "object"){
4940                     b += ' style="';
4941                     for(var key in s){
4942                         if(typeof s[key] != "function"){
4943                             b += key + ":" + s[key] + ";";
4944                         }
4945                     }
4946                     b += '"';
4947                 }
4948             }else{
4949                 if(attr == "cls"){
4950                     b += ' class="' + o["cls"] + '"';
4951                 }else if(attr == "htmlFor"){
4952                     b += ' for="' + o["htmlFor"] + '"';
4953                 }else{
4954                     b += " " + attr + '="' + o[attr] + '"';
4955                 }
4956             }
4957         }
4958         if(emptyTags.test(o.tag)){
4959             b += "/>";
4960         }else{
4961             b += ">";
4962             var cn = o.children || o.cn;
4963             if(cn){
4964                 //http://bugs.kde.org/show_bug.cgi?id=71506
4965                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4966                     for(var i = 0, len = cn.length; i < len; i++) {
4967                         b += createHtml(cn[i], b);
4968                     }
4969                 }else{
4970                     b += createHtml(cn, b);
4971                 }
4972             }
4973             if(o.html){
4974                 b += o.html;
4975             }
4976             b += "</" + o.tag + ">";
4977         }
4978         return b;
4979     };
4980
4981     // build as dom
4982     /** @ignore */
4983     var createDom = function(o, parentNode){
4984          
4985         // defininition craeted..
4986         var ns = false;
4987         if (o.ns && o.ns != 'html') {
4988                
4989             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4990                 xmlns[o.ns] = o.xmlns;
4991                 ns = o.xmlns;
4992             }
4993             if (typeof(xmlns[o.ns]) == 'undefined') {
4994                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4995             }
4996             ns = xmlns[o.ns];
4997         }
4998         
4999         
5000         if (typeof(o) == 'string') {
5001             return parentNode.appendChild(document.createTextNode(o));
5002         }
5003         o.tag = o.tag || div;
5004         if (o.ns && Roo.isIE) {
5005             ns = false;
5006             o.tag = o.ns + ':' + o.tag;
5007             
5008         }
5009         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5010         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5011         for(var attr in o){
5012             
5013             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5014                     attr == "style" || typeof o[attr] == "function") { continue; }
5015                     
5016             if(attr=="cls" && Roo.isIE){
5017                 el.className = o["cls"];
5018             }else{
5019                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5020                 else { 
5021                     el[attr] = o[attr];
5022                 }
5023             }
5024         }
5025         Roo.DomHelper.applyStyles(el, o.style);
5026         var cn = o.children || o.cn;
5027         if(cn){
5028             //http://bugs.kde.org/show_bug.cgi?id=71506
5029              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5030                 for(var i = 0, len = cn.length; i < len; i++) {
5031                     createDom(cn[i], el);
5032                 }
5033             }else{
5034                 createDom(cn, el);
5035             }
5036         }
5037         if(o.html){
5038             el.innerHTML = o.html;
5039         }
5040         if(parentNode){
5041            parentNode.appendChild(el);
5042         }
5043         return el;
5044     };
5045
5046     var ieTable = function(depth, s, h, e){
5047         tempTableEl.innerHTML = [s, h, e].join('');
5048         var i = -1, el = tempTableEl;
5049         while(++i < depth && el.firstChild){
5050             el = el.firstChild;
5051         }
5052         return el;
5053     };
5054
5055     // kill repeat to save bytes
5056     var ts = '<table>',
5057         te = '</table>',
5058         tbs = ts+'<tbody>',
5059         tbe = '</tbody>'+te,
5060         trs = tbs + '<tr>',
5061         tre = '</tr>'+tbe;
5062
5063     /**
5064      * @ignore
5065      * Nasty code for IE's broken table implementation
5066      */
5067     var insertIntoTable = function(tag, where, el, html){
5068         if(!tempTableEl){
5069             tempTableEl = document.createElement('div');
5070         }
5071         var node;
5072         var before = null;
5073         if(tag == 'td'){
5074             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5075                 return;
5076             }
5077             if(where == 'beforebegin'){
5078                 before = el;
5079                 el = el.parentNode;
5080             } else{
5081                 before = el.nextSibling;
5082                 el = el.parentNode;
5083             }
5084             node = ieTable(4, trs, html, tre);
5085         }
5086         else if(tag == 'tr'){
5087             if(where == 'beforebegin'){
5088                 before = el;
5089                 el = el.parentNode;
5090                 node = ieTable(3, tbs, html, tbe);
5091             } else if(where == 'afterend'){
5092                 before = el.nextSibling;
5093                 el = el.parentNode;
5094                 node = ieTable(3, tbs, html, tbe);
5095             } else{ // INTO a TR
5096                 if(where == 'afterbegin'){
5097                     before = el.firstChild;
5098                 }
5099                 node = ieTable(4, trs, html, tre);
5100             }
5101         } else if(tag == 'tbody'){
5102             if(where == 'beforebegin'){
5103                 before = el;
5104                 el = el.parentNode;
5105                 node = ieTable(2, ts, html, te);
5106             } else if(where == 'afterend'){
5107                 before = el.nextSibling;
5108                 el = el.parentNode;
5109                 node = ieTable(2, ts, html, te);
5110             } else{
5111                 if(where == 'afterbegin'){
5112                     before = el.firstChild;
5113                 }
5114                 node = ieTable(3, tbs, html, tbe);
5115             }
5116         } else{ // TABLE
5117             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5118                 return;
5119             }
5120             if(where == 'afterbegin'){
5121                 before = el.firstChild;
5122             }
5123             node = ieTable(2, ts, html, te);
5124         }
5125         el.insertBefore(node, before);
5126         return node;
5127     };
5128
5129     return {
5130     /** True to force the use of DOM instead of html fragments @type Boolean */
5131     useDom : false,
5132
5133     /**
5134      * Returns the markup for the passed Element(s) config
5135      * @param {Object} o The Dom object spec (and children)
5136      * @return {String}
5137      */
5138     markup : function(o){
5139         return createHtml(o);
5140     },
5141
5142     /**
5143      * Applies a style specification to an element
5144      * @param {String/HTMLElement} el The element to apply styles to
5145      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5146      * a function which returns such a specification.
5147      */
5148     applyStyles : function(el, styles){
5149         if(styles){
5150            el = Roo.fly(el);
5151            if(typeof styles == "string"){
5152                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5153                var matches;
5154                while ((matches = re.exec(styles)) != null){
5155                    el.setStyle(matches[1], matches[2]);
5156                }
5157            }else if (typeof styles == "object"){
5158                for (var style in styles){
5159                   el.setStyle(style, styles[style]);
5160                }
5161            }else if (typeof styles == "function"){
5162                 Roo.DomHelper.applyStyles(el, styles.call());
5163            }
5164         }
5165     },
5166
5167     /**
5168      * Inserts an HTML fragment into the Dom
5169      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5170      * @param {HTMLElement} el The context element
5171      * @param {String} html The HTML fragmenet
5172      * @return {HTMLElement} The new node
5173      */
5174     insertHtml : function(where, el, html){
5175         where = where.toLowerCase();
5176         if(el.insertAdjacentHTML){
5177             if(tableRe.test(el.tagName)){
5178                 var rs;
5179                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5180                     return rs;
5181                 }
5182             }
5183             switch(where){
5184                 case "beforebegin":
5185                     el.insertAdjacentHTML('BeforeBegin', html);
5186                     return el.previousSibling;
5187                 case "afterbegin":
5188                     el.insertAdjacentHTML('AfterBegin', html);
5189                     return el.firstChild;
5190                 case "beforeend":
5191                     el.insertAdjacentHTML('BeforeEnd', html);
5192                     return el.lastChild;
5193                 case "afterend":
5194                     el.insertAdjacentHTML('AfterEnd', html);
5195                     return el.nextSibling;
5196             }
5197             throw 'Illegal insertion point -> "' + where + '"';
5198         }
5199         var range = el.ownerDocument.createRange();
5200         var frag;
5201         switch(where){
5202              case "beforebegin":
5203                 range.setStartBefore(el);
5204                 frag = range.createContextualFragment(html);
5205                 el.parentNode.insertBefore(frag, el);
5206                 return el.previousSibling;
5207              case "afterbegin":
5208                 if(el.firstChild){
5209                     range.setStartBefore(el.firstChild);
5210                     frag = range.createContextualFragment(html);
5211                     el.insertBefore(frag, el.firstChild);
5212                     return el.firstChild;
5213                 }else{
5214                     el.innerHTML = html;
5215                     return el.firstChild;
5216                 }
5217             case "beforeend":
5218                 if(el.lastChild){
5219                     range.setStartAfter(el.lastChild);
5220                     frag = range.createContextualFragment(html);
5221                     el.appendChild(frag);
5222                     return el.lastChild;
5223                 }else{
5224                     el.innerHTML = html;
5225                     return el.lastChild;
5226                 }
5227             case "afterend":
5228                 range.setStartAfter(el);
5229                 frag = range.createContextualFragment(html);
5230                 el.parentNode.insertBefore(frag, el.nextSibling);
5231                 return el.nextSibling;
5232             }
5233             throw 'Illegal insertion point -> "' + where + '"';
5234     },
5235
5236     /**
5237      * Creates new Dom element(s) and inserts them before el
5238      * @param {String/HTMLElement/Element} el The context element
5239      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5240      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5241      * @return {HTMLElement/Roo.Element} The new node
5242      */
5243     insertBefore : function(el, o, returnElement){
5244         return this.doInsert(el, o, returnElement, "beforeBegin");
5245     },
5246
5247     /**
5248      * Creates new Dom element(s) and inserts them after el
5249      * @param {String/HTMLElement/Element} el The context element
5250      * @param {Object} o The Dom object spec (and children)
5251      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5252      * @return {HTMLElement/Roo.Element} The new node
5253      */
5254     insertAfter : function(el, o, returnElement){
5255         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5256     },
5257
5258     /**
5259      * Creates new Dom element(s) and inserts them as the first child of el
5260      * @param {String/HTMLElement/Element} el The context element
5261      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5262      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5263      * @return {HTMLElement/Roo.Element} The new node
5264      */
5265     insertFirst : function(el, o, returnElement){
5266         return this.doInsert(el, o, returnElement, "afterBegin");
5267     },
5268
5269     // private
5270     doInsert : function(el, o, returnElement, pos, sibling){
5271         el = Roo.getDom(el);
5272         var newNode;
5273         if(this.useDom || o.ns){
5274             newNode = createDom(o, null);
5275             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5276         }else{
5277             var html = createHtml(o);
5278             newNode = this.insertHtml(pos, el, html);
5279         }
5280         return returnElement ? Roo.get(newNode, true) : newNode;
5281     },
5282
5283     /**
5284      * Creates new Dom element(s) and appends them to el
5285      * @param {String/HTMLElement/Element} el The context element
5286      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5287      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5288      * @return {HTMLElement/Roo.Element} The new node
5289      */
5290     append : function(el, o, returnElement){
5291         el = Roo.getDom(el);
5292         var newNode;
5293         if(this.useDom || o.ns){
5294             newNode = createDom(o, null);
5295             el.appendChild(newNode);
5296         }else{
5297             var html = createHtml(o);
5298             newNode = this.insertHtml("beforeEnd", el, html);
5299         }
5300         return returnElement ? Roo.get(newNode, true) : newNode;
5301     },
5302
5303     /**
5304      * Creates new Dom element(s) and overwrites the contents of el with them
5305      * @param {String/HTMLElement/Element} el The context element
5306      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5307      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5308      * @return {HTMLElement/Roo.Element} The new node
5309      */
5310     overwrite : function(el, o, returnElement){
5311         el = Roo.getDom(el);
5312         if (o.ns) {
5313           
5314             while (el.childNodes.length) {
5315                 el.removeChild(el.firstChild);
5316             }
5317             createDom(o, el);
5318         } else {
5319             el.innerHTML = createHtml(o);   
5320         }
5321         
5322         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5323     },
5324
5325     /**
5326      * Creates a new Roo.DomHelper.Template from the Dom object spec
5327      * @param {Object} o The Dom object spec (and children)
5328      * @return {Roo.DomHelper.Template} The new template
5329      */
5330     createTemplate : function(o){
5331         var html = createHtml(o);
5332         return new Roo.Template(html);
5333     }
5334     };
5335 }();
5336 /*
5337  * Based on:
5338  * Ext JS Library 1.1.1
5339  * Copyright(c) 2006-2007, Ext JS, LLC.
5340  *
5341  * Originally Released Under LGPL - original licence link has changed is not relivant.
5342  *
5343  * Fork - LGPL
5344  * <script type="text/javascript">
5345  */
5346  
5347 /**
5348 * @class Roo.Template
5349 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5350 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5351 * Usage:
5352 <pre><code>
5353 var t = new Roo.Template({
5354     html :  '&lt;div name="{id}"&gt;' + 
5355         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5356         '&lt;/div&gt;',
5357     myformat: function (value, allValues) {
5358         return 'XX' + value;
5359     }
5360 });
5361 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5362 </code></pre>
5363 * For more information see this blog post with examples:
5364 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5365      - Create Elements using DOM, HTML fragments and Templates</a>. 
5366 * @constructor
5367 * @param {Object} cfg - Configuration object.
5368 */
5369 Roo.Template = function(cfg){
5370     // BC!
5371     if(cfg instanceof Array){
5372         cfg = cfg.join("");
5373     }else if(arguments.length > 1){
5374         cfg = Array.prototype.join.call(arguments, "");
5375     }
5376     
5377     
5378     if (typeof(cfg) == 'object') {
5379         Roo.apply(this,cfg)
5380     } else {
5381         // bc
5382         this.html = cfg;
5383     }
5384     if (this.url) {
5385         this.load();
5386     }
5387     
5388 };
5389 Roo.Template.prototype = {
5390     
5391     /**
5392      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5393      */
5394     onLoad : false,
5395     
5396     
5397     /**
5398      * @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..
5399      *                    it should be fixed so that template is observable...
5400      */
5401     url : false,
5402     /**
5403      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5404      */
5405     html : '',
5406     
5407     
5408     compiled : false,
5409     loaded : false,
5410     /**
5411      * Returns an HTML fragment of this template with the specified values applied.
5412      * @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'})
5413      * @return {String} The HTML fragment
5414      */
5415     
5416    
5417     
5418     applyTemplate : function(values){
5419         //Roo.log(["applyTemplate", values]);
5420         try {
5421            
5422             if(this.compiled){
5423                 return this.compiled(values);
5424             }
5425             var useF = this.disableFormats !== true;
5426             var fm = Roo.util.Format, tpl = this;
5427             var fn = function(m, name, format, args){
5428                 if(format && useF){
5429                     if(format.substr(0, 5) == "this."){
5430                         return tpl.call(format.substr(5), values[name], values);
5431                     }else{
5432                         if(args){
5433                             // quoted values are required for strings in compiled templates, 
5434                             // but for non compiled we need to strip them
5435                             // quoted reversed for jsmin
5436                             var re = /^\s*['"](.*)["']\s*$/;
5437                             args = args.split(',');
5438                             for(var i = 0, len = args.length; i < len; i++){
5439                                 args[i] = args[i].replace(re, "$1");
5440                             }
5441                             args = [values[name]].concat(args);
5442                         }else{
5443                             args = [values[name]];
5444                         }
5445                         return fm[format].apply(fm, args);
5446                     }
5447                 }else{
5448                     return values[name] !== undefined ? values[name] : "";
5449                 }
5450             };
5451             return this.html.replace(this.re, fn);
5452         } catch (e) {
5453             Roo.log(e);
5454             throw e;
5455         }
5456          
5457     },
5458     
5459     loading : false,
5460       
5461     load : function ()
5462     {
5463          
5464         if (this.loading) {
5465             return;
5466         }
5467         var _t = this;
5468         
5469         this.loading = true;
5470         this.compiled = false;
5471         
5472         var cx = new Roo.data.Connection();
5473         cx.request({
5474             url : this.url,
5475             method : 'GET',
5476             success : function (response) {
5477                 _t.loading = false;
5478                 _t.url = false;
5479                 
5480                 _t.set(response.responseText,true);
5481                 _t.loaded = true;
5482                 if (_t.onLoad) {
5483                     _t.onLoad();
5484                 }
5485              },
5486             failure : function(response) {
5487                 Roo.log("Template failed to load from " + _t.url);
5488                 _t.loading = false;
5489             }
5490         });
5491     },
5492
5493     /**
5494      * Sets the HTML used as the template and optionally compiles it.
5495      * @param {String} html
5496      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5497      * @return {Roo.Template} this
5498      */
5499     set : function(html, compile){
5500         this.html = html;
5501         this.compiled = false;
5502         if(compile){
5503             this.compile();
5504         }
5505         return this;
5506     },
5507     
5508     /**
5509      * True to disable format functions (defaults to false)
5510      * @type Boolean
5511      */
5512     disableFormats : false,
5513     
5514     /**
5515     * The regular expression used to match template variables 
5516     * @type RegExp
5517     * @property 
5518     */
5519     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5520     
5521     /**
5522      * Compiles the template into an internal function, eliminating the RegEx overhead.
5523      * @return {Roo.Template} this
5524      */
5525     compile : function(){
5526         var fm = Roo.util.Format;
5527         var useF = this.disableFormats !== true;
5528         var sep = Roo.isGecko ? "+" : ",";
5529         var fn = function(m, name, format, args){
5530             if(format && useF){
5531                 args = args ? ',' + args : "";
5532                 if(format.substr(0, 5) != "this."){
5533                     format = "fm." + format + '(';
5534                 }else{
5535                     format = 'this.call("'+ format.substr(5) + '", ';
5536                     args = ", values";
5537                 }
5538             }else{
5539                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5540             }
5541             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5542         };
5543         var body;
5544         // branched to use + in gecko and [].join() in others
5545         if(Roo.isGecko){
5546             body = "this.compiled = function(values){ return '" +
5547                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5548                     "';};";
5549         }else{
5550             body = ["this.compiled = function(values){ return ['"];
5551             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5552             body.push("'].join('');};");
5553             body = body.join('');
5554         }
5555         /**
5556          * eval:var:values
5557          * eval:var:fm
5558          */
5559         eval(body);
5560         return this;
5561     },
5562     
5563     // private function used to call members
5564     call : function(fnName, value, allValues){
5565         return this[fnName](value, allValues);
5566     },
5567     
5568     /**
5569      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5570      * @param {String/HTMLElement/Roo.Element} el The context element
5571      * @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'})
5572      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5573      * @return {HTMLElement/Roo.Element} The new node or Element
5574      */
5575     insertFirst: function(el, values, returnElement){
5576         return this.doInsert('afterBegin', el, values, returnElement);
5577     },
5578
5579     /**
5580      * Applies the supplied values to the template and inserts the new node(s) before el.
5581      * @param {String/HTMLElement/Roo.Element} el The context element
5582      * @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'})
5583      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5584      * @return {HTMLElement/Roo.Element} The new node or Element
5585      */
5586     insertBefore: function(el, values, returnElement){
5587         return this.doInsert('beforeBegin', el, values, returnElement);
5588     },
5589
5590     /**
5591      * Applies the supplied values to the template and inserts the new node(s) after el.
5592      * @param {String/HTMLElement/Roo.Element} el The context element
5593      * @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'})
5594      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5595      * @return {HTMLElement/Roo.Element} The new node or Element
5596      */
5597     insertAfter : function(el, values, returnElement){
5598         return this.doInsert('afterEnd', el, values, returnElement);
5599     },
5600     
5601     /**
5602      * Applies the supplied values to the template and appends the new node(s) to el.
5603      * @param {String/HTMLElement/Roo.Element} el The context element
5604      * @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'})
5605      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5606      * @return {HTMLElement/Roo.Element} The new node or Element
5607      */
5608     append : function(el, values, returnElement){
5609         return this.doInsert('beforeEnd', el, values, returnElement);
5610     },
5611
5612     doInsert : function(where, el, values, returnEl){
5613         el = Roo.getDom(el);
5614         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5615         return returnEl ? Roo.get(newNode, true) : newNode;
5616     },
5617
5618     /**
5619      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5620      * @param {String/HTMLElement/Roo.Element} el The context element
5621      * @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'})
5622      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5623      * @return {HTMLElement/Roo.Element} The new node or Element
5624      */
5625     overwrite : function(el, values, returnElement){
5626         el = Roo.getDom(el);
5627         el.innerHTML = this.applyTemplate(values);
5628         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5629     }
5630 };
5631 /**
5632  * Alias for {@link #applyTemplate}
5633  * @method
5634  */
5635 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5636
5637 // backwards compat
5638 Roo.DomHelper.Template = Roo.Template;
5639
5640 /**
5641  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5642  * @param {String/HTMLElement} el A DOM element or its id
5643  * @returns {Roo.Template} The created template
5644  * @static
5645  */
5646 Roo.Template.from = function(el){
5647     el = Roo.getDom(el);
5648     return new Roo.Template(el.value || el.innerHTML);
5649 };/*
5650  * Based on:
5651  * Ext JS Library 1.1.1
5652  * Copyright(c) 2006-2007, Ext JS, LLC.
5653  *
5654  * Originally Released Under LGPL - original licence link has changed is not relivant.
5655  *
5656  * Fork - LGPL
5657  * <script type="text/javascript">
5658  */
5659  
5660
5661 /*
5662  * This is code is also distributed under MIT license for use
5663  * with jQuery and prototype JavaScript libraries.
5664  */
5665 /**
5666  * @class Roo.DomQuery
5667 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).
5668 <p>
5669 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>
5670
5671 <p>
5672 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.
5673 </p>
5674 <h4>Element Selectors:</h4>
5675 <ul class="list">
5676     <li> <b>*</b> any element</li>
5677     <li> <b>E</b> an element with the tag E</li>
5678     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5679     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5680     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5681     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5682 </ul>
5683 <h4>Attribute Selectors:</h4>
5684 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5685 <ul class="list">
5686     <li> <b>E[foo]</b> has an attribute "foo"</li>
5687     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5688     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5689     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5690     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5691     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5692     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5693 </ul>
5694 <h4>Pseudo Classes:</h4>
5695 <ul class="list">
5696     <li> <b>E:first-child</b> E is the first child of its parent</li>
5697     <li> <b>E:last-child</b> E is the last child of its parent</li>
5698     <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>
5699     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5700     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5701     <li> <b>E:only-child</b> E is the only child of its parent</li>
5702     <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>
5703     <li> <b>E:first</b> the first E in the resultset</li>
5704     <li> <b>E:last</b> the last E in the resultset</li>
5705     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5706     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5707     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5708     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5709     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5710     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5711     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5712     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5713     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5714 </ul>
5715 <h4>CSS Value Selectors:</h4>
5716 <ul class="list">
5717     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5718     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5719     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5720     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5721     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5722     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5723 </ul>
5724  * @static
5725  */
5726 Roo.DomQuery = function(){
5727     var cache = {}, simpleCache = {}, valueCache = {};
5728     var nonSpace = /\S/;
5729     var trimRe = /^\s+|\s+$/g;
5730     var tplRe = /\{(\d+)\}/g;
5731     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5732     var tagTokenRe = /^(#)?([\w-\*]+)/;
5733     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5734
5735     function child(p, index){
5736         var i = 0;
5737         var n = p.firstChild;
5738         while(n){
5739             if(n.nodeType == 1){
5740                if(++i == index){
5741                    return n;
5742                }
5743             }
5744             n = n.nextSibling;
5745         }
5746         return null;
5747     };
5748
5749     function next(n){
5750         while((n = n.nextSibling) && n.nodeType != 1);
5751         return n;
5752     };
5753
5754     function prev(n){
5755         while((n = n.previousSibling) && n.nodeType != 1);
5756         return n;
5757     };
5758
5759     function children(d){
5760         var n = d.firstChild, ni = -1;
5761             while(n){
5762                 var nx = n.nextSibling;
5763                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5764                     d.removeChild(n);
5765                 }else{
5766                     n.nodeIndex = ++ni;
5767                 }
5768                 n = nx;
5769             }
5770             return this;
5771         };
5772
5773     function byClassName(c, a, v){
5774         if(!v){
5775             return c;
5776         }
5777         var r = [], ri = -1, cn;
5778         for(var i = 0, ci; ci = c[i]; i++){
5779             
5780             
5781             if((' '+
5782                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5783                  +' ').indexOf(v) != -1){
5784                 r[++ri] = ci;
5785             }
5786         }
5787         return r;
5788     };
5789
5790     function attrValue(n, attr){
5791         if(!n.tagName && typeof n.length != "undefined"){
5792             n = n[0];
5793         }
5794         if(!n){
5795             return null;
5796         }
5797         if(attr == "for"){
5798             return n.htmlFor;
5799         }
5800         if(attr == "class" || attr == "className"){
5801             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5802         }
5803         return n.getAttribute(attr) || n[attr];
5804
5805     };
5806
5807     function getNodes(ns, mode, tagName){
5808         var result = [], ri = -1, cs;
5809         if(!ns){
5810             return result;
5811         }
5812         tagName = tagName || "*";
5813         if(typeof ns.getElementsByTagName != "undefined"){
5814             ns = [ns];
5815         }
5816         if(!mode){
5817             for(var i = 0, ni; ni = ns[i]; i++){
5818                 cs = ni.getElementsByTagName(tagName);
5819                 for(var j = 0, ci; ci = cs[j]; j++){
5820                     result[++ri] = ci;
5821                 }
5822             }
5823         }else if(mode == "/" || mode == ">"){
5824             var utag = tagName.toUpperCase();
5825             for(var i = 0, ni, cn; ni = ns[i]; i++){
5826                 cn = ni.children || ni.childNodes;
5827                 for(var j = 0, cj; cj = cn[j]; j++){
5828                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5829                         result[++ri] = cj;
5830                     }
5831                 }
5832             }
5833         }else if(mode == "+"){
5834             var utag = tagName.toUpperCase();
5835             for(var i = 0, n; n = ns[i]; i++){
5836                 while((n = n.nextSibling) && n.nodeType != 1);
5837                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5838                     result[++ri] = n;
5839                 }
5840             }
5841         }else if(mode == "~"){
5842             for(var i = 0, n; n = ns[i]; i++){
5843                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5844                 if(n){
5845                     result[++ri] = n;
5846                 }
5847             }
5848         }
5849         return result;
5850     };
5851
5852     function concat(a, b){
5853         if(b.slice){
5854             return a.concat(b);
5855         }
5856         for(var i = 0, l = b.length; i < l; i++){
5857             a[a.length] = b[i];
5858         }
5859         return a;
5860     }
5861
5862     function byTag(cs, tagName){
5863         if(cs.tagName || cs == document){
5864             cs = [cs];
5865         }
5866         if(!tagName){
5867             return cs;
5868         }
5869         var r = [], ri = -1;
5870         tagName = tagName.toLowerCase();
5871         for(var i = 0, ci; ci = cs[i]; i++){
5872             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5873                 r[++ri] = ci;
5874             }
5875         }
5876         return r;
5877     };
5878
5879     function byId(cs, attr, id){
5880         if(cs.tagName || cs == document){
5881             cs = [cs];
5882         }
5883         if(!id){
5884             return cs;
5885         }
5886         var r = [], ri = -1;
5887         for(var i = 0,ci; ci = cs[i]; i++){
5888             if(ci && ci.id == id){
5889                 r[++ri] = ci;
5890                 return r;
5891             }
5892         }
5893         return r;
5894     };
5895
5896     function byAttribute(cs, attr, value, op, custom){
5897         var r = [], ri = -1, st = custom=="{";
5898         var f = Roo.DomQuery.operators[op];
5899         for(var i = 0, ci; ci = cs[i]; i++){
5900             var a;
5901             if(st){
5902                 a = Roo.DomQuery.getStyle(ci, attr);
5903             }
5904             else if(attr == "class" || attr == "className"){
5905                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5906             }else if(attr == "for"){
5907                 a = ci.htmlFor;
5908             }else if(attr == "href"){
5909                 a = ci.getAttribute("href", 2);
5910             }else{
5911                 a = ci.getAttribute(attr);
5912             }
5913             if((f && f(a, value)) || (!f && a)){
5914                 r[++ri] = ci;
5915             }
5916         }
5917         return r;
5918     };
5919
5920     function byPseudo(cs, name, value){
5921         return Roo.DomQuery.pseudos[name](cs, value);
5922     };
5923
5924     // This is for IE MSXML which does not support expandos.
5925     // IE runs the same speed using setAttribute, however FF slows way down
5926     // and Safari completely fails so they need to continue to use expandos.
5927     var isIE = window.ActiveXObject ? true : false;
5928
5929     // this eval is stop the compressor from
5930     // renaming the variable to something shorter
5931     
5932     /** eval:var:batch */
5933     var batch = 30803; 
5934
5935     var key = 30803;
5936
5937     function nodupIEXml(cs){
5938         var d = ++key;
5939         cs[0].setAttribute("_nodup", d);
5940         var r = [cs[0]];
5941         for(var i = 1, len = cs.length; i < len; i++){
5942             var c = cs[i];
5943             if(!c.getAttribute("_nodup") != d){
5944                 c.setAttribute("_nodup", d);
5945                 r[r.length] = c;
5946             }
5947         }
5948         for(var i = 0, len = cs.length; i < len; i++){
5949             cs[i].removeAttribute("_nodup");
5950         }
5951         return r;
5952     }
5953
5954     function nodup(cs){
5955         if(!cs){
5956             return [];
5957         }
5958         var len = cs.length, c, i, r = cs, cj, ri = -1;
5959         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5960             return cs;
5961         }
5962         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5963             return nodupIEXml(cs);
5964         }
5965         var d = ++key;
5966         cs[0]._nodup = d;
5967         for(i = 1; c = cs[i]; i++){
5968             if(c._nodup != d){
5969                 c._nodup = d;
5970             }else{
5971                 r = [];
5972                 for(var j = 0; j < i; j++){
5973                     r[++ri] = cs[j];
5974                 }
5975                 for(j = i+1; cj = cs[j]; j++){
5976                     if(cj._nodup != d){
5977                         cj._nodup = d;
5978                         r[++ri] = cj;
5979                     }
5980                 }
5981                 return r;
5982             }
5983         }
5984         return r;
5985     }
5986
5987     function quickDiffIEXml(c1, c2){
5988         var d = ++key;
5989         for(var i = 0, len = c1.length; i < len; i++){
5990             c1[i].setAttribute("_qdiff", d);
5991         }
5992         var r = [];
5993         for(var i = 0, len = c2.length; i < len; i++){
5994             if(c2[i].getAttribute("_qdiff") != d){
5995                 r[r.length] = c2[i];
5996             }
5997         }
5998         for(var i = 0, len = c1.length; i < len; i++){
5999            c1[i].removeAttribute("_qdiff");
6000         }
6001         return r;
6002     }
6003
6004     function quickDiff(c1, c2){
6005         var len1 = c1.length;
6006         if(!len1){
6007             return c2;
6008         }
6009         if(isIE && c1[0].selectSingleNode){
6010             return quickDiffIEXml(c1, c2);
6011         }
6012         var d = ++key;
6013         for(var i = 0; i < len1; i++){
6014             c1[i]._qdiff = d;
6015         }
6016         var r = [];
6017         for(var i = 0, len = c2.length; i < len; i++){
6018             if(c2[i]._qdiff != d){
6019                 r[r.length] = c2[i];
6020             }
6021         }
6022         return r;
6023     }
6024
6025     function quickId(ns, mode, root, id){
6026         if(ns == root){
6027            var d = root.ownerDocument || root;
6028            return d.getElementById(id);
6029         }
6030         ns = getNodes(ns, mode, "*");
6031         return byId(ns, null, id);
6032     }
6033
6034     return {
6035         getStyle : function(el, name){
6036             return Roo.fly(el).getStyle(name);
6037         },
6038         /**
6039          * Compiles a selector/xpath query into a reusable function. The returned function
6040          * takes one parameter "root" (optional), which is the context node from where the query should start.
6041          * @param {String} selector The selector/xpath query
6042          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6043          * @return {Function}
6044          */
6045         compile : function(path, type){
6046             type = type || "select";
6047             
6048             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6049             var q = path, mode, lq;
6050             var tk = Roo.DomQuery.matchers;
6051             var tklen = tk.length;
6052             var mm;
6053
6054             // accept leading mode switch
6055             var lmode = q.match(modeRe);
6056             if(lmode && lmode[1]){
6057                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6058                 q = q.replace(lmode[1], "");
6059             }
6060             // strip leading slashes
6061             while(path.substr(0, 1)=="/"){
6062                 path = path.substr(1);
6063             }
6064
6065             while(q && lq != q){
6066                 lq = q;
6067                 var tm = q.match(tagTokenRe);
6068                 if(type == "select"){
6069                     if(tm){
6070                         if(tm[1] == "#"){
6071                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6072                         }else{
6073                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6074                         }
6075                         q = q.replace(tm[0], "");
6076                     }else if(q.substr(0, 1) != '@'){
6077                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6078                     }
6079                 }else{
6080                     if(tm){
6081                         if(tm[1] == "#"){
6082                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6083                         }else{
6084                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6085                         }
6086                         q = q.replace(tm[0], "");
6087                     }
6088                 }
6089                 while(!(mm = q.match(modeRe))){
6090                     var matched = false;
6091                     for(var j = 0; j < tklen; j++){
6092                         var t = tk[j];
6093                         var m = q.match(t.re);
6094                         if(m){
6095                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6096                                                     return m[i];
6097                                                 });
6098                             q = q.replace(m[0], "");
6099                             matched = true;
6100                             break;
6101                         }
6102                     }
6103                     // prevent infinite loop on bad selector
6104                     if(!matched){
6105                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6106                     }
6107                 }
6108                 if(mm[1]){
6109                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6110                     q = q.replace(mm[1], "");
6111                 }
6112             }
6113             fn[fn.length] = "return nodup(n);\n}";
6114             
6115              /** 
6116               * list of variables that need from compression as they are used by eval.
6117              *  eval:var:batch 
6118              *  eval:var:nodup
6119              *  eval:var:byTag
6120              *  eval:var:ById
6121              *  eval:var:getNodes
6122              *  eval:var:quickId
6123              *  eval:var:mode
6124              *  eval:var:root
6125              *  eval:var:n
6126              *  eval:var:byClassName
6127              *  eval:var:byPseudo
6128              *  eval:var:byAttribute
6129              *  eval:var:attrValue
6130              * 
6131              **/ 
6132             eval(fn.join(""));
6133             return f;
6134         },
6135
6136         /**
6137          * Selects a group of elements.
6138          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6139          * @param {Node} root (optional) The start of the query (defaults to document).
6140          * @return {Array}
6141          */
6142         select : function(path, root, type){
6143             if(!root || root == document){
6144                 root = document;
6145             }
6146             if(typeof root == "string"){
6147                 root = document.getElementById(root);
6148             }
6149             var paths = path.split(",");
6150             var results = [];
6151             for(var i = 0, len = paths.length; i < len; i++){
6152                 var p = paths[i].replace(trimRe, "");
6153                 if(!cache[p]){
6154                     cache[p] = Roo.DomQuery.compile(p);
6155                     if(!cache[p]){
6156                         throw p + " is not a valid selector";
6157                     }
6158                 }
6159                 var result = cache[p](root);
6160                 if(result && result != document){
6161                     results = results.concat(result);
6162                 }
6163             }
6164             if(paths.length > 1){
6165                 return nodup(results);
6166             }
6167             return results;
6168         },
6169
6170         /**
6171          * Selects a single element.
6172          * @param {String} selector The selector/xpath query
6173          * @param {Node} root (optional) The start of the query (defaults to document).
6174          * @return {Element}
6175          */
6176         selectNode : function(path, root){
6177             return Roo.DomQuery.select(path, root)[0];
6178         },
6179
6180         /**
6181          * Selects the value of a node, optionally replacing null with the defaultValue.
6182          * @param {String} selector The selector/xpath query
6183          * @param {Node} root (optional) The start of the query (defaults to document).
6184          * @param {String} defaultValue
6185          */
6186         selectValue : function(path, root, defaultValue){
6187             path = path.replace(trimRe, "");
6188             if(!valueCache[path]){
6189                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6190             }
6191             var n = valueCache[path](root);
6192             n = n[0] ? n[0] : n;
6193             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6194             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6195         },
6196
6197         /**
6198          * Selects the value of a node, parsing integers and floats.
6199          * @param {String} selector The selector/xpath query
6200          * @param {Node} root (optional) The start of the query (defaults to document).
6201          * @param {Number} defaultValue
6202          * @return {Number}
6203          */
6204         selectNumber : function(path, root, defaultValue){
6205             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6206             return parseFloat(v);
6207         },
6208
6209         /**
6210          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6211          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6212          * @param {String} selector The simple selector to test
6213          * @return {Boolean}
6214          */
6215         is : function(el, ss){
6216             if(typeof el == "string"){
6217                 el = document.getElementById(el);
6218             }
6219             var isArray = (el instanceof Array);
6220             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6221             return isArray ? (result.length == el.length) : (result.length > 0);
6222         },
6223
6224         /**
6225          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6226          * @param {Array} el An array of elements to filter
6227          * @param {String} selector The simple selector to test
6228          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6229          * the selector instead of the ones that match
6230          * @return {Array}
6231          */
6232         filter : function(els, ss, nonMatches){
6233             ss = ss.replace(trimRe, "");
6234             if(!simpleCache[ss]){
6235                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6236             }
6237             var result = simpleCache[ss](els);
6238             return nonMatches ? quickDiff(result, els) : result;
6239         },
6240
6241         /**
6242          * Collection of matching regular expressions and code snippets.
6243          */
6244         matchers : [{
6245                 re: /^\.([\w-]+)/,
6246                 select: 'n = byClassName(n, null, " {1} ");'
6247             }, {
6248                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6249                 select: 'n = byPseudo(n, "{1}", "{2}");'
6250             },{
6251                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6252                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6253             }, {
6254                 re: /^#([\w-]+)/,
6255                 select: 'n = byId(n, null, "{1}");'
6256             },{
6257                 re: /^@([\w-]+)/,
6258                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6259             }
6260         ],
6261
6262         /**
6263          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6264          * 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;.
6265          */
6266         operators : {
6267             "=" : function(a, v){
6268                 return a == v;
6269             },
6270             "!=" : function(a, v){
6271                 return a != v;
6272             },
6273             "^=" : function(a, v){
6274                 return a && a.substr(0, v.length) == v;
6275             },
6276             "$=" : function(a, v){
6277                 return a && a.substr(a.length-v.length) == v;
6278             },
6279             "*=" : function(a, v){
6280                 return a && a.indexOf(v) !== -1;
6281             },
6282             "%=" : function(a, v){
6283                 return (a % v) == 0;
6284             },
6285             "|=" : function(a, v){
6286                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6287             },
6288             "~=" : function(a, v){
6289                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6290             }
6291         },
6292
6293         /**
6294          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6295          * and the argument (if any) supplied in the selector.
6296          */
6297         pseudos : {
6298             "first-child" : function(c){
6299                 var r = [], ri = -1, n;
6300                 for(var i = 0, ci; ci = n = c[i]; i++){
6301                     while((n = n.previousSibling) && n.nodeType != 1);
6302                     if(!n){
6303                         r[++ri] = ci;
6304                     }
6305                 }
6306                 return r;
6307             },
6308
6309             "last-child" : function(c){
6310                 var r = [], ri = -1, n;
6311                 for(var i = 0, ci; ci = n = c[i]; i++){
6312                     while((n = n.nextSibling) && n.nodeType != 1);
6313                     if(!n){
6314                         r[++ri] = ci;
6315                     }
6316                 }
6317                 return r;
6318             },
6319
6320             "nth-child" : function(c, a) {
6321                 var r = [], ri = -1;
6322                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6323                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6324                 for(var i = 0, n; n = c[i]; i++){
6325                     var pn = n.parentNode;
6326                     if (batch != pn._batch) {
6327                         var j = 0;
6328                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6329                             if(cn.nodeType == 1){
6330                                cn.nodeIndex = ++j;
6331                             }
6332                         }
6333                         pn._batch = batch;
6334                     }
6335                     if (f == 1) {
6336                         if (l == 0 || n.nodeIndex == l){
6337                             r[++ri] = n;
6338                         }
6339                     } else if ((n.nodeIndex + l) % f == 0){
6340                         r[++ri] = n;
6341                     }
6342                 }
6343
6344                 return r;
6345             },
6346
6347             "only-child" : function(c){
6348                 var r = [], ri = -1;;
6349                 for(var i = 0, ci; ci = c[i]; i++){
6350                     if(!prev(ci) && !next(ci)){
6351                         r[++ri] = ci;
6352                     }
6353                 }
6354                 return r;
6355             },
6356
6357             "empty" : function(c){
6358                 var r = [], ri = -1;
6359                 for(var i = 0, ci; ci = c[i]; i++){
6360                     var cns = ci.childNodes, j = 0, cn, empty = true;
6361                     while(cn = cns[j]){
6362                         ++j;
6363                         if(cn.nodeType == 1 || cn.nodeType == 3){
6364                             empty = false;
6365                             break;
6366                         }
6367                     }
6368                     if(empty){
6369                         r[++ri] = ci;
6370                     }
6371                 }
6372                 return r;
6373             },
6374
6375             "contains" : function(c, v){
6376                 var r = [], ri = -1;
6377                 for(var i = 0, ci; ci = c[i]; i++){
6378                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6379                         r[++ri] = ci;
6380                     }
6381                 }
6382                 return r;
6383             },
6384
6385             "nodeValue" : function(c, v){
6386                 var r = [], ri = -1;
6387                 for(var i = 0, ci; ci = c[i]; i++){
6388                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6389                         r[++ri] = ci;
6390                     }
6391                 }
6392                 return r;
6393             },
6394
6395             "checked" : function(c){
6396                 var r = [], ri = -1;
6397                 for(var i = 0, ci; ci = c[i]; i++){
6398                     if(ci.checked == true){
6399                         r[++ri] = ci;
6400                     }
6401                 }
6402                 return r;
6403             },
6404
6405             "not" : function(c, ss){
6406                 return Roo.DomQuery.filter(c, ss, true);
6407             },
6408
6409             "odd" : function(c){
6410                 return this["nth-child"](c, "odd");
6411             },
6412
6413             "even" : function(c){
6414                 return this["nth-child"](c, "even");
6415             },
6416
6417             "nth" : function(c, a){
6418                 return c[a-1] || [];
6419             },
6420
6421             "first" : function(c){
6422                 return c[0] || [];
6423             },
6424
6425             "last" : function(c){
6426                 return c[c.length-1] || [];
6427             },
6428
6429             "has" : function(c, ss){
6430                 var s = Roo.DomQuery.select;
6431                 var r = [], ri = -1;
6432                 for(var i = 0, ci; ci = c[i]; i++){
6433                     if(s(ss, ci).length > 0){
6434                         r[++ri] = ci;
6435                     }
6436                 }
6437                 return r;
6438             },
6439
6440             "next" : function(c, ss){
6441                 var is = Roo.DomQuery.is;
6442                 var r = [], ri = -1;
6443                 for(var i = 0, ci; ci = c[i]; i++){
6444                     var n = next(ci);
6445                     if(n && is(n, ss)){
6446                         r[++ri] = ci;
6447                     }
6448                 }
6449                 return r;
6450             },
6451
6452             "prev" : function(c, ss){
6453                 var is = Roo.DomQuery.is;
6454                 var r = [], ri = -1;
6455                 for(var i = 0, ci; ci = c[i]; i++){
6456                     var n = prev(ci);
6457                     if(n && is(n, ss)){
6458                         r[++ri] = ci;
6459                     }
6460                 }
6461                 return r;
6462             }
6463         }
6464     };
6465 }();
6466
6467 /**
6468  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6469  * @param {String} path The selector/xpath query
6470  * @param {Node} root (optional) The start of the query (defaults to document).
6471  * @return {Array}
6472  * @member Roo
6473  * @method query
6474  */
6475 Roo.query = Roo.DomQuery.select;
6476 /*
6477  * Based on:
6478  * Ext JS Library 1.1.1
6479  * Copyright(c) 2006-2007, Ext JS, LLC.
6480  *
6481  * Originally Released Under LGPL - original licence link has changed is not relivant.
6482  *
6483  * Fork - LGPL
6484  * <script type="text/javascript">
6485  */
6486
6487 /**
6488  * @class Roo.util.Observable
6489  * Base class that provides a common interface for publishing events. Subclasses are expected to
6490  * to have a property "events" with all the events defined.<br>
6491  * For example:
6492  * <pre><code>
6493  Employee = function(name){
6494     this.name = name;
6495     this.addEvents({
6496         "fired" : true,
6497         "quit" : true
6498     });
6499  }
6500  Roo.extend(Employee, Roo.util.Observable);
6501 </code></pre>
6502  * @param {Object} config properties to use (incuding events / listeners)
6503  */
6504
6505 Roo.util.Observable = function(cfg){
6506     
6507     cfg = cfg|| {};
6508     this.addEvents(cfg.events || {});
6509     if (cfg.events) {
6510         delete cfg.events; // make sure
6511     }
6512      
6513     Roo.apply(this, cfg);
6514     
6515     if(this.listeners){
6516         this.on(this.listeners);
6517         delete this.listeners;
6518     }
6519 };
6520 Roo.util.Observable.prototype = {
6521     /** 
6522  * @cfg {Object} listeners  list of events and functions to call for this object, 
6523  * For example :
6524  * <pre><code>
6525     listeners :  { 
6526        'click' : function(e) {
6527            ..... 
6528         } ,
6529         .... 
6530     } 
6531   </code></pre>
6532  */
6533     
6534     
6535     /**
6536      * Fires the specified event with the passed parameters (minus the event name).
6537      * @param {String} eventName
6538      * @param {Object...} args Variable number of parameters are passed to handlers
6539      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6540      */
6541     fireEvent : function(){
6542         var ce = this.events[arguments[0].toLowerCase()];
6543         if(typeof ce == "object"){
6544             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6545         }else{
6546             return true;
6547         }
6548     },
6549
6550     // private
6551     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6552
6553     /**
6554      * Appends an event handler to this component
6555      * @param {String}   eventName The type of event to listen for
6556      * @param {Function} handler The method the event invokes
6557      * @param {Object}   scope (optional) The scope in which to execute the handler
6558      * function. The handler function's "this" context.
6559      * @param {Object}   options (optional) An object containing handler configuration
6560      * properties. This may contain any of the following properties:<ul>
6561      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6562      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6563      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6564      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6565      * by the specified number of milliseconds. If the event fires again within that time, the original
6566      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6567      * </ul><br>
6568      * <p>
6569      * <b>Combining Options</b><br>
6570      * Using the options argument, it is possible to combine different types of listeners:<br>
6571      * <br>
6572      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6573                 <pre><code>
6574                 el.on('click', this.onClick, this, {
6575                         single: true,
6576                 delay: 100,
6577                 forumId: 4
6578                 });
6579                 </code></pre>
6580      * <p>
6581      * <b>Attaching multiple handlers in 1 call</b><br>
6582      * The method also allows for a single argument to be passed which is a config object containing properties
6583      * which specify multiple handlers.
6584      * <pre><code>
6585                 el.on({
6586                         'click': {
6587                         fn: this.onClick,
6588                         scope: this,
6589                         delay: 100
6590                 }, 
6591                 'mouseover': {
6592                         fn: this.onMouseOver,
6593                         scope: this
6594                 },
6595                 'mouseout': {
6596                         fn: this.onMouseOut,
6597                         scope: this
6598                 }
6599                 });
6600                 </code></pre>
6601      * <p>
6602      * Or a shorthand syntax which passes the same scope object to all handlers:
6603         <pre><code>
6604                 el.on({
6605                         'click': this.onClick,
6606                 'mouseover': this.onMouseOver,
6607                 'mouseout': this.onMouseOut,
6608                 scope: this
6609                 });
6610                 </code></pre>
6611      */
6612     addListener : function(eventName, fn, scope, o){
6613         if(typeof eventName == "object"){
6614             o = eventName;
6615             for(var e in o){
6616                 if(this.filterOptRe.test(e)){
6617                     continue;
6618                 }
6619                 if(typeof o[e] == "function"){
6620                     // shared options
6621                     this.addListener(e, o[e], o.scope,  o);
6622                 }else{
6623                     // individual options
6624                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6625                 }
6626             }
6627             return;
6628         }
6629         o = (!o || typeof o == "boolean") ? {} : o;
6630         eventName = eventName.toLowerCase();
6631         var ce = this.events[eventName] || true;
6632         if(typeof ce == "boolean"){
6633             ce = new Roo.util.Event(this, eventName);
6634             this.events[eventName] = ce;
6635         }
6636         ce.addListener(fn, scope, o);
6637     },
6638
6639     /**
6640      * Removes a listener
6641      * @param {String}   eventName     The type of event to listen for
6642      * @param {Function} handler        The handler to remove
6643      * @param {Object}   scope  (optional) The scope (this object) for the handler
6644      */
6645     removeListener : function(eventName, fn, scope){
6646         var ce = this.events[eventName.toLowerCase()];
6647         if(typeof ce == "object"){
6648             ce.removeListener(fn, scope);
6649         }
6650     },
6651
6652     /**
6653      * Removes all listeners for this object
6654      */
6655     purgeListeners : function(){
6656         for(var evt in this.events){
6657             if(typeof this.events[evt] == "object"){
6658                  this.events[evt].clearListeners();
6659             }
6660         }
6661     },
6662
6663     relayEvents : function(o, events){
6664         var createHandler = function(ename){
6665             return function(){
6666                  
6667                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6668             };
6669         };
6670         for(var i = 0, len = events.length; i < len; i++){
6671             var ename = events[i];
6672             if(!this.events[ename]){
6673                 this.events[ename] = true;
6674             };
6675             o.on(ename, createHandler(ename), this);
6676         }
6677     },
6678
6679     /**
6680      * Used to define events on this Observable
6681      * @param {Object} object The object with the events defined
6682      */
6683     addEvents : function(o){
6684         if(!this.events){
6685             this.events = {};
6686         }
6687         Roo.applyIf(this.events, o);
6688     },
6689
6690     /**
6691      * Checks to see if this object has any listeners for a specified event
6692      * @param {String} eventName The name of the event to check for
6693      * @return {Boolean} True if the event is being listened for, else false
6694      */
6695     hasListener : function(eventName){
6696         var e = this.events[eventName];
6697         return typeof e == "object" && e.listeners.length > 0;
6698     }
6699 };
6700 /**
6701  * Appends an event handler to this element (shorthand for addListener)
6702  * @param {String}   eventName     The type of event to listen for
6703  * @param {Function} handler        The method the event invokes
6704  * @param {Object}   scope (optional) The scope in which to execute the handler
6705  * function. The handler function's "this" context.
6706  * @param {Object}   options  (optional)
6707  * @method
6708  */
6709 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6710 /**
6711  * Removes a listener (shorthand for removeListener)
6712  * @param {String}   eventName     The type of event to listen for
6713  * @param {Function} handler        The handler to remove
6714  * @param {Object}   scope  (optional) The scope (this object) for the handler
6715  * @method
6716  */
6717 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6718
6719 /**
6720  * Starts capture on the specified Observable. All events will be passed
6721  * to the supplied function with the event name + standard signature of the event
6722  * <b>before</b> the event is fired. If the supplied function returns false,
6723  * the event will not fire.
6724  * @param {Observable} o The Observable to capture
6725  * @param {Function} fn The function to call
6726  * @param {Object} scope (optional) The scope (this object) for the fn
6727  * @static
6728  */
6729 Roo.util.Observable.capture = function(o, fn, scope){
6730     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6731 };
6732
6733 /**
6734  * Removes <b>all</b> added captures from the Observable.
6735  * @param {Observable} o The Observable to release
6736  * @static
6737  */
6738 Roo.util.Observable.releaseCapture = function(o){
6739     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6740 };
6741
6742 (function(){
6743
6744     var createBuffered = function(h, o, scope){
6745         var task = new Roo.util.DelayedTask();
6746         return function(){
6747             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6748         };
6749     };
6750
6751     var createSingle = function(h, e, fn, scope){
6752         return function(){
6753             e.removeListener(fn, scope);
6754             return h.apply(scope, arguments);
6755         };
6756     };
6757
6758     var createDelayed = function(h, o, scope){
6759         return function(){
6760             var args = Array.prototype.slice.call(arguments, 0);
6761             setTimeout(function(){
6762                 h.apply(scope, args);
6763             }, o.delay || 10);
6764         };
6765     };
6766
6767     Roo.util.Event = function(obj, name){
6768         this.name = name;
6769         this.obj = obj;
6770         this.listeners = [];
6771     };
6772
6773     Roo.util.Event.prototype = {
6774         addListener : function(fn, scope, options){
6775             var o = options || {};
6776             scope = scope || this.obj;
6777             if(!this.isListening(fn, scope)){
6778                 var l = {fn: fn, scope: scope, options: o};
6779                 var h = fn;
6780                 if(o.delay){
6781                     h = createDelayed(h, o, scope);
6782                 }
6783                 if(o.single){
6784                     h = createSingle(h, this, fn, scope);
6785                 }
6786                 if(o.buffer){
6787                     h = createBuffered(h, o, scope);
6788                 }
6789                 l.fireFn = h;
6790                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6791                     this.listeners.push(l);
6792                 }else{
6793                     this.listeners = this.listeners.slice(0);
6794                     this.listeners.push(l);
6795                 }
6796             }
6797         },
6798
6799         findListener : function(fn, scope){
6800             scope = scope || this.obj;
6801             var ls = this.listeners;
6802             for(var i = 0, len = ls.length; i < len; i++){
6803                 var l = ls[i];
6804                 if(l.fn == fn && l.scope == scope){
6805                     return i;
6806                 }
6807             }
6808             return -1;
6809         },
6810
6811         isListening : function(fn, scope){
6812             return this.findListener(fn, scope) != -1;
6813         },
6814
6815         removeListener : function(fn, scope){
6816             var index;
6817             if((index = this.findListener(fn, scope)) != -1){
6818                 if(!this.firing){
6819                     this.listeners.splice(index, 1);
6820                 }else{
6821                     this.listeners = this.listeners.slice(0);
6822                     this.listeners.splice(index, 1);
6823                 }
6824                 return true;
6825             }
6826             return false;
6827         },
6828
6829         clearListeners : function(){
6830             this.listeners = [];
6831         },
6832
6833         fire : function(){
6834             var ls = this.listeners, scope, len = ls.length;
6835             if(len > 0){
6836                 this.firing = true;
6837                 var args = Array.prototype.slice.call(arguments, 0);                
6838                 for(var i = 0; i < len; i++){
6839                     var l = ls[i];
6840                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6841                         this.firing = false;
6842                         return false;
6843                     }
6844                 }
6845                 this.firing = false;
6846             }
6847             return true;
6848         }
6849     };
6850 })();/*
6851  * RooJS Library 
6852  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6853  *
6854  * Licence LGPL 
6855  *
6856  */
6857  
6858 /**
6859  * @class Roo.Document
6860  * @extends Roo.util.Observable
6861  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6862  * 
6863  * @param {Object} config the methods and properties of the 'base' class for the application.
6864  * 
6865  *  Generic Page handler - implement this to start your app..
6866  * 
6867  * eg.
6868  *  MyProject = new Roo.Document({
6869         events : {
6870             'load' : true // your events..
6871         },
6872         listeners : {
6873             'ready' : function() {
6874                 // fired on Roo.onReady()
6875             }
6876         }
6877  * 
6878  */
6879 Roo.Document = function(cfg) {
6880      
6881     this.addEvents({ 
6882         'ready' : true
6883     });
6884     Roo.util.Observable.call(this,cfg);
6885     
6886     var _this = this;
6887     
6888     Roo.onReady(function() {
6889         _this.fireEvent('ready');
6890     },null,false);
6891     
6892     
6893 }
6894
6895 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6896  * Based on:
6897  * Ext JS Library 1.1.1
6898  * Copyright(c) 2006-2007, Ext JS, LLC.
6899  *
6900  * Originally Released Under LGPL - original licence link has changed is not relivant.
6901  *
6902  * Fork - LGPL
6903  * <script type="text/javascript">
6904  */
6905
6906 /**
6907  * @class Roo.EventManager
6908  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6909  * several useful events directly.
6910  * See {@link Roo.EventObject} for more details on normalized event objects.
6911  * @static
6912  */
6913 Roo.EventManager = function(){
6914     var docReadyEvent, docReadyProcId, docReadyState = false;
6915     var resizeEvent, resizeTask, textEvent, textSize;
6916     var E = Roo.lib.Event;
6917     var D = Roo.lib.Dom;
6918
6919     
6920     
6921
6922     var fireDocReady = function(){
6923         if(!docReadyState){
6924             docReadyState = true;
6925             Roo.isReady = true;
6926             if(docReadyProcId){
6927                 clearInterval(docReadyProcId);
6928             }
6929             if(Roo.isGecko || Roo.isOpera) {
6930                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6931             }
6932             if(Roo.isIE){
6933                 var defer = document.getElementById("ie-deferred-loader");
6934                 if(defer){
6935                     defer.onreadystatechange = null;
6936                     defer.parentNode.removeChild(defer);
6937                 }
6938             }
6939             if(docReadyEvent){
6940                 docReadyEvent.fire();
6941                 docReadyEvent.clearListeners();
6942             }
6943         }
6944     };
6945     
6946     var initDocReady = function(){
6947         docReadyEvent = new Roo.util.Event();
6948         if(Roo.isGecko || Roo.isOpera) {
6949             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6950         }else if(Roo.isIE){
6951             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6952             var defer = document.getElementById("ie-deferred-loader");
6953             defer.onreadystatechange = function(){
6954                 if(this.readyState == "complete"){
6955                     fireDocReady();
6956                 }
6957             };
6958         }else if(Roo.isSafari){ 
6959             docReadyProcId = setInterval(function(){
6960                 var rs = document.readyState;
6961                 if(rs == "complete") {
6962                     fireDocReady();     
6963                  }
6964             }, 10);
6965         }
6966         // no matter what, make sure it fires on load
6967         E.on(window, "load", fireDocReady);
6968     };
6969
6970     var createBuffered = function(h, o){
6971         var task = new Roo.util.DelayedTask(h);
6972         return function(e){
6973             // create new event object impl so new events don't wipe out properties
6974             e = new Roo.EventObjectImpl(e);
6975             task.delay(o.buffer, h, null, [e]);
6976         };
6977     };
6978
6979     var createSingle = function(h, el, ename, fn){
6980         return function(e){
6981             Roo.EventManager.removeListener(el, ename, fn);
6982             h(e);
6983         };
6984     };
6985
6986     var createDelayed = function(h, o){
6987         return function(e){
6988             // create new event object impl so new events don't wipe out properties
6989             e = new Roo.EventObjectImpl(e);
6990             setTimeout(function(){
6991                 h(e);
6992             }, o.delay || 10);
6993         };
6994     };
6995     var transitionEndVal = false;
6996     
6997     var transitionEnd = function()
6998     {
6999         if (transitionEndVal) {
7000             return transitionEndVal;
7001         }
7002         var el = document.createElement('div');
7003
7004         var transEndEventNames = {
7005             WebkitTransition : 'webkitTransitionEnd',
7006             MozTransition    : 'transitionend',
7007             OTransition      : 'oTransitionEnd otransitionend',
7008             transition       : 'transitionend'
7009         };
7010     
7011         for (var name in transEndEventNames) {
7012             if (el.style[name] !== undefined) {
7013                 transitionEndVal = transEndEventNames[name];
7014                 return  transitionEndVal ;
7015             }
7016         }
7017     }
7018     
7019   
7020
7021     var listen = function(element, ename, opt, fn, scope)
7022     {
7023         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7024         fn = fn || o.fn; scope = scope || o.scope;
7025         var el = Roo.getDom(element);
7026         
7027         
7028         if(!el){
7029             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7030         }
7031         
7032         if (ename == 'transitionend') {
7033             ename = transitionEnd();
7034         }
7035         var h = function(e){
7036             e = Roo.EventObject.setEvent(e);
7037             var t;
7038             if(o.delegate){
7039                 t = e.getTarget(o.delegate, el);
7040                 if(!t){
7041                     return;
7042                 }
7043             }else{
7044                 t = e.target;
7045             }
7046             if(o.stopEvent === true){
7047                 e.stopEvent();
7048             }
7049             if(o.preventDefault === true){
7050                e.preventDefault();
7051             }
7052             if(o.stopPropagation === true){
7053                 e.stopPropagation();
7054             }
7055
7056             if(o.normalized === false){
7057                 e = e.browserEvent;
7058             }
7059
7060             fn.call(scope || el, e, t, o);
7061         };
7062         if(o.delay){
7063             h = createDelayed(h, o);
7064         }
7065         if(o.single){
7066             h = createSingle(h, el, ename, fn);
7067         }
7068         if(o.buffer){
7069             h = createBuffered(h, o);
7070         }
7071         
7072         fn._handlers = fn._handlers || [];
7073         
7074         
7075         fn._handlers.push([Roo.id(el), ename, h]);
7076         
7077         
7078          
7079         E.on(el, ename, h); // this adds the actuall listener to the object..
7080         
7081         
7082         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7083             el.addEventListener("DOMMouseScroll", h, false);
7084             E.on(window, 'unload', function(){
7085                 el.removeEventListener("DOMMouseScroll", h, false);
7086             });
7087         }
7088         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7089             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7090         }
7091         return h;
7092     };
7093
7094     var stopListening = function(el, ename, fn){
7095         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7096         if(hds){
7097             for(var i = 0, len = hds.length; i < len; i++){
7098                 var h = hds[i];
7099                 if(h[0] == id && h[1] == ename){
7100                     hd = h[2];
7101                     hds.splice(i, 1);
7102                     break;
7103                 }
7104             }
7105         }
7106         E.un(el, ename, hd);
7107         el = Roo.getDom(el);
7108         if(ename == "mousewheel" && el.addEventListener){
7109             el.removeEventListener("DOMMouseScroll", hd, false);
7110         }
7111         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7112             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7113         }
7114     };
7115
7116     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7117     
7118     var pub = {
7119         
7120         
7121         /** 
7122          * Fix for doc tools
7123          * @scope Roo.EventManager
7124          */
7125         
7126         
7127         /** 
7128          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7129          * object with a Roo.EventObject
7130          * @param {Function} fn        The method the event invokes
7131          * @param {Object}   scope    An object that becomes the scope of the handler
7132          * @param {boolean}  override If true, the obj passed in becomes
7133          *                             the execution scope of the listener
7134          * @return {Function} The wrapped function
7135          * @deprecated
7136          */
7137         wrap : function(fn, scope, override){
7138             return function(e){
7139                 Roo.EventObject.setEvent(e);
7140                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7141             };
7142         },
7143         
7144         /**
7145      * Appends an event handler to an element (shorthand for addListener)
7146      * @param {String/HTMLElement}   element        The html element or id to assign the
7147      * @param {String}   eventName The type of event to listen for
7148      * @param {Function} handler The method the event invokes
7149      * @param {Object}   scope (optional) The scope in which to execute the handler
7150      * function. The handler function's "this" context.
7151      * @param {Object}   options (optional) An object containing handler configuration
7152      * properties. This may contain any of the following properties:<ul>
7153      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7154      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7155      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7156      * <li>preventDefault {Boolean} True to prevent the default action</li>
7157      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7158      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7159      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7160      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7161      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7162      * by the specified number of milliseconds. If the event fires again within that time, the original
7163      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7164      * </ul><br>
7165      * <p>
7166      * <b>Combining Options</b><br>
7167      * Using the options argument, it is possible to combine different types of listeners:<br>
7168      * <br>
7169      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7170      * Code:<pre><code>
7171 el.on('click', this.onClick, this, {
7172     single: true,
7173     delay: 100,
7174     stopEvent : true,
7175     forumId: 4
7176 });</code></pre>
7177      * <p>
7178      * <b>Attaching multiple handlers in 1 call</b><br>
7179       * The method also allows for a single argument to be passed which is a config object containing properties
7180      * which specify multiple handlers.
7181      * <p>
7182      * Code:<pre><code>
7183 el.on({
7184     'click' : {
7185         fn: this.onClick
7186         scope: this,
7187         delay: 100
7188     },
7189     'mouseover' : {
7190         fn: this.onMouseOver
7191         scope: this
7192     },
7193     'mouseout' : {
7194         fn: this.onMouseOut
7195         scope: this
7196     }
7197 });</code></pre>
7198      * <p>
7199      * Or a shorthand syntax:<br>
7200      * Code:<pre><code>
7201 el.on({
7202     'click' : this.onClick,
7203     'mouseover' : this.onMouseOver,
7204     'mouseout' : this.onMouseOut
7205     scope: this
7206 });</code></pre>
7207      */
7208         addListener : function(element, eventName, fn, scope, options){
7209             if(typeof eventName == "object"){
7210                 var o = eventName;
7211                 for(var e in o){
7212                     if(propRe.test(e)){
7213                         continue;
7214                     }
7215                     if(typeof o[e] == "function"){
7216                         // shared options
7217                         listen(element, e, o, o[e], o.scope);
7218                     }else{
7219                         // individual options
7220                         listen(element, e, o[e]);
7221                     }
7222                 }
7223                 return;
7224             }
7225             return listen(element, eventName, options, fn, scope);
7226         },
7227         
7228         /**
7229          * Removes an event handler
7230          *
7231          * @param {String/HTMLElement}   element        The id or html element to remove the 
7232          *                             event from
7233          * @param {String}   eventName     The type of event
7234          * @param {Function} fn
7235          * @return {Boolean} True if a listener was actually removed
7236          */
7237         removeListener : function(element, eventName, fn){
7238             return stopListening(element, eventName, fn);
7239         },
7240         
7241         /**
7242          * Fires when the document is ready (before onload and before images are loaded). Can be 
7243          * accessed shorthanded Roo.onReady().
7244          * @param {Function} fn        The method the event invokes
7245          * @param {Object}   scope    An  object that becomes the scope of the handler
7246          * @param {boolean}  options
7247          */
7248         onDocumentReady : function(fn, scope, options){
7249             if(docReadyState){ // if it already fired
7250                 docReadyEvent.addListener(fn, scope, options);
7251                 docReadyEvent.fire();
7252                 docReadyEvent.clearListeners();
7253                 return;
7254             }
7255             if(!docReadyEvent){
7256                 initDocReady();
7257             }
7258             docReadyEvent.addListener(fn, scope, options);
7259         },
7260         
7261         /**
7262          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7263          * @param {Function} fn        The method the event invokes
7264          * @param {Object}   scope    An object that becomes the scope of the handler
7265          * @param {boolean}  options
7266          */
7267         onWindowResize : function(fn, scope, options){
7268             if(!resizeEvent){
7269                 resizeEvent = new Roo.util.Event();
7270                 resizeTask = new Roo.util.DelayedTask(function(){
7271                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7272                 });
7273                 E.on(window, "resize", function()
7274                 {
7275                     // it seems that even chrome likes to have a slight delay here.
7276                     //if(Roo.isIE){
7277                         resizeTask.delay(50);
7278                     //}else{
7279                     //    resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7280                     //}
7281                 });
7282             }
7283             resizeEvent.addListener(fn, scope, options);
7284         },
7285
7286         /**
7287          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7288          * @param {Function} fn        The method the event invokes
7289          * @param {Object}   scope    An object that becomes the scope of the handler
7290          * @param {boolean}  options
7291          */
7292         onTextResize : function(fn, scope, options){
7293             if(!textEvent){
7294                 textEvent = new Roo.util.Event();
7295                 var textEl = new Roo.Element(document.createElement('div'));
7296                 textEl.dom.className = 'x-text-resize';
7297                 textEl.dom.innerHTML = 'X';
7298                 textEl.appendTo(document.body);
7299                 textSize = textEl.dom.offsetHeight;
7300                 setInterval(function(){
7301                     if(textEl.dom.offsetHeight != textSize){
7302                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7303                     }
7304                 }, this.textResizeInterval);
7305             }
7306             textEvent.addListener(fn, scope, options);
7307         },
7308
7309         /**
7310          * Removes the passed window resize listener.
7311          * @param {Function} fn        The method the event invokes
7312          * @param {Object}   scope    The scope of handler
7313          */
7314         removeResizeListener : function(fn, scope){
7315             if(resizeEvent){
7316                 resizeEvent.removeListener(fn, scope);
7317             }
7318         },
7319
7320         // private
7321         fireResize : function(){
7322             if(resizeEvent){
7323                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7324             }   
7325         },
7326         /**
7327          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7328          */
7329         ieDeferSrc : false,
7330         /**
7331          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7332          */
7333         textResizeInterval : 50
7334     };
7335     
7336     /**
7337      * Fix for doc tools
7338      * @scopeAlias pub=Roo.EventManager
7339      */
7340     
7341      /**
7342      * Appends an event handler to an element (shorthand for addListener)
7343      * @param {String/HTMLElement}   element        The html element or id to assign the
7344      * @param {String}   eventName The type of event to listen for
7345      * @param {Function} handler The method the event invokes
7346      * @param {Object}   scope (optional) The scope in which to execute the handler
7347      * function. The handler function's "this" context.
7348      * @param {Object}   options (optional) An object containing handler configuration
7349      * properties. This may contain any of the following properties:<ul>
7350      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7351      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7352      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7353      * <li>preventDefault {Boolean} True to prevent the default action</li>
7354      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7355      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7356      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7357      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7358      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7359      * by the specified number of milliseconds. If the event fires again within that time, the original
7360      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7361      * </ul><br>
7362      * <p>
7363      * <b>Combining Options</b><br>
7364      * Using the options argument, it is possible to combine different types of listeners:<br>
7365      * <br>
7366      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7367      * Code:<pre><code>
7368 el.on('click', this.onClick, this, {
7369     single: true,
7370     delay: 100,
7371     stopEvent : true,
7372     forumId: 4
7373 });</code></pre>
7374      * <p>
7375      * <b>Attaching multiple handlers in 1 call</b><br>
7376       * The method also allows for a single argument to be passed which is a config object containing properties
7377      * which specify multiple handlers.
7378      * <p>
7379      * Code:<pre><code>
7380 el.on({
7381     'click' : {
7382         fn: this.onClick
7383         scope: this,
7384         delay: 100
7385     },
7386     'mouseover' : {
7387         fn: this.onMouseOver
7388         scope: this
7389     },
7390     'mouseout' : {
7391         fn: this.onMouseOut
7392         scope: this
7393     }
7394 });</code></pre>
7395      * <p>
7396      * Or a shorthand syntax:<br>
7397      * Code:<pre><code>
7398 el.on({
7399     'click' : this.onClick,
7400     'mouseover' : this.onMouseOver,
7401     'mouseout' : this.onMouseOut
7402     scope: this
7403 });</code></pre>
7404      */
7405     pub.on = pub.addListener;
7406     pub.un = pub.removeListener;
7407
7408     pub.stoppedMouseDownEvent = new Roo.util.Event();
7409     return pub;
7410 }();
7411 /**
7412   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7413   * @param {Function} fn        The method the event invokes
7414   * @param {Object}   scope    An  object that becomes the scope of the handler
7415   * @param {boolean}  override If true, the obj passed in becomes
7416   *                             the execution scope of the listener
7417   * @member Roo
7418   * @method onReady
7419  */
7420 Roo.onReady = Roo.EventManager.onDocumentReady;
7421
7422 Roo.onReady(function(){
7423     var bd = Roo.get(document.body);
7424     if(!bd){ return; }
7425
7426     var cls = [
7427             Roo.isIE ? "roo-ie"
7428             : Roo.isIE11 ? "roo-ie11"
7429             : Roo.isEdge ? "roo-edge"
7430             : Roo.isGecko ? "roo-gecko"
7431             : Roo.isOpera ? "roo-opera"
7432             : Roo.isSafari ? "roo-safari" : ""];
7433
7434     if(Roo.isMac){
7435         cls.push("roo-mac");
7436     }
7437     if(Roo.isLinux){
7438         cls.push("roo-linux");
7439     }
7440     if(Roo.isIOS){
7441         cls.push("roo-ios");
7442     }
7443     if(Roo.isTouch){
7444         cls.push("roo-touch");
7445     }
7446     if(Roo.isBorderBox){
7447         cls.push('roo-border-box');
7448     }
7449     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7450         var p = bd.dom.parentNode;
7451         if(p){
7452             p.className += ' roo-strict';
7453         }
7454     }
7455     bd.addClass(cls.join(' '));
7456 });
7457
7458 /**
7459  * @class Roo.EventObject
7460  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7461  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7462  * Example:
7463  * <pre><code>
7464  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7465     e.preventDefault();
7466     var target = e.getTarget();
7467     ...
7468  }
7469  var myDiv = Roo.get("myDiv");
7470  myDiv.on("click", handleClick);
7471  //or
7472  Roo.EventManager.on("myDiv", 'click', handleClick);
7473  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7474  </code></pre>
7475  * @static
7476  */
7477 Roo.EventObject = function(){
7478     
7479     var E = Roo.lib.Event;
7480     
7481     // safari keypress events for special keys return bad keycodes
7482     var safariKeys = {
7483         63234 : 37, // left
7484         63235 : 39, // right
7485         63232 : 38, // up
7486         63233 : 40, // down
7487         63276 : 33, // page up
7488         63277 : 34, // page down
7489         63272 : 46, // delete
7490         63273 : 36, // home
7491         63275 : 35  // end
7492     };
7493
7494     // normalize button clicks
7495     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7496                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7497
7498     Roo.EventObjectImpl = function(e){
7499         if(e){
7500             this.setEvent(e.browserEvent || e);
7501         }
7502     };
7503     Roo.EventObjectImpl.prototype = {
7504         /**
7505          * Used to fix doc tools.
7506          * @scope Roo.EventObject.prototype
7507          */
7508             
7509
7510         
7511         
7512         /** The normal browser event */
7513         browserEvent : null,
7514         /** The button pressed in a mouse event */
7515         button : -1,
7516         /** True if the shift key was down during the event */
7517         shiftKey : false,
7518         /** True if the control key was down during the event */
7519         ctrlKey : false,
7520         /** True if the alt key was down during the event */
7521         altKey : false,
7522
7523         /** Key constant 
7524         * @type Number */
7525         BACKSPACE : 8,
7526         /** Key constant 
7527         * @type Number */
7528         TAB : 9,
7529         /** Key constant 
7530         * @type Number */
7531         RETURN : 13,
7532         /** Key constant 
7533         * @type Number */
7534         ENTER : 13,
7535         /** Key constant 
7536         * @type Number */
7537         SHIFT : 16,
7538         /** Key constant 
7539         * @type Number */
7540         CONTROL : 17,
7541         /** Key constant 
7542         * @type Number */
7543         ESC : 27,
7544         /** Key constant 
7545         * @type Number */
7546         SPACE : 32,
7547         /** Key constant 
7548         * @type Number */
7549         PAGEUP : 33,
7550         /** Key constant 
7551         * @type Number */
7552         PAGEDOWN : 34,
7553         /** Key constant 
7554         * @type Number */
7555         END : 35,
7556         /** Key constant 
7557         * @type Number */
7558         HOME : 36,
7559         /** Key constant 
7560         * @type Number */
7561         LEFT : 37,
7562         /** Key constant 
7563         * @type Number */
7564         UP : 38,
7565         /** Key constant 
7566         * @type Number */
7567         RIGHT : 39,
7568         /** Key constant 
7569         * @type Number */
7570         DOWN : 40,
7571         /** Key constant 
7572         * @type Number */
7573         DELETE : 46,
7574         /** Key constant 
7575         * @type Number */
7576         F5 : 116,
7577
7578            /** @private */
7579         setEvent : function(e){
7580             if(e == this || (e && e.browserEvent)){ // already wrapped
7581                 return e;
7582             }
7583             this.browserEvent = e;
7584             if(e){
7585                 // normalize buttons
7586                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7587                 if(e.type == 'click' && this.button == -1){
7588                     this.button = 0;
7589                 }
7590                 this.type = e.type;
7591                 this.shiftKey = e.shiftKey;
7592                 // mac metaKey behaves like ctrlKey
7593                 this.ctrlKey = e.ctrlKey || e.metaKey;
7594                 this.altKey = e.altKey;
7595                 // in getKey these will be normalized for the mac
7596                 this.keyCode = e.keyCode;
7597                 // keyup warnings on firefox.
7598                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7599                 // cache the target for the delayed and or buffered events
7600                 this.target = E.getTarget(e);
7601                 // same for XY
7602                 this.xy = E.getXY(e);
7603             }else{
7604                 this.button = -1;
7605                 this.shiftKey = false;
7606                 this.ctrlKey = false;
7607                 this.altKey = false;
7608                 this.keyCode = 0;
7609                 this.charCode =0;
7610                 this.target = null;
7611                 this.xy = [0, 0];
7612             }
7613             return this;
7614         },
7615
7616         /**
7617          * Stop the event (preventDefault and stopPropagation)
7618          */
7619         stopEvent : function(){
7620             if(this.browserEvent){
7621                 if(this.browserEvent.type == 'mousedown'){
7622                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7623                 }
7624                 E.stopEvent(this.browserEvent);
7625             }
7626         },
7627
7628         /**
7629          * Prevents the browsers default handling of the event.
7630          */
7631         preventDefault : function(){
7632             if(this.browserEvent){
7633                 E.preventDefault(this.browserEvent);
7634             }
7635         },
7636
7637         /** @private */
7638         isNavKeyPress : function(){
7639             var k = this.keyCode;
7640             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7641             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7642         },
7643
7644         isSpecialKey : function(){
7645             var k = this.keyCode;
7646             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7647             (k == 16) || (k == 17) ||
7648             (k >= 18 && k <= 20) ||
7649             (k >= 33 && k <= 35) ||
7650             (k >= 36 && k <= 39) ||
7651             (k >= 44 && k <= 45);
7652         },
7653         /**
7654          * Cancels bubbling of the event.
7655          */
7656         stopPropagation : function(){
7657             if(this.browserEvent){
7658                 if(this.type == 'mousedown'){
7659                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7660                 }
7661                 E.stopPropagation(this.browserEvent);
7662             }
7663         },
7664
7665         /**
7666          * Gets the key code for the event.
7667          * @return {Number}
7668          */
7669         getCharCode : function(){
7670             return this.charCode || this.keyCode;
7671         },
7672
7673         /**
7674          * Returns a normalized keyCode for the event.
7675          * @return {Number} The key code
7676          */
7677         getKey : function(){
7678             var k = this.keyCode || this.charCode;
7679             return Roo.isSafari ? (safariKeys[k] || k) : k;
7680         },
7681
7682         /**
7683          * Gets the x coordinate of the event.
7684          * @return {Number}
7685          */
7686         getPageX : function(){
7687             return this.xy[0];
7688         },
7689
7690         /**
7691          * Gets the y coordinate of the event.
7692          * @return {Number}
7693          */
7694         getPageY : function(){
7695             return this.xy[1];
7696         },
7697
7698         /**
7699          * Gets the time of the event.
7700          * @return {Number}
7701          */
7702         getTime : function(){
7703             if(this.browserEvent){
7704                 return E.getTime(this.browserEvent);
7705             }
7706             return null;
7707         },
7708
7709         /**
7710          * Gets the page coordinates of the event.
7711          * @return {Array} The xy values like [x, y]
7712          */
7713         getXY : function(){
7714             return this.xy;
7715         },
7716
7717         /**
7718          * Gets the target for the event.
7719          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7720          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7721                 search as a number or element (defaults to 10 || document.body)
7722          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7723          * @return {HTMLelement}
7724          */
7725         getTarget : function(selector, maxDepth, returnEl){
7726             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7727         },
7728         /**
7729          * Gets the related target.
7730          * @return {HTMLElement}
7731          */
7732         getRelatedTarget : function(){
7733             if(this.browserEvent){
7734                 return E.getRelatedTarget(this.browserEvent);
7735             }
7736             return null;
7737         },
7738
7739         /**
7740          * Normalizes mouse wheel delta across browsers
7741          * @return {Number} The delta
7742          */
7743         getWheelDelta : function(){
7744             var e = this.browserEvent;
7745             var delta = 0;
7746             if(e.wheelDelta){ /* IE/Opera. */
7747                 delta = e.wheelDelta/120;
7748             }else if(e.detail){ /* Mozilla case. */
7749                 delta = -e.detail/3;
7750             }
7751             return delta;
7752         },
7753
7754         /**
7755          * Returns true if the control, meta, shift or alt key was pressed during this event.
7756          * @return {Boolean}
7757          */
7758         hasModifier : function(){
7759             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7760         },
7761
7762         /**
7763          * Returns true if the target of this event equals el or is a child of el
7764          * @param {String/HTMLElement/Element} el
7765          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7766          * @return {Boolean}
7767          */
7768         within : function(el, related){
7769             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7770             return t && Roo.fly(el).contains(t);
7771         },
7772
7773         getPoint : function(){
7774             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7775         }
7776     };
7777
7778     return new Roo.EventObjectImpl();
7779 }();
7780             
7781     /*
7782  * Based on:
7783  * Ext JS Library 1.1.1
7784  * Copyright(c) 2006-2007, Ext JS, LLC.
7785  *
7786  * Originally Released Under LGPL - original licence link has changed is not relivant.
7787  *
7788  * Fork - LGPL
7789  * <script type="text/javascript">
7790  */
7791
7792  
7793 // was in Composite Element!??!?!
7794  
7795 (function(){
7796     var D = Roo.lib.Dom;
7797     var E = Roo.lib.Event;
7798     var A = Roo.lib.Anim;
7799
7800     // local style camelizing for speed
7801     var propCache = {};
7802     var camelRe = /(-[a-z])/gi;
7803     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7804     var view = document.defaultView;
7805
7806 /**
7807  * @class Roo.Element
7808  * Represents an Element in the DOM.<br><br>
7809  * Usage:<br>
7810 <pre><code>
7811 var el = Roo.get("my-div");
7812
7813 // or with getEl
7814 var el = getEl("my-div");
7815
7816 // or with a DOM element
7817 var el = Roo.get(myDivElement);
7818 </code></pre>
7819  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7820  * each call instead of constructing a new one.<br><br>
7821  * <b>Animations</b><br />
7822  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7823  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7824 <pre>
7825 Option    Default   Description
7826 --------- --------  ---------------------------------------------
7827 duration  .35       The duration of the animation in seconds
7828 easing    easeOut   The YUI easing method
7829 callback  none      A function to execute when the anim completes
7830 scope     this      The scope (this) of the callback function
7831 </pre>
7832 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7833 * manipulate the animation. Here's an example:
7834 <pre><code>
7835 var el = Roo.get("my-div");
7836
7837 // no animation
7838 el.setWidth(100);
7839
7840 // default animation
7841 el.setWidth(100, true);
7842
7843 // animation with some options set
7844 el.setWidth(100, {
7845     duration: 1,
7846     callback: this.foo,
7847     scope: this
7848 });
7849
7850 // using the "anim" property to get the Anim object
7851 var opt = {
7852     duration: 1,
7853     callback: this.foo,
7854     scope: this
7855 };
7856 el.setWidth(100, opt);
7857 ...
7858 if(opt.anim.isAnimated()){
7859     opt.anim.stop();
7860 }
7861 </code></pre>
7862 * <b> Composite (Collections of) Elements</b><br />
7863  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7864  * @constructor Create a new Element directly.
7865  * @param {String/HTMLElement} element
7866  * @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).
7867  */
7868     Roo.Element = function(element, forceNew)
7869     {
7870         var dom = typeof element == "string" ?
7871                 document.getElementById(element) : element;
7872         
7873         this.listeners = {};
7874         
7875         if(!dom){ // invalid id/element
7876             return null;
7877         }
7878         var id = dom.id;
7879         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7880             return Roo.Element.cache[id];
7881         }
7882
7883         /**
7884          * The DOM element
7885          * @type HTMLElement
7886          */
7887         this.dom = dom;
7888
7889         /**
7890          * The DOM element ID
7891          * @type String
7892          */
7893         this.id = id || Roo.id(dom);
7894         
7895         return this; // assumed for cctor?
7896     };
7897
7898     var El = Roo.Element;
7899
7900     El.prototype = {
7901         /**
7902          * The element's default display mode  (defaults to "") 
7903          * @type String
7904          */
7905         originalDisplay : "",
7906
7907         
7908         // note this is overridden in BS version..
7909         visibilityMode : 1, 
7910         /**
7911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7912          * @type String
7913          */
7914         defaultUnit : "px",
7915         
7916         /**
7917          * Sets the element's visibility mode. When setVisible() is called it
7918          * will use this to determine whether to set the visibility or the display property.
7919          * @param visMode Element.VISIBILITY or Element.DISPLAY
7920          * @return {Roo.Element} this
7921          */
7922         setVisibilityMode : function(visMode){
7923             this.visibilityMode = visMode;
7924             return this;
7925         },
7926         /**
7927          * Convenience method for setVisibilityMode(Element.DISPLAY)
7928          * @param {String} display (optional) What to set display to when visible
7929          * @return {Roo.Element} this
7930          */
7931         enableDisplayMode : function(display){
7932             this.setVisibilityMode(El.DISPLAY);
7933             if(typeof display != "undefined") { this.originalDisplay = display; }
7934             return this;
7935         },
7936
7937         /**
7938          * 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)
7939          * @param {String} selector The simple selector to test
7940          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7941                 search as a number or element (defaults to 10 || document.body)
7942          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7943          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7944          */
7945         findParent : function(simpleSelector, maxDepth, returnEl){
7946             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7947             maxDepth = maxDepth || 50;
7948             if(typeof maxDepth != "number"){
7949                 stopEl = Roo.getDom(maxDepth);
7950                 maxDepth = 10;
7951             }
7952             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7953                 if(dq.is(p, simpleSelector)){
7954                     return returnEl ? Roo.get(p) : p;
7955                 }
7956                 depth++;
7957                 p = p.parentNode;
7958             }
7959             return null;
7960         },
7961
7962
7963         /**
7964          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7965          * @param {String} selector The simple selector to test
7966          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7967                 search as a number or element (defaults to 10 || document.body)
7968          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7969          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7970          */
7971         findParentNode : function(simpleSelector, maxDepth, returnEl){
7972             var p = Roo.fly(this.dom.parentNode, '_internal');
7973             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7974         },
7975         
7976         /**
7977          * Looks at  the scrollable parent element
7978          */
7979         findScrollableParent : function()
7980         {
7981             var overflowRegex = /(auto|scroll)/;
7982             
7983             if(this.getStyle('position') === 'fixed'){
7984                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7985             }
7986             
7987             var excludeStaticParent = this.getStyle('position') === "absolute";
7988             
7989             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7990                 
7991                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7992                     continue;
7993                 }
7994                 
7995                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7996                     return parent;
7997                 }
7998                 
7999                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8000                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8001                 }
8002             }
8003             
8004             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8005         },
8006
8007         /**
8008          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8009          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8010          * @param {String} selector The simple selector to test
8011          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8012                 search as a number or element (defaults to 10 || document.body)
8013          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8014          */
8015         up : function(simpleSelector, maxDepth){
8016             return this.findParentNode(simpleSelector, maxDepth, true);
8017         },
8018
8019
8020
8021         /**
8022          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8023          * @param {String} selector The simple selector to test
8024          * @return {Boolean} True if this element matches the selector, else false
8025          */
8026         is : function(simpleSelector){
8027             return Roo.DomQuery.is(this.dom, simpleSelector);
8028         },
8029
8030         /**
8031          * Perform animation on this element.
8032          * @param {Object} args The YUI animation control args
8033          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8034          * @param {Function} onComplete (optional) Function to call when animation completes
8035          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8036          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8037          * @return {Roo.Element} this
8038          */
8039         animate : function(args, duration, onComplete, easing, animType){
8040             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8041             return this;
8042         },
8043
8044         /*
8045          * @private Internal animation call
8046          */
8047         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8048             animType = animType || 'run';
8049             opt = opt || {};
8050             var anim = Roo.lib.Anim[animType](
8051                 this.dom, args,
8052                 (opt.duration || defaultDur) || .35,
8053                 (opt.easing || defaultEase) || 'easeOut',
8054                 function(){
8055                     Roo.callback(cb, this);
8056                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8057                 },
8058                 this
8059             );
8060             opt.anim = anim;
8061             return anim;
8062         },
8063
8064         // private legacy anim prep
8065         preanim : function(a, i){
8066             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8067         },
8068
8069         /**
8070          * Removes worthless text nodes
8071          * @param {Boolean} forceReclean (optional) By default the element
8072          * keeps track if it has been cleaned already so
8073          * you can call this over and over. However, if you update the element and
8074          * need to force a reclean, you can pass true.
8075          */
8076         clean : function(forceReclean){
8077             if(this.isCleaned && forceReclean !== true){
8078                 return this;
8079             }
8080             var ns = /\S/;
8081             var d = this.dom, n = d.firstChild, ni = -1;
8082             while(n){
8083                 var nx = n.nextSibling;
8084                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8085                     d.removeChild(n);
8086                 }else{
8087                     n.nodeIndex = ++ni;
8088                 }
8089                 n = nx;
8090             }
8091             this.isCleaned = true;
8092             return this;
8093         },
8094
8095         // private
8096         calcOffsetsTo : function(el){
8097             el = Roo.get(el);
8098             var d = el.dom;
8099             var restorePos = false;
8100             if(el.getStyle('position') == 'static'){
8101                 el.position('relative');
8102                 restorePos = true;
8103             }
8104             var x = 0, y =0;
8105             var op = this.dom;
8106             while(op && op != d && op.tagName != 'HTML'){
8107                 x+= op.offsetLeft;
8108                 y+= op.offsetTop;
8109                 op = op.offsetParent;
8110             }
8111             if(restorePos){
8112                 el.position('static');
8113             }
8114             return [x, y];
8115         },
8116
8117         /**
8118          * Scrolls this element into view within the passed container.
8119          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8120          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8121          * @return {Roo.Element} this
8122          */
8123         scrollIntoView : function(container, hscroll){
8124             var c = Roo.getDom(container) || document.body;
8125             var el = this.dom;
8126
8127             var o = this.calcOffsetsTo(c),
8128                 l = o[0],
8129                 t = o[1],
8130                 b = t+el.offsetHeight,
8131                 r = l+el.offsetWidth;
8132
8133             var ch = c.clientHeight;
8134             var ct = parseInt(c.scrollTop, 10);
8135             var cl = parseInt(c.scrollLeft, 10);
8136             var cb = ct + ch;
8137             var cr = cl + c.clientWidth;
8138
8139             if(t < ct){
8140                 c.scrollTop = t;
8141             }else if(b > cb){
8142                 c.scrollTop = b-ch;
8143             }
8144
8145             if(hscroll !== false){
8146                 if(l < cl){
8147                     c.scrollLeft = l;
8148                 }else if(r > cr){
8149                     c.scrollLeft = r-c.clientWidth;
8150                 }
8151             }
8152             return this;
8153         },
8154
8155         // private
8156         scrollChildIntoView : function(child, hscroll){
8157             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8158         },
8159
8160         /**
8161          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8162          * the new height may not be available immediately.
8163          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8164          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8165          * @param {Function} onComplete (optional) Function to call when animation completes
8166          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8167          * @return {Roo.Element} this
8168          */
8169         autoHeight : function(animate, duration, onComplete, easing){
8170             var oldHeight = this.getHeight();
8171             this.clip();
8172             this.setHeight(1); // force clipping
8173             setTimeout(function(){
8174                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8175                 if(!animate){
8176                     this.setHeight(height);
8177                     this.unclip();
8178                     if(typeof onComplete == "function"){
8179                         onComplete();
8180                     }
8181                 }else{
8182                     this.setHeight(oldHeight); // restore original height
8183                     this.setHeight(height, animate, duration, function(){
8184                         this.unclip();
8185                         if(typeof onComplete == "function") { onComplete(); }
8186                     }.createDelegate(this), easing);
8187                 }
8188             }.createDelegate(this), 0);
8189             return this;
8190         },
8191
8192         /**
8193          * Returns true if this element is an ancestor of the passed element
8194          * @param {HTMLElement/String} el The element to check
8195          * @return {Boolean} True if this element is an ancestor of el, else false
8196          */
8197         contains : function(el){
8198             if(!el){return false;}
8199             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8200         },
8201
8202         /**
8203          * Checks whether the element is currently visible using both visibility and display properties.
8204          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8205          * @return {Boolean} True if the element is currently visible, else false
8206          */
8207         isVisible : function(deep) {
8208             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8209             if(deep !== true || !vis){
8210                 return vis;
8211             }
8212             var p = this.dom.parentNode;
8213             while(p && p.tagName.toLowerCase() != "body"){
8214                 if(!Roo.fly(p, '_isVisible').isVisible()){
8215                     return false;
8216                 }
8217                 p = p.parentNode;
8218             }
8219             return true;
8220         },
8221
8222         /**
8223          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8224          * @param {String} selector The CSS selector
8225          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8226          * @return {CompositeElement/CompositeElementLite} The composite element
8227          */
8228         select : function(selector, unique){
8229             return El.select(selector, unique, this.dom);
8230         },
8231
8232         /**
8233          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8234          * @param {String} selector The CSS selector
8235          * @return {Array} An array of the matched nodes
8236          */
8237         query : function(selector, unique){
8238             return Roo.DomQuery.select(selector, this.dom);
8239         },
8240
8241         /**
8242          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8243          * @param {String} selector The CSS selector
8244          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8245          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8246          */
8247         child : function(selector, returnDom){
8248             var n = Roo.DomQuery.selectNode(selector, this.dom);
8249             return returnDom ? n : Roo.get(n);
8250         },
8251
8252         /**
8253          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8254          * @param {String} selector The CSS selector
8255          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8256          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8257          */
8258         down : function(selector, returnDom){
8259             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8260             return returnDom ? n : Roo.get(n);
8261         },
8262
8263         /**
8264          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8265          * @param {String} group The group the DD object is member of
8266          * @param {Object} config The DD config object
8267          * @param {Object} overrides An object containing methods to override/implement on the DD object
8268          * @return {Roo.dd.DD} The DD object
8269          */
8270         initDD : function(group, config, overrides){
8271             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8272             return Roo.apply(dd, overrides);
8273         },
8274
8275         /**
8276          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8277          * @param {String} group The group the DDProxy object is member of
8278          * @param {Object} config The DDProxy config object
8279          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8280          * @return {Roo.dd.DDProxy} The DDProxy object
8281          */
8282         initDDProxy : function(group, config, overrides){
8283             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8284             return Roo.apply(dd, overrides);
8285         },
8286
8287         /**
8288          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8289          * @param {String} group The group the DDTarget object is member of
8290          * @param {Object} config The DDTarget config object
8291          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8292          * @return {Roo.dd.DDTarget} The DDTarget object
8293          */
8294         initDDTarget : function(group, config, overrides){
8295             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8296             return Roo.apply(dd, overrides);
8297         },
8298
8299         /**
8300          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8301          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8302          * @param {Boolean} visible Whether the element is visible
8303          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8304          * @return {Roo.Element} this
8305          */
8306          setVisible : function(visible, animate){
8307             if(!animate || !A){
8308                 if(this.visibilityMode == El.DISPLAY){
8309                     this.setDisplayed(visible);
8310                 }else{
8311                     this.fixDisplay();
8312                     this.dom.style.visibility = visible ? "visible" : "hidden";
8313                 }
8314             }else{
8315                 // closure for composites
8316                 var dom = this.dom;
8317                 var visMode = this.visibilityMode;
8318                 if(visible){
8319                     this.setOpacity(.01);
8320                     this.setVisible(true);
8321                 }
8322                 this.anim({opacity: { to: (visible?1:0) }},
8323                       this.preanim(arguments, 1),
8324                       null, .35, 'easeIn', function(){
8325                          if(!visible){
8326                              if(visMode == El.DISPLAY){
8327                                  dom.style.display = "none";
8328                              }else{
8329                                  dom.style.visibility = "hidden";
8330                              }
8331                              Roo.get(dom).setOpacity(1);
8332                          }
8333                      });
8334             }
8335             return this;
8336         },
8337
8338         /**
8339          * Returns true if display is not "none"
8340          * @return {Boolean}
8341          */
8342         isDisplayed : function() {
8343             return this.getStyle("display") != "none";
8344         },
8345
8346         /**
8347          * Toggles the element's visibility or display, depending on visibility mode.
8348          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8349          * @return {Roo.Element} this
8350          */
8351         toggle : function(animate){
8352             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8353             return this;
8354         },
8355
8356         /**
8357          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8358          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8359          * @return {Roo.Element} this
8360          */
8361         setDisplayed : function(value) {
8362             if(typeof value == "boolean"){
8363                value = value ? this.originalDisplay : "none";
8364             }
8365             this.setStyle("display", value);
8366             return this;
8367         },
8368
8369         /**
8370          * Tries to focus the element. Any exceptions are caught and ignored.
8371          * @return {Roo.Element} this
8372          */
8373         focus : function() {
8374             try{
8375                 this.dom.focus();
8376             }catch(e){}
8377             return this;
8378         },
8379
8380         /**
8381          * Tries to blur the element. Any exceptions are caught and ignored.
8382          * @return {Roo.Element} this
8383          */
8384         blur : function() {
8385             try{
8386                 this.dom.blur();
8387             }catch(e){}
8388             return this;
8389         },
8390
8391         /**
8392          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8393          * @param {String/Array} className The CSS class to add, or an array of classes
8394          * @return {Roo.Element} this
8395          */
8396         addClass : function(className){
8397             if(className instanceof Array){
8398                 for(var i = 0, len = className.length; i < len; i++) {
8399                     this.addClass(className[i]);
8400                 }
8401             }else{
8402                 if(className && !this.hasClass(className)){
8403                     if (this.dom instanceof SVGElement) {
8404                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8405                     } else {
8406                         this.dom.className = this.dom.className + " " + className;
8407                     }
8408                 }
8409             }
8410             return this;
8411         },
8412
8413         /**
8414          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8415          * @param {String/Array} className The CSS class to add, or an array of classes
8416          * @return {Roo.Element} this
8417          */
8418         radioClass : function(className){
8419             var siblings = this.dom.parentNode.childNodes;
8420             for(var i = 0; i < siblings.length; i++) {
8421                 var s = siblings[i];
8422                 if(s.nodeType == 1){
8423                     Roo.get(s).removeClass(className);
8424                 }
8425             }
8426             this.addClass(className);
8427             return this;
8428         },
8429
8430         /**
8431          * Removes one or more CSS classes from the element.
8432          * @param {String/Array} className The CSS class to remove, or an array of classes
8433          * @return {Roo.Element} this
8434          */
8435         removeClass : function(className){
8436             
8437             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8438             if(!className || !cn){
8439                 return this;
8440             }
8441             if(className instanceof Array){
8442                 for(var i = 0, len = className.length; i < len; i++) {
8443                     this.removeClass(className[i]);
8444                 }
8445             }else{
8446                 if(this.hasClass(className)){
8447                     var re = this.classReCache[className];
8448                     if (!re) {
8449                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8450                        this.classReCache[className] = re;
8451                     }
8452                     if (this.dom instanceof SVGElement) {
8453                         this.dom.className.baseVal = cn.replace(re, " ");
8454                     } else {
8455                         this.dom.className = cn.replace(re, " ");
8456                     }
8457                 }
8458             }
8459             return this;
8460         },
8461
8462         // private
8463         classReCache: {},
8464
8465         /**
8466          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8467          * @param {String} className The CSS class to toggle
8468          * @return {Roo.Element} this
8469          */
8470         toggleClass : function(className){
8471             if(this.hasClass(className)){
8472                 this.removeClass(className);
8473             }else{
8474                 this.addClass(className);
8475             }
8476             return this;
8477         },
8478
8479         /**
8480          * Checks if the specified CSS class exists on this element's DOM node.
8481          * @param {String} className The CSS class to check for
8482          * @return {Boolean} True if the class exists, else false
8483          */
8484         hasClass : function(className){
8485             if (this.dom instanceof SVGElement) {
8486                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8487             } 
8488             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8489         },
8490
8491         /**
8492          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8493          * @param {String} oldClassName The CSS class to replace
8494          * @param {String} newClassName The replacement CSS class
8495          * @return {Roo.Element} this
8496          */
8497         replaceClass : function(oldClassName, newClassName){
8498             this.removeClass(oldClassName);
8499             this.addClass(newClassName);
8500             return this;
8501         },
8502
8503         /**
8504          * Returns an object with properties matching the styles requested.
8505          * For example, el.getStyles('color', 'font-size', 'width') might return
8506          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8507          * @param {String} style1 A style name
8508          * @param {String} style2 A style name
8509          * @param {String} etc.
8510          * @return {Object} The style object
8511          */
8512         getStyles : function(){
8513             var a = arguments, len = a.length, r = {};
8514             for(var i = 0; i < len; i++){
8515                 r[a[i]] = this.getStyle(a[i]);
8516             }
8517             return r;
8518         },
8519
8520         /**
8521          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8522          * @param {String} property The style property whose value is returned.
8523          * @return {String} The current value of the style property for this element.
8524          */
8525         getStyle : function(){
8526             return view && view.getComputedStyle ?
8527                 function(prop){
8528                     var el = this.dom, v, cs, camel;
8529                     if(prop == 'float'){
8530                         prop = "cssFloat";
8531                     }
8532                     if(el.style && (v = el.style[prop])){
8533                         return v;
8534                     }
8535                     if(cs = view.getComputedStyle(el, "")){
8536                         if(!(camel = propCache[prop])){
8537                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8538                         }
8539                         return cs[camel];
8540                     }
8541                     return null;
8542                 } :
8543                 function(prop){
8544                     var el = this.dom, v, cs, camel;
8545                     if(prop == 'opacity'){
8546                         if(typeof el.style.filter == 'string'){
8547                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8548                             if(m){
8549                                 var fv = parseFloat(m[1]);
8550                                 if(!isNaN(fv)){
8551                                     return fv ? fv / 100 : 0;
8552                                 }
8553                             }
8554                         }
8555                         return 1;
8556                     }else if(prop == 'float'){
8557                         prop = "styleFloat";
8558                     }
8559                     if(!(camel = propCache[prop])){
8560                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8561                     }
8562                     if(v = el.style[camel]){
8563                         return v;
8564                     }
8565                     if(cs = el.currentStyle){
8566                         return cs[camel];
8567                     }
8568                     return null;
8569                 };
8570         }(),
8571
8572         /**
8573          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8574          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8575          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8576          * @return {Roo.Element} this
8577          */
8578         setStyle : function(prop, value){
8579             if(typeof prop == "string"){
8580                 
8581                 if (prop == 'float') {
8582                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8583                     return this;
8584                 }
8585                 
8586                 var camel;
8587                 if(!(camel = propCache[prop])){
8588                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8589                 }
8590                 
8591                 if(camel == 'opacity') {
8592                     this.setOpacity(value);
8593                 }else{
8594                     this.dom.style[camel] = value;
8595                 }
8596             }else{
8597                 for(var style in prop){
8598                     if(typeof prop[style] != "function"){
8599                        this.setStyle(style, prop[style]);
8600                     }
8601                 }
8602             }
8603             return this;
8604         },
8605
8606         /**
8607          * More flexible version of {@link #setStyle} for setting style properties.
8608          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8609          * a function which returns such a specification.
8610          * @return {Roo.Element} this
8611          */
8612         applyStyles : function(style){
8613             Roo.DomHelper.applyStyles(this.dom, style);
8614             return this;
8615         },
8616
8617         /**
8618           * 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).
8619           * @return {Number} The X position of the element
8620           */
8621         getX : function(){
8622             return D.getX(this.dom);
8623         },
8624
8625         /**
8626           * 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).
8627           * @return {Number} The Y position of the element
8628           */
8629         getY : function(){
8630             return D.getY(this.dom);
8631         },
8632
8633         /**
8634           * 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).
8635           * @return {Array} The XY position of the element
8636           */
8637         getXY : function(){
8638             return D.getXY(this.dom);
8639         },
8640
8641         /**
8642          * 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).
8643          * @param {Number} The X position of the element
8644          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8645          * @return {Roo.Element} this
8646          */
8647         setX : function(x, animate){
8648             if(!animate || !A){
8649                 D.setX(this.dom, x);
8650             }else{
8651                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8652             }
8653             return this;
8654         },
8655
8656         /**
8657          * 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).
8658          * @param {Number} The Y position of the element
8659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8660          * @return {Roo.Element} this
8661          */
8662         setY : function(y, animate){
8663             if(!animate || !A){
8664                 D.setY(this.dom, y);
8665             }else{
8666                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8667             }
8668             return this;
8669         },
8670
8671         /**
8672          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8673          * @param {String} left The left CSS property value
8674          * @return {Roo.Element} this
8675          */
8676         setLeft : function(left){
8677             this.setStyle("left", this.addUnits(left));
8678             return this;
8679         },
8680
8681         /**
8682          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8683          * @param {String} top The top CSS property value
8684          * @return {Roo.Element} this
8685          */
8686         setTop : function(top){
8687             this.setStyle("top", this.addUnits(top));
8688             return this;
8689         },
8690
8691         /**
8692          * Sets the element's CSS right style.
8693          * @param {String} right The right CSS property value
8694          * @return {Roo.Element} this
8695          */
8696         setRight : function(right){
8697             this.setStyle("right", this.addUnits(right));
8698             return this;
8699         },
8700
8701         /**
8702          * Sets the element's CSS bottom style.
8703          * @param {String} bottom The bottom CSS property value
8704          * @return {Roo.Element} this
8705          */
8706         setBottom : function(bottom){
8707             this.setStyle("bottom", this.addUnits(bottom));
8708             return this;
8709         },
8710
8711         /**
8712          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8713          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8714          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8715          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8716          * @return {Roo.Element} this
8717          */
8718         setXY : function(pos, animate){
8719             if(!animate || !A){
8720                 D.setXY(this.dom, pos);
8721             }else{
8722                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8723             }
8724             return this;
8725         },
8726
8727         /**
8728          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8729          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8730          * @param {Number} x X value for new position (coordinates are page-based)
8731          * @param {Number} y Y value for new position (coordinates are page-based)
8732          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8733          * @return {Roo.Element} this
8734          */
8735         setLocation : function(x, y, animate){
8736             this.setXY([x, y], this.preanim(arguments, 2));
8737             return this;
8738         },
8739
8740         /**
8741          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8742          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8743          * @param {Number} x X value for new position (coordinates are page-based)
8744          * @param {Number} y Y value for new position (coordinates are page-based)
8745          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8746          * @return {Roo.Element} this
8747          */
8748         moveTo : function(x, y, animate){
8749             this.setXY([x, y], this.preanim(arguments, 2));
8750             return this;
8751         },
8752
8753         /**
8754          * Returns the region of the given element.
8755          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8756          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8757          */
8758         getRegion : function(){
8759             return D.getRegion(this.dom);
8760         },
8761
8762         /**
8763          * Returns the offset height of the element
8764          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8765          * @return {Number} The element's height
8766          */
8767         getHeight : function(contentHeight){
8768             var h = this.dom.offsetHeight || 0;
8769             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8770         },
8771
8772         /**
8773          * Returns the offset width of the element
8774          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8775          * @return {Number} The element's width
8776          */
8777         getWidth : function(contentWidth){
8778             var w = this.dom.offsetWidth || 0;
8779             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8780         },
8781
8782         /**
8783          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8784          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8785          * if a height has not been set using CSS.
8786          * @return {Number}
8787          */
8788         getComputedHeight : function(){
8789             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8790             if(!h){
8791                 h = parseInt(this.getStyle('height'), 10) || 0;
8792                 if(!this.isBorderBox()){
8793                     h += this.getFrameWidth('tb');
8794                 }
8795             }
8796             return h;
8797         },
8798
8799         /**
8800          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8801          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8802          * if a width has not been set using CSS.
8803          * @return {Number}
8804          */
8805         getComputedWidth : function(){
8806             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8807             if(!w){
8808                 w = parseInt(this.getStyle('width'), 10) || 0;
8809                 if(!this.isBorderBox()){
8810                     w += this.getFrameWidth('lr');
8811                 }
8812             }
8813             return w;
8814         },
8815
8816         /**
8817          * Returns the size of the element.
8818          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8819          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8820          */
8821         getSize : function(contentSize){
8822             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8823         },
8824
8825         /**
8826          * Returns the width and height of the viewport.
8827          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8828          */
8829         getViewSize : function(){
8830             var d = this.dom, doc = document, aw = 0, ah = 0;
8831             if(d == doc || d == doc.body){
8832                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8833             }else{
8834                 return {
8835                     width : d.clientWidth,
8836                     height: d.clientHeight
8837                 };
8838             }
8839         },
8840
8841         /**
8842          * Returns the value of the "value" attribute
8843          * @param {Boolean} asNumber true to parse the value as a number
8844          * @return {String/Number}
8845          */
8846         getValue : function(asNumber){
8847             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8848         },
8849
8850         // private
8851         adjustWidth : function(width){
8852             if(typeof width == "number"){
8853                 if(this.autoBoxAdjust && !this.isBorderBox()){
8854                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8855                 }
8856                 if(width < 0){
8857                     width = 0;
8858                 }
8859             }
8860             return width;
8861         },
8862
8863         // private
8864         adjustHeight : function(height){
8865             if(typeof height == "number"){
8866                if(this.autoBoxAdjust && !this.isBorderBox()){
8867                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8868                }
8869                if(height < 0){
8870                    height = 0;
8871                }
8872             }
8873             return height;
8874         },
8875
8876         /**
8877          * Set the width of the element
8878          * @param {Number} width The new width
8879          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8880          * @return {Roo.Element} this
8881          */
8882         setWidth : function(width, animate){
8883             width = this.adjustWidth(width);
8884             if(!animate || !A){
8885                 this.dom.style.width = this.addUnits(width);
8886             }else{
8887                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8888             }
8889             return this;
8890         },
8891
8892         /**
8893          * Set the height of the element
8894          * @param {Number} height The new height
8895          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8896          * @return {Roo.Element} this
8897          */
8898          setHeight : function(height, animate){
8899             height = this.adjustHeight(height);
8900             if(!animate || !A){
8901                 this.dom.style.height = this.addUnits(height);
8902             }else{
8903                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8904             }
8905             return this;
8906         },
8907
8908         /**
8909          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8910          * @param {Number} width The new width
8911          * @param {Number} height The new height
8912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8913          * @return {Roo.Element} this
8914          */
8915          setSize : function(width, height, animate){
8916             if(typeof width == "object"){ // in case of object from getSize()
8917                 height = width.height; width = width.width;
8918             }
8919             width = this.adjustWidth(width); height = this.adjustHeight(height);
8920             if(!animate || !A){
8921                 this.dom.style.width = this.addUnits(width);
8922                 this.dom.style.height = this.addUnits(height);
8923             }else{
8924                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8925             }
8926             return this;
8927         },
8928
8929         /**
8930          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8931          * @param {Number} x X value for new position (coordinates are page-based)
8932          * @param {Number} y Y value for new position (coordinates are page-based)
8933          * @param {Number} width The new width
8934          * @param {Number} height The new height
8935          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8936          * @return {Roo.Element} this
8937          */
8938         setBounds : function(x, y, width, height, animate){
8939             if(!animate || !A){
8940                 this.setSize(width, height);
8941                 this.setLocation(x, y);
8942             }else{
8943                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8944                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8945                               this.preanim(arguments, 4), 'motion');
8946             }
8947             return this;
8948         },
8949
8950         /**
8951          * 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.
8952          * @param {Roo.lib.Region} region The region to fill
8953          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8954          * @return {Roo.Element} this
8955          */
8956         setRegion : function(region, animate){
8957             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8958             return this;
8959         },
8960
8961         /**
8962          * Appends an event handler
8963          *
8964          * @param {String}   eventName     The type of event to append
8965          * @param {Function} fn        The method the event invokes
8966          * @param {Object} scope       (optional) The scope (this object) of the fn
8967          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8968          */
8969         addListener : function(eventName, fn, scope, options)
8970         {
8971             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8972                 this.addListener('touchstart', this.onTapHandler, this);
8973             }
8974             
8975             // we need to handle a special case where dom element is a svg element.
8976             // in this case we do not actua
8977             if (!this.dom) {
8978                 return;
8979             }
8980             
8981             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8982                 if (typeof(this.listeners[eventName]) == 'undefined') {
8983                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8984                 }
8985                 this.listeners[eventName].addListener(fn, scope, options);
8986                 return;
8987             }
8988             
8989                 
8990             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8991             
8992             
8993         },
8994         tapedTwice : false,
8995         onTapHandler : function(event)
8996         {
8997             if(!this.tapedTwice) {
8998                 this.tapedTwice = true;
8999                 var s = this;
9000                 setTimeout( function() {
9001                     s.tapedTwice = false;
9002                 }, 300 );
9003                 return;
9004             }
9005             event.preventDefault();
9006             var revent = new MouseEvent('dblclick',  {
9007                 view: window,
9008                 bubbles: true,
9009                 cancelable: true
9010             });
9011              
9012             this.dom.dispatchEvent(revent);
9013             //action on double tap goes below
9014              
9015         }, 
9016  
9017         /**
9018          * Removes an event handler from this element
9019          * @param {String} eventName the type of event to remove
9020          * @param {Function} fn the method the event invokes
9021          * @param {Function} scope (needed for svg fake listeners)
9022          * @return {Roo.Element} this
9023          */
9024         removeListener : function(eventName, fn, scope){
9025             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9026             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9027                 return this;
9028             }
9029             this.listeners[eventName].removeListener(fn, scope);
9030             return this;
9031         },
9032
9033         /**
9034          * Removes all previous added listeners from this element
9035          * @return {Roo.Element} this
9036          */
9037         removeAllListeners : function(){
9038             E.purgeElement(this.dom);
9039             this.listeners = {};
9040             return this;
9041         },
9042
9043         relayEvent : function(eventName, observable){
9044             this.on(eventName, function(e){
9045                 observable.fireEvent(eventName, e);
9046             });
9047         },
9048
9049         
9050         /**
9051          * Set the opacity of the element
9052          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9053          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9054          * @return {Roo.Element} this
9055          */
9056          setOpacity : function(opacity, animate){
9057             if(!animate || !A){
9058                 var s = this.dom.style;
9059                 if(Roo.isIE){
9060                     s.zoom = 1;
9061                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9062                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9063                 }else{
9064                     s.opacity = opacity;
9065                 }
9066             }else{
9067                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9068             }
9069             return this;
9070         },
9071
9072         /**
9073          * Gets the left X coordinate
9074          * @param {Boolean} local True to get the local css position instead of page coordinate
9075          * @return {Number}
9076          */
9077         getLeft : function(local){
9078             if(!local){
9079                 return this.getX();
9080             }else{
9081                 return parseInt(this.getStyle("left"), 10) || 0;
9082             }
9083         },
9084
9085         /**
9086          * Gets the right X coordinate of the element (element X position + element width)
9087          * @param {Boolean} local True to get the local css position instead of page coordinate
9088          * @return {Number}
9089          */
9090         getRight : function(local){
9091             if(!local){
9092                 return this.getX() + this.getWidth();
9093             }else{
9094                 return (this.getLeft(true) + this.getWidth()) || 0;
9095             }
9096         },
9097
9098         /**
9099          * Gets the top Y coordinate
9100          * @param {Boolean} local True to get the local css position instead of page coordinate
9101          * @return {Number}
9102          */
9103         getTop : function(local) {
9104             if(!local){
9105                 return this.getY();
9106             }else{
9107                 return parseInt(this.getStyle("top"), 10) || 0;
9108             }
9109         },
9110
9111         /**
9112          * Gets the bottom Y coordinate of the element (element Y position + element height)
9113          * @param {Boolean} local True to get the local css position instead of page coordinate
9114          * @return {Number}
9115          */
9116         getBottom : function(local){
9117             if(!local){
9118                 return this.getY() + this.getHeight();
9119             }else{
9120                 return (this.getTop(true) + this.getHeight()) || 0;
9121             }
9122         },
9123
9124         /**
9125         * Initializes positioning on this element. If a desired position is not passed, it will make the
9126         * the element positioned relative IF it is not already positioned.
9127         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9128         * @param {Number} zIndex (optional) The zIndex to apply
9129         * @param {Number} x (optional) Set the page X position
9130         * @param {Number} y (optional) Set the page Y position
9131         */
9132         position : function(pos, zIndex, x, y){
9133             if(!pos){
9134                if(this.getStyle('position') == 'static'){
9135                    this.setStyle('position', 'relative');
9136                }
9137             }else{
9138                 this.setStyle("position", pos);
9139             }
9140             if(zIndex){
9141                 this.setStyle("z-index", zIndex);
9142             }
9143             if(x !== undefined && y !== undefined){
9144                 this.setXY([x, y]);
9145             }else if(x !== undefined){
9146                 this.setX(x);
9147             }else if(y !== undefined){
9148                 this.setY(y);
9149             }
9150         },
9151
9152         /**
9153         * Clear positioning back to the default when the document was loaded
9154         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9155         * @return {Roo.Element} this
9156          */
9157         clearPositioning : function(value){
9158             value = value ||'';
9159             this.setStyle({
9160                 "left": value,
9161                 "right": value,
9162                 "top": value,
9163                 "bottom": value,
9164                 "z-index": "",
9165                 "position" : "static"
9166             });
9167             return this;
9168         },
9169
9170         /**
9171         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9172         * snapshot before performing an update and then restoring the element.
9173         * @return {Object}
9174         */
9175         getPositioning : function(){
9176             var l = this.getStyle("left");
9177             var t = this.getStyle("top");
9178             return {
9179                 "position" : this.getStyle("position"),
9180                 "left" : l,
9181                 "right" : l ? "" : this.getStyle("right"),
9182                 "top" : t,
9183                 "bottom" : t ? "" : this.getStyle("bottom"),
9184                 "z-index" : this.getStyle("z-index")
9185             };
9186         },
9187
9188         /**
9189          * Gets the width of the border(s) for the specified side(s)
9190          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9191          * passing lr would get the border (l)eft width + the border (r)ight width.
9192          * @return {Number} The width of the sides passed added together
9193          */
9194         getBorderWidth : function(side){
9195             return this.addStyles(side, El.borders);
9196         },
9197
9198         /**
9199          * Gets the width of the padding(s) for the specified side(s)
9200          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9201          * passing lr would get the padding (l)eft + the padding (r)ight.
9202          * @return {Number} The padding of the sides passed added together
9203          */
9204         getPadding : function(side){
9205             return this.addStyles(side, El.paddings);
9206         },
9207
9208         /**
9209         * Set positioning with an object returned by getPositioning().
9210         * @param {Object} posCfg
9211         * @return {Roo.Element} this
9212          */
9213         setPositioning : function(pc){
9214             this.applyStyles(pc);
9215             if(pc.right == "auto"){
9216                 this.dom.style.right = "";
9217             }
9218             if(pc.bottom == "auto"){
9219                 this.dom.style.bottom = "";
9220             }
9221             return this;
9222         },
9223
9224         // private
9225         fixDisplay : function(){
9226             if(this.getStyle("display") == "none"){
9227                 this.setStyle("visibility", "hidden");
9228                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9229                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9230                     this.setStyle("display", "block");
9231                 }
9232             }
9233         },
9234
9235         /**
9236          * Quick set left and top adding default units
9237          * @param {String} left The left CSS property value
9238          * @param {String} top The top CSS property value
9239          * @return {Roo.Element} this
9240          */
9241          setLeftTop : function(left, top){
9242             this.dom.style.left = this.addUnits(left);
9243             this.dom.style.top = this.addUnits(top);
9244             return this;
9245         },
9246
9247         /**
9248          * Move this element relative to its current position.
9249          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9250          * @param {Number} distance How far to move the element in pixels
9251          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9252          * @return {Roo.Element} this
9253          */
9254          move : function(direction, distance, animate){
9255             var xy = this.getXY();
9256             direction = direction.toLowerCase();
9257             switch(direction){
9258                 case "l":
9259                 case "left":
9260                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9261                     break;
9262                case "r":
9263                case "right":
9264                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9265                     break;
9266                case "t":
9267                case "top":
9268                case "up":
9269                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9270                     break;
9271                case "b":
9272                case "bottom":
9273                case "down":
9274                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9275                     break;
9276             }
9277             return this;
9278         },
9279
9280         /**
9281          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9282          * @return {Roo.Element} this
9283          */
9284         clip : function(){
9285             if(!this.isClipped){
9286                this.isClipped = true;
9287                this.originalClip = {
9288                    "o": this.getStyle("overflow"),
9289                    "x": this.getStyle("overflow-x"),
9290                    "y": this.getStyle("overflow-y")
9291                };
9292                this.setStyle("overflow", "hidden");
9293                this.setStyle("overflow-x", "hidden");
9294                this.setStyle("overflow-y", "hidden");
9295             }
9296             return this;
9297         },
9298
9299         /**
9300          *  Return clipping (overflow) to original clipping before clip() was called
9301          * @return {Roo.Element} this
9302          */
9303         unclip : function(){
9304             if(this.isClipped){
9305                 this.isClipped = false;
9306                 var o = this.originalClip;
9307                 if(o.o){this.setStyle("overflow", o.o);}
9308                 if(o.x){this.setStyle("overflow-x", o.x);}
9309                 if(o.y){this.setStyle("overflow-y", o.y);}
9310             }
9311             return this;
9312         },
9313
9314
9315         /**
9316          * Gets the x,y coordinates specified by the anchor position on the element.
9317          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9318          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9319          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9320          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9321          * @return {Array} [x, y] An array containing the element's x and y coordinates
9322          */
9323         getAnchorXY : function(anchor, local, s){
9324             //Passing a different size is useful for pre-calculating anchors,
9325             //especially for anchored animations that change the el size.
9326
9327             var w, h, vp = false;
9328             if(!s){
9329                 var d = this.dom;
9330                 if(d == document.body || d == document){
9331                     vp = true;
9332                     w = D.getViewWidth(); h = D.getViewHeight();
9333                 }else{
9334                     w = this.getWidth(); h = this.getHeight();
9335                 }
9336             }else{
9337                 w = s.width;  h = s.height;
9338             }
9339             var x = 0, y = 0, r = Math.round;
9340             switch((anchor || "tl").toLowerCase()){
9341                 case "c":
9342                     x = r(w*.5);
9343                     y = r(h*.5);
9344                 break;
9345                 case "t":
9346                     x = r(w*.5);
9347                     y = 0;
9348                 break;
9349                 case "l":
9350                     x = 0;
9351                     y = r(h*.5);
9352                 break;
9353                 case "r":
9354                     x = w;
9355                     y = r(h*.5);
9356                 break;
9357                 case "b":
9358                     x = r(w*.5);
9359                     y = h;
9360                 break;
9361                 case "tl":
9362                     x = 0;
9363                     y = 0;
9364                 break;
9365                 case "bl":
9366                     x = 0;
9367                     y = h;
9368                 break;
9369                 case "br":
9370                     x = w;
9371                     y = h;
9372                 break;
9373                 case "tr":
9374                     x = w;
9375                     y = 0;
9376                 break;
9377             }
9378             if(local === true){
9379                 return [x, y];
9380             }
9381             if(vp){
9382                 var sc = this.getScroll();
9383                 return [x + sc.left, y + sc.top];
9384             }
9385             //Add the element's offset xy
9386             var o = this.getXY();
9387             return [x+o[0], y+o[1]];
9388         },
9389
9390         /**
9391          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9392          * supported position values.
9393          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9394          * @param {String} position The position to align to.
9395          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9396          * @return {Array} [x, y]
9397          */
9398         getAlignToXY : function(el, p, o)
9399         {
9400             el = Roo.get(el);
9401             var d = this.dom;
9402             if(!el.dom){
9403                 throw "Element.alignTo with an element that doesn't exist";
9404             }
9405             var c = false; //constrain to viewport
9406             var p1 = "", p2 = "";
9407             o = o || [0,0];
9408
9409             if(!p){
9410                 p = "tl-bl";
9411             }else if(p == "?"){
9412                 p = "tl-bl?";
9413             }else if(p.indexOf("-") == -1){
9414                 p = "tl-" + p;
9415             }
9416             p = p.toLowerCase();
9417             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9418             if(!m){
9419                throw "Element.alignTo with an invalid alignment " + p;
9420             }
9421             p1 = m[1]; p2 = m[2]; c = !!m[3];
9422
9423             //Subtract the aligned el's internal xy from the target's offset xy
9424             //plus custom offset to get the aligned el's new offset xy
9425             var a1 = this.getAnchorXY(p1, true);
9426             var a2 = el.getAnchorXY(p2, false);
9427             var x = a2[0] - a1[0] + o[0];
9428             var y = a2[1] - a1[1] + o[1];
9429             if(c){
9430                 //constrain the aligned el to viewport if necessary
9431                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9432                 // 5px of margin for ie
9433                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9434
9435                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9436                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9437                 //otherwise swap the aligned el to the opposite border of the target.
9438                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9439                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9440                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9441                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9442
9443                var doc = document;
9444                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9445                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9446
9447                if((x+w) > dw + scrollX){
9448                     x = swapX ? r.left-w : dw+scrollX-w;
9449                 }
9450                if(x < scrollX){
9451                    x = swapX ? r.right : scrollX;
9452                }
9453                if((y+h) > dh + scrollY){
9454                     y = swapY ? r.top-h : dh+scrollY-h;
9455                 }
9456                if (y < scrollY){
9457                    y = swapY ? r.bottom : scrollY;
9458                }
9459             }
9460             return [x,y];
9461         },
9462
9463         // private
9464         getConstrainToXY : function(){
9465             var os = {top:0, left:0, bottom:0, right: 0};
9466
9467             return function(el, local, offsets, proposedXY){
9468                 el = Roo.get(el);
9469                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9470
9471                 var vw, vh, vx = 0, vy = 0;
9472                 if(el.dom == document.body || el.dom == document){
9473                     vw = Roo.lib.Dom.getViewWidth();
9474                     vh = Roo.lib.Dom.getViewHeight();
9475                 }else{
9476                     vw = el.dom.clientWidth;
9477                     vh = el.dom.clientHeight;
9478                     if(!local){
9479                         var vxy = el.getXY();
9480                         vx = vxy[0];
9481                         vy = vxy[1];
9482                     }
9483                 }
9484
9485                 var s = el.getScroll();
9486
9487                 vx += offsets.left + s.left;
9488                 vy += offsets.top + s.top;
9489
9490                 vw -= offsets.right;
9491                 vh -= offsets.bottom;
9492
9493                 var vr = vx+vw;
9494                 var vb = vy+vh;
9495
9496                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9497                 var x = xy[0], y = xy[1];
9498                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9499
9500                 // only move it if it needs it
9501                 var moved = false;
9502
9503                 // first validate right/bottom
9504                 if((x + w) > vr){
9505                     x = vr - w;
9506                     moved = true;
9507                 }
9508                 if((y + h) > vb){
9509                     y = vb - h;
9510                     moved = true;
9511                 }
9512                 // then make sure top/left isn't negative
9513                 if(x < vx){
9514                     x = vx;
9515                     moved = true;
9516                 }
9517                 if(y < vy){
9518                     y = vy;
9519                     moved = true;
9520                 }
9521                 return moved ? [x, y] : false;
9522             };
9523         }(),
9524
9525         // private
9526         adjustForConstraints : function(xy, parent, offsets){
9527             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9528         },
9529
9530         /**
9531          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9532          * document it aligns it to the viewport.
9533          * The position parameter is optional, and can be specified in any one of the following formats:
9534          * <ul>
9535          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9536          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9537          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9538          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9539          *   <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
9540          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9541          * </ul>
9542          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9543          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9544          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9545          * that specified in order to enforce the viewport constraints.
9546          * Following are all of the supported anchor positions:
9547     <pre>
9548     Value  Description
9549     -----  -----------------------------
9550     tl     The top left corner (default)
9551     t      The center of the top edge
9552     tr     The top right corner
9553     l      The center of the left edge
9554     c      In the center of the element
9555     r      The center of the right edge
9556     bl     The bottom left corner
9557     b      The center of the bottom edge
9558     br     The bottom right corner
9559     </pre>
9560     Example Usage:
9561     <pre><code>
9562     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9563     el.alignTo("other-el");
9564
9565     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9566     el.alignTo("other-el", "tr?");
9567
9568     // align the bottom right corner of el with the center left edge of other-el
9569     el.alignTo("other-el", "br-l?");
9570
9571     // align the center of el with the bottom left corner of other-el and
9572     // adjust the x position by -6 pixels (and the y position by 0)
9573     el.alignTo("other-el", "c-bl", [-6, 0]);
9574     </code></pre>
9575          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9576          * @param {String} position The position to align to.
9577          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9578          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9579          * @return {Roo.Element} this
9580          */
9581         alignTo : function(element, position, offsets, animate){
9582             var xy = this.getAlignToXY(element, position, offsets);
9583             this.setXY(xy, this.preanim(arguments, 3));
9584             return this;
9585         },
9586
9587         /**
9588          * Anchors an element to another element and realigns it when the window is resized.
9589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9590          * @param {String} position The position to align to.
9591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9592          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9593          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9594          * is a number, it is used as the buffer delay (defaults to 50ms).
9595          * @param {Function} callback The function to call after the animation finishes
9596          * @return {Roo.Element} this
9597          */
9598         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9599             var action = function(){
9600                 this.alignTo(el, alignment, offsets, animate);
9601                 Roo.callback(callback, this);
9602             };
9603             Roo.EventManager.onWindowResize(action, this);
9604             var tm = typeof monitorScroll;
9605             if(tm != 'undefined'){
9606                 Roo.EventManager.on(window, 'scroll', action, this,
9607                     {buffer: tm == 'number' ? monitorScroll : 50});
9608             }
9609             action.call(this); // align immediately
9610             return this;
9611         },
9612         /**
9613          * Clears any opacity settings from this element. Required in some cases for IE.
9614          * @return {Roo.Element} this
9615          */
9616         clearOpacity : function(){
9617             if (window.ActiveXObject) {
9618                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9619                     this.dom.style.filter = "";
9620                 }
9621             } else {
9622                 this.dom.style.opacity = "";
9623                 this.dom.style["-moz-opacity"] = "";
9624                 this.dom.style["-khtml-opacity"] = "";
9625             }
9626             return this;
9627         },
9628
9629         /**
9630          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9631          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9632          * @return {Roo.Element} this
9633          */
9634         hide : function(animate){
9635             this.setVisible(false, this.preanim(arguments, 0));
9636             return this;
9637         },
9638
9639         /**
9640         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9641         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9642          * @return {Roo.Element} this
9643          */
9644         show : function(animate){
9645             this.setVisible(true, this.preanim(arguments, 0));
9646             return this;
9647         },
9648
9649         /**
9650          * @private Test if size has a unit, otherwise appends the default
9651          */
9652         addUnits : function(size){
9653             return Roo.Element.addUnits(size, this.defaultUnit);
9654         },
9655
9656         /**
9657          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9658          * @return {Roo.Element} this
9659          */
9660         beginMeasure : function(){
9661             var el = this.dom;
9662             if(el.offsetWidth || el.offsetHeight){
9663                 return this; // offsets work already
9664             }
9665             var changed = [];
9666             var p = this.dom, b = document.body; // start with this element
9667             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9668                 var pe = Roo.get(p);
9669                 if(pe.getStyle('display') == 'none'){
9670                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9671                     p.style.visibility = "hidden";
9672                     p.style.display = "block";
9673                 }
9674                 p = p.parentNode;
9675             }
9676             this._measureChanged = changed;
9677             return this;
9678
9679         },
9680
9681         /**
9682          * Restores displays to before beginMeasure was called
9683          * @return {Roo.Element} this
9684          */
9685         endMeasure : function(){
9686             var changed = this._measureChanged;
9687             if(changed){
9688                 for(var i = 0, len = changed.length; i < len; i++) {
9689                     var r = changed[i];
9690                     r.el.style.visibility = r.visibility;
9691                     r.el.style.display = "none";
9692                 }
9693                 this._measureChanged = null;
9694             }
9695             return this;
9696         },
9697
9698         /**
9699         * Update the innerHTML of this element, optionally searching for and processing scripts
9700         * @param {String} html The new HTML
9701         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9702         * @param {Function} callback For async script loading you can be noticed when the update completes
9703         * @return {Roo.Element} this
9704          */
9705         update : function(html, loadScripts, callback){
9706             if(typeof html == "undefined"){
9707                 html = "";
9708             }
9709             if(loadScripts !== true){
9710                 this.dom.innerHTML = html;
9711                 if(typeof callback == "function"){
9712                     callback();
9713                 }
9714                 return this;
9715             }
9716             var id = Roo.id();
9717             var dom = this.dom;
9718
9719             html += '<span id="' + id + '"></span>';
9720
9721             E.onAvailable(id, function(){
9722                 var hd = document.getElementsByTagName("head")[0];
9723                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9724                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9725                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9726
9727                 var match;
9728                 while(match = re.exec(html)){
9729                     var attrs = match[1];
9730                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9731                     if(srcMatch && srcMatch[2]){
9732                        var s = document.createElement("script");
9733                        s.src = srcMatch[2];
9734                        var typeMatch = attrs.match(typeRe);
9735                        if(typeMatch && typeMatch[2]){
9736                            s.type = typeMatch[2];
9737                        }
9738                        hd.appendChild(s);
9739                     }else if(match[2] && match[2].length > 0){
9740                         if(window.execScript) {
9741                            window.execScript(match[2]);
9742                         } else {
9743                             /**
9744                              * eval:var:id
9745                              * eval:var:dom
9746                              * eval:var:html
9747                              * 
9748                              */
9749                            window.eval(match[2]);
9750                         }
9751                     }
9752                 }
9753                 var el = document.getElementById(id);
9754                 if(el){el.parentNode.removeChild(el);}
9755                 if(typeof callback == "function"){
9756                     callback();
9757                 }
9758             });
9759             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9760             return this;
9761         },
9762
9763         /**
9764          * Direct access to the UpdateManager update() method (takes the same parameters).
9765          * @param {String/Function} url The url for this request or a function to call to get the url
9766          * @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}
9767          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9768          * @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.
9769          * @return {Roo.Element} this
9770          */
9771         load : function(){
9772             var um = this.getUpdateManager();
9773             um.update.apply(um, arguments);
9774             return this;
9775         },
9776
9777         /**
9778         * Gets this element's UpdateManager
9779         * @return {Roo.UpdateManager} The UpdateManager
9780         */
9781         getUpdateManager : function(){
9782             if(!this.updateManager){
9783                 this.updateManager = new Roo.UpdateManager(this);
9784             }
9785             return this.updateManager;
9786         },
9787
9788         /**
9789          * Disables text selection for this element (normalized across browsers)
9790          * @return {Roo.Element} this
9791          */
9792         unselectable : function(){
9793             this.dom.unselectable = "on";
9794             this.swallowEvent("selectstart", true);
9795             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9796             this.addClass("x-unselectable");
9797             return this;
9798         },
9799
9800         /**
9801         * Calculates the x, y to center this element on the screen
9802         * @return {Array} The x, y values [x, y]
9803         */
9804         getCenterXY : function(){
9805             return this.getAlignToXY(document, 'c-c');
9806         },
9807
9808         /**
9809         * Centers the Element in either the viewport, or another Element.
9810         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9811         */
9812         center : function(centerIn){
9813             this.alignTo(centerIn || document, 'c-c');
9814             return this;
9815         },
9816
9817         /**
9818          * Tests various css rules/browsers to determine if this element uses a border box
9819          * @return {Boolean}
9820          */
9821         isBorderBox : function(){
9822             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9823         },
9824
9825         /**
9826          * Return a box {x, y, width, height} that can be used to set another elements
9827          * size/location to match this element.
9828          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9829          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9830          * @return {Object} box An object in the format {x, y, width, height}
9831          */
9832         getBox : function(contentBox, local){
9833             var xy;
9834             if(!local){
9835                 xy = this.getXY();
9836             }else{
9837                 var left = parseInt(this.getStyle("left"), 10) || 0;
9838                 var top = parseInt(this.getStyle("top"), 10) || 0;
9839                 xy = [left, top];
9840             }
9841             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9842             if(!contentBox){
9843                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9844             }else{
9845                 var l = this.getBorderWidth("l")+this.getPadding("l");
9846                 var r = this.getBorderWidth("r")+this.getPadding("r");
9847                 var t = this.getBorderWidth("t")+this.getPadding("t");
9848                 var b = this.getBorderWidth("b")+this.getPadding("b");
9849                 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)};
9850             }
9851             bx.right = bx.x + bx.width;
9852             bx.bottom = bx.y + bx.height;
9853             return bx;
9854         },
9855
9856         /**
9857          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9858          for more information about the sides.
9859          * @param {String} sides
9860          * @return {Number}
9861          */
9862         getFrameWidth : function(sides, onlyContentBox){
9863             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9864         },
9865
9866         /**
9867          * 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.
9868          * @param {Object} box The box to fill {x, y, width, height}
9869          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9870          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9871          * @return {Roo.Element} this
9872          */
9873         setBox : function(box, adjust, animate){
9874             var w = box.width, h = box.height;
9875             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9876                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9877                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9878             }
9879             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9880             return this;
9881         },
9882
9883         /**
9884          * Forces the browser to repaint this element
9885          * @return {Roo.Element} this
9886          */
9887          repaint : function(){
9888             var dom = this.dom;
9889             this.addClass("x-repaint");
9890             setTimeout(function(){
9891                 Roo.get(dom).removeClass("x-repaint");
9892             }, 1);
9893             return this;
9894         },
9895
9896         /**
9897          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9898          * then it returns the calculated width of the sides (see getPadding)
9899          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9900          * @return {Object/Number}
9901          */
9902         getMargins : function(side){
9903             if(!side){
9904                 return {
9905                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9906                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9907                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9908                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9909                 };
9910             }else{
9911                 return this.addStyles(side, El.margins);
9912              }
9913         },
9914
9915         // private
9916         addStyles : function(sides, styles){
9917             var val = 0, v, w;
9918             for(var i = 0, len = sides.length; i < len; i++){
9919                 v = this.getStyle(styles[sides.charAt(i)]);
9920                 if(v){
9921                      w = parseInt(v, 10);
9922                      if(w){ val += w; }
9923                 }
9924             }
9925             return val;
9926         },
9927
9928         /**
9929          * Creates a proxy element of this element
9930          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9931          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9932          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9933          * @return {Roo.Element} The new proxy element
9934          */
9935         createProxy : function(config, renderTo, matchBox){
9936             if(renderTo){
9937                 renderTo = Roo.getDom(renderTo);
9938             }else{
9939                 renderTo = document.body;
9940             }
9941             config = typeof config == "object" ?
9942                 config : {tag : "div", cls: config};
9943             var proxy = Roo.DomHelper.append(renderTo, config, true);
9944             if(matchBox){
9945                proxy.setBox(this.getBox());
9946             }
9947             return proxy;
9948         },
9949
9950         /**
9951          * Puts a mask over this element to disable user interaction. Requires core.css.
9952          * This method can only be applied to elements which accept child nodes.
9953          * @param {String} msg (optional) A message to display in the mask
9954          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
9955          * @return {Element} The mask  element
9956          */
9957         mask : function(msg, msgCls)
9958         {
9959             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9960                 this.setStyle("position", "relative");
9961             }
9962             if(!this._mask){
9963                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9964             }
9965             
9966             this.addClass("x-masked");
9967             this._mask.setDisplayed(true);
9968             
9969             // we wander
9970             var z = 0;
9971             var dom = this.dom;
9972             while (dom && dom.style) {
9973                 if (!isNaN(parseInt(dom.style.zIndex))) {
9974                     z = Math.max(z, parseInt(dom.style.zIndex));
9975                 }
9976                 dom = dom.parentNode;
9977             }
9978             // if we are masking the body - then it hides everything..
9979             if (this.dom == document.body) {
9980                 z = 1000000;
9981                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9982                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9983             }
9984            
9985             if(typeof msg == 'string'){
9986                 if(!this._maskMsg){
9987                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9988                         cls: "roo-el-mask-msg", 
9989                         cn: [
9990                             {
9991                                 tag: 'i',
9992                                 cls: 'fa fa-spinner fa-spin'
9993                             },
9994                             {
9995                                 tag: 'div'
9996                             }   
9997                         ]
9998                     }, true);
9999                 }
10000                 var mm = this._maskMsg;
10001                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10002                 if (mm.dom.lastChild) { // weird IE issue?
10003                     mm.dom.lastChild.innerHTML = msg;
10004                 }
10005                 mm.setDisplayed(true);
10006                 mm.center(this);
10007                 mm.setStyle('z-index', z + 102);
10008             }
10009             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10010                 this._mask.setHeight(this.getHeight());
10011             }
10012             this._mask.setStyle('z-index', z + 100);
10013             
10014             return this._mask;
10015         },
10016
10017         /**
10018          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10019          * it is cached for reuse.
10020          */
10021         unmask : function(removeEl){
10022             if(this._mask){
10023                 if(removeEl === true){
10024                     this._mask.remove();
10025                     delete this._mask;
10026                     if(this._maskMsg){
10027                         this._maskMsg.remove();
10028                         delete this._maskMsg;
10029                     }
10030                 }else{
10031                     this._mask.setDisplayed(false);
10032                     if(this._maskMsg){
10033                         this._maskMsg.setDisplayed(false);
10034                     }
10035                 }
10036             }
10037             this.removeClass("x-masked");
10038         },
10039
10040         /**
10041          * Returns true if this element is masked
10042          * @return {Boolean}
10043          */
10044         isMasked : function(){
10045             return this._mask && this._mask.isVisible();
10046         },
10047
10048         /**
10049          * Creates an iframe shim for this element to keep selects and other windowed objects from
10050          * showing through.
10051          * @return {Roo.Element} The new shim element
10052          */
10053         createShim : function(){
10054             var el = document.createElement('iframe');
10055             el.frameBorder = 'no';
10056             el.className = 'roo-shim';
10057             if(Roo.isIE && Roo.isSecure){
10058                 el.src = Roo.SSL_SECURE_URL;
10059             }
10060             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10061             shim.autoBoxAdjust = false;
10062             return shim;
10063         },
10064
10065         /**
10066          * Removes this element from the DOM and deletes it from the cache
10067          */
10068         remove : function(){
10069             if(this.dom.parentNode){
10070                 this.dom.parentNode.removeChild(this.dom);
10071             }
10072             delete El.cache[this.dom.id];
10073         },
10074
10075         /**
10076          * Sets up event handlers to add and remove a css class when the mouse is over this element
10077          * @param {String} className
10078          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10079          * mouseout events for children elements
10080          * @return {Roo.Element} this
10081          */
10082         addClassOnOver : function(className, preventFlicker){
10083             this.on("mouseover", function(){
10084                 Roo.fly(this, '_internal').addClass(className);
10085             }, this.dom);
10086             var removeFn = function(e){
10087                 if(preventFlicker !== true || !e.within(this, true)){
10088                     Roo.fly(this, '_internal').removeClass(className);
10089                 }
10090             };
10091             this.on("mouseout", removeFn, this.dom);
10092             return this;
10093         },
10094
10095         /**
10096          * Sets up event handlers to add and remove a css class when this element has the focus
10097          * @param {String} className
10098          * @return {Roo.Element} this
10099          */
10100         addClassOnFocus : function(className){
10101             this.on("focus", function(){
10102                 Roo.fly(this, '_internal').addClass(className);
10103             }, this.dom);
10104             this.on("blur", function(){
10105                 Roo.fly(this, '_internal').removeClass(className);
10106             }, this.dom);
10107             return this;
10108         },
10109         /**
10110          * 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)
10111          * @param {String} className
10112          * @return {Roo.Element} this
10113          */
10114         addClassOnClick : function(className){
10115             var dom = this.dom;
10116             this.on("mousedown", function(){
10117                 Roo.fly(dom, '_internal').addClass(className);
10118                 var d = Roo.get(document);
10119                 var fn = function(){
10120                     Roo.fly(dom, '_internal').removeClass(className);
10121                     d.removeListener("mouseup", fn);
10122                 };
10123                 d.on("mouseup", fn);
10124             });
10125             return this;
10126         },
10127
10128         /**
10129          * Stops the specified event from bubbling and optionally prevents the default action
10130          * @param {String} eventName
10131          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10132          * @return {Roo.Element} this
10133          */
10134         swallowEvent : function(eventName, preventDefault){
10135             var fn = function(e){
10136                 e.stopPropagation();
10137                 if(preventDefault){
10138                     e.preventDefault();
10139                 }
10140             };
10141             if(eventName instanceof Array){
10142                 for(var i = 0, len = eventName.length; i < len; i++){
10143                      this.on(eventName[i], fn);
10144                 }
10145                 return this;
10146             }
10147             this.on(eventName, fn);
10148             return this;
10149         },
10150
10151         /**
10152          * @private
10153          */
10154         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10155
10156         /**
10157          * Sizes this element to its parent element's dimensions performing
10158          * neccessary box adjustments.
10159          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10160          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10161          * @return {Roo.Element} this
10162          */
10163         fitToParent : function(monitorResize, targetParent) {
10164           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10165           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10166           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10167             return this;
10168           }
10169           var p = Roo.get(targetParent || this.dom.parentNode);
10170           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10171           if (monitorResize === true) {
10172             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10173             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10174           }
10175           return this;
10176         },
10177
10178         /**
10179          * Gets the next sibling, skipping text nodes
10180          * @return {HTMLElement} The next sibling or null
10181          */
10182         getNextSibling : function(){
10183             var n = this.dom.nextSibling;
10184             while(n && n.nodeType != 1){
10185                 n = n.nextSibling;
10186             }
10187             return n;
10188         },
10189
10190         /**
10191          * Gets the previous sibling, skipping text nodes
10192          * @return {HTMLElement} The previous sibling or null
10193          */
10194         getPrevSibling : function(){
10195             var n = this.dom.previousSibling;
10196             while(n && n.nodeType != 1){
10197                 n = n.previousSibling;
10198             }
10199             return n;
10200         },
10201
10202
10203         /**
10204          * Appends the passed element(s) to this element
10205          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10206          * @return {Roo.Element} this
10207          */
10208         appendChild: function(el){
10209             el = Roo.get(el);
10210             el.appendTo(this);
10211             return this;
10212         },
10213
10214         /**
10215          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10216          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10217          * automatically generated with the specified attributes.
10218          * @param {HTMLElement} insertBefore (optional) a child element of this element
10219          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10220          * @return {Roo.Element} The new child element
10221          */
10222         createChild: function(config, insertBefore, returnDom){
10223             config = config || {tag:'div'};
10224             if(insertBefore){
10225                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10226             }
10227             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10228         },
10229
10230         /**
10231          * Appends this element to the passed element
10232          * @param {String/HTMLElement/Element} el The new parent element
10233          * @return {Roo.Element} this
10234          */
10235         appendTo: function(el){
10236             el = Roo.getDom(el);
10237             el.appendChild(this.dom);
10238             return this;
10239         },
10240
10241         /**
10242          * Inserts this element before the passed element in the DOM
10243          * @param {String/HTMLElement/Element} el The element to insert before
10244          * @return {Roo.Element} this
10245          */
10246         insertBefore: function(el){
10247             el = Roo.getDom(el);
10248             el.parentNode.insertBefore(this.dom, el);
10249             return this;
10250         },
10251
10252         /**
10253          * Inserts this element after the passed element in the DOM
10254          * @param {String/HTMLElement/Element} el The element to insert after
10255          * @return {Roo.Element} this
10256          */
10257         insertAfter: function(el){
10258             el = Roo.getDom(el);
10259             el.parentNode.insertBefore(this.dom, el.nextSibling);
10260             return this;
10261         },
10262
10263         /**
10264          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10265          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10266          * @return {Roo.Element} The new child
10267          */
10268         insertFirst: function(el, returnDom){
10269             el = el || {};
10270             if(typeof el == 'object' && !el.nodeType){ // dh config
10271                 return this.createChild(el, this.dom.firstChild, returnDom);
10272             }else{
10273                 el = Roo.getDom(el);
10274                 this.dom.insertBefore(el, this.dom.firstChild);
10275                 return !returnDom ? Roo.get(el) : el;
10276             }
10277         },
10278
10279         /**
10280          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10282          * @param {String} where (optional) 'before' or 'after' defaults to before
10283          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10284          * @return {Roo.Element} the inserted Element
10285          */
10286         insertSibling: function(el, where, returnDom){
10287             where = where ? where.toLowerCase() : 'before';
10288             el = el || {};
10289             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10290
10291             if(typeof el == 'object' && !el.nodeType){ // dh config
10292                 if(where == 'after' && !this.dom.nextSibling){
10293                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10294                 }else{
10295                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10296                 }
10297
10298             }else{
10299                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10300                             where == 'before' ? this.dom : this.dom.nextSibling);
10301                 if(!returnDom){
10302                     rt = Roo.get(rt);
10303                 }
10304             }
10305             return rt;
10306         },
10307
10308         /**
10309          * Creates and wraps this element with another element
10310          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10312          * @return {HTMLElement/Element} The newly created wrapper element
10313          */
10314         wrap: function(config, returnDom){
10315             if(!config){
10316                 config = {tag: "div"};
10317             }
10318             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10319             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10320             return newEl;
10321         },
10322
10323         /**
10324          * Replaces the passed element with this element
10325          * @param {String/HTMLElement/Element} el The element to replace
10326          * @return {Roo.Element} this
10327          */
10328         replace: function(el){
10329             el = Roo.get(el);
10330             this.insertBefore(el);
10331             el.remove();
10332             return this;
10333         },
10334
10335         /**
10336          * Inserts an html fragment into this element
10337          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10338          * @param {String} html The HTML fragment
10339          * @param {Boolean} returnEl True to return an Roo.Element
10340          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10341          */
10342         insertHtml : function(where, html, returnEl){
10343             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10344             return returnEl ? Roo.get(el) : el;
10345         },
10346
10347         /**
10348          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10349          * @param {Object} o The object with the attributes
10350          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10351          * @return {Roo.Element} this
10352          */
10353         set : function(o, useSet){
10354             var el = this.dom;
10355             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10356             for(var attr in o){
10357                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10358                 if(attr=="cls"){
10359                     el.className = o["cls"];
10360                 }else{
10361                     if(useSet) {
10362                         el.setAttribute(attr, o[attr]);
10363                     } else {
10364                         el[attr] = o[attr];
10365                     }
10366                 }
10367             }
10368             if(o.style){
10369                 Roo.DomHelper.applyStyles(el, o.style);
10370             }
10371             return this;
10372         },
10373
10374         /**
10375          * Convenience method for constructing a KeyMap
10376          * @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:
10377          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10378          * @param {Function} fn The function to call
10379          * @param {Object} scope (optional) The scope of the function
10380          * @return {Roo.KeyMap} The KeyMap created
10381          */
10382         addKeyListener : function(key, fn, scope){
10383             var config;
10384             if(typeof key != "object" || key instanceof Array){
10385                 config = {
10386                     key: key,
10387                     fn: fn,
10388                     scope: scope
10389                 };
10390             }else{
10391                 config = {
10392                     key : key.key,
10393                     shift : key.shift,
10394                     ctrl : key.ctrl,
10395                     alt : key.alt,
10396                     fn: fn,
10397                     scope: scope
10398                 };
10399             }
10400             return new Roo.KeyMap(this, config);
10401         },
10402
10403         /**
10404          * Creates a KeyMap for this element
10405          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10406          * @return {Roo.KeyMap} The KeyMap created
10407          */
10408         addKeyMap : function(config){
10409             return new Roo.KeyMap(this, config);
10410         },
10411
10412         /**
10413          * Returns true if this element is scrollable.
10414          * @return {Boolean}
10415          */
10416          isScrollable : function(){
10417             var dom = this.dom;
10418             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10419         },
10420
10421         /**
10422          * 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().
10423          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10424          * @param {Number} value The new scroll value
10425          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10426          * @return {Element} this
10427          */
10428
10429         scrollTo : function(side, value, animate){
10430             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10431             if(!animate || !A){
10432                 this.dom[prop] = value;
10433             }else{
10434                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10435                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10436             }
10437             return this;
10438         },
10439
10440         /**
10441          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10442          * within this element's scrollable range.
10443          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10444          * @param {Number} distance How far to scroll the element in pixels
10445          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10446          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10447          * was scrolled as far as it could go.
10448          */
10449          scroll : function(direction, distance, animate){
10450              if(!this.isScrollable()){
10451                  return;
10452              }
10453              var el = this.dom;
10454              var l = el.scrollLeft, t = el.scrollTop;
10455              var w = el.scrollWidth, h = el.scrollHeight;
10456              var cw = el.clientWidth, ch = el.clientHeight;
10457              direction = direction.toLowerCase();
10458              var scrolled = false;
10459              var a = this.preanim(arguments, 2);
10460              switch(direction){
10461                  case "l":
10462                  case "left":
10463                      if(w - l > cw){
10464                          var v = Math.min(l + distance, w-cw);
10465                          this.scrollTo("left", v, a);
10466                          scrolled = true;
10467                      }
10468                      break;
10469                 case "r":
10470                 case "right":
10471                      if(l > 0){
10472                          var v = Math.max(l - distance, 0);
10473                          this.scrollTo("left", v, a);
10474                          scrolled = true;
10475                      }
10476                      break;
10477                 case "t":
10478                 case "top":
10479                 case "up":
10480                      if(t > 0){
10481                          var v = Math.max(t - distance, 0);
10482                          this.scrollTo("top", v, a);
10483                          scrolled = true;
10484                      }
10485                      break;
10486                 case "b":
10487                 case "bottom":
10488                 case "down":
10489                      if(h - t > ch){
10490                          var v = Math.min(t + distance, h-ch);
10491                          this.scrollTo("top", v, a);
10492                          scrolled = true;
10493                      }
10494                      break;
10495              }
10496              return scrolled;
10497         },
10498
10499         /**
10500          * Translates the passed page coordinates into left/top css values for this element
10501          * @param {Number/Array} x The page x or an array containing [x, y]
10502          * @param {Number} y The page y
10503          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10504          */
10505         translatePoints : function(x, y){
10506             if(typeof x == 'object' || x instanceof Array){
10507                 y = x[1]; x = x[0];
10508             }
10509             var p = this.getStyle('position');
10510             var o = this.getXY();
10511
10512             var l = parseInt(this.getStyle('left'), 10);
10513             var t = parseInt(this.getStyle('top'), 10);
10514
10515             if(isNaN(l)){
10516                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10517             }
10518             if(isNaN(t)){
10519                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10520             }
10521
10522             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10523         },
10524
10525         /**
10526          * Returns the current scroll position of the element.
10527          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10528          */
10529         getScroll : function(){
10530             var d = this.dom, doc = document;
10531             if(d == doc || d == doc.body){
10532                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10533                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10534                 return {left: l, top: t};
10535             }else{
10536                 return {left: d.scrollLeft, top: d.scrollTop};
10537             }
10538         },
10539
10540         /**
10541          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10542          * are convert to standard 6 digit hex color.
10543          * @param {String} attr The css attribute
10544          * @param {String} defaultValue The default value to use when a valid color isn't found
10545          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10546          * YUI color anims.
10547          */
10548         getColor : function(attr, defaultValue, prefix){
10549             var v = this.getStyle(attr);
10550             if(!v || v == "transparent" || v == "inherit") {
10551                 return defaultValue;
10552             }
10553             var color = typeof prefix == "undefined" ? "#" : prefix;
10554             if(v.substr(0, 4) == "rgb("){
10555                 var rvs = v.slice(4, v.length -1).split(",");
10556                 for(var i = 0; i < 3; i++){
10557                     var h = parseInt(rvs[i]).toString(16);
10558                     if(h < 16){
10559                         h = "0" + h;
10560                     }
10561                     color += h;
10562                 }
10563             } else {
10564                 if(v.substr(0, 1) == "#"){
10565                     if(v.length == 4) {
10566                         for(var i = 1; i < 4; i++){
10567                             var c = v.charAt(i);
10568                             color +=  c + c;
10569                         }
10570                     }else if(v.length == 7){
10571                         color += v.substr(1);
10572                     }
10573                 }
10574             }
10575             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10576         },
10577
10578         /**
10579          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10580          * gradient background, rounded corners and a 4-way shadow.
10581          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10582          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10583          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10584          * @return {Roo.Element} this
10585          */
10586         boxWrap : function(cls){
10587             cls = cls || 'x-box';
10588             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10589             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10590             return el;
10591         },
10592
10593         /**
10594          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10595          * @param {String} namespace The namespace in which to look for the attribute
10596          * @param {String} name The attribute name
10597          * @return {String} The attribute value
10598          */
10599         getAttributeNS : Roo.isIE ? function(ns, name){
10600             var d = this.dom;
10601             var type = typeof d[ns+":"+name];
10602             if(type != 'undefined' && type != 'unknown'){
10603                 return d[ns+":"+name];
10604             }
10605             return d[name];
10606         } : function(ns, name){
10607             var d = this.dom;
10608             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10609         },
10610         
10611         
10612         /**
10613          * Sets or Returns the value the dom attribute value
10614          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10615          * @param {String} value (optional) The value to set the attribute to
10616          * @return {String} The attribute value
10617          */
10618         attr : function(name){
10619             if (arguments.length > 1) {
10620                 this.dom.setAttribute(name, arguments[1]);
10621                 return arguments[1];
10622             }
10623             if (typeof(name) == 'object') {
10624                 for(var i in name) {
10625                     this.attr(i, name[i]);
10626                 }
10627                 return name;
10628             }
10629             
10630             
10631             if (!this.dom.hasAttribute(name)) {
10632                 return undefined;
10633             }
10634             return this.dom.getAttribute(name);
10635         }
10636         
10637         
10638         
10639     };
10640
10641     var ep = El.prototype;
10642
10643     /**
10644      * Appends an event handler (Shorthand for addListener)
10645      * @param {String}   eventName     The type of event to append
10646      * @param {Function} fn        The method the event invokes
10647      * @param {Object} scope       (optional) The scope (this object) of the fn
10648      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10649      * @method
10650      */
10651     ep.on = ep.addListener;
10652         // backwards compat
10653     ep.mon = ep.addListener;
10654
10655     /**
10656      * Removes an event handler from this element (shorthand for removeListener)
10657      * @param {String} eventName the type of event to remove
10658      * @param {Function} fn the method the event invokes
10659      * @return {Roo.Element} this
10660      * @method
10661      */
10662     ep.un = ep.removeListener;
10663
10664     /**
10665      * true to automatically adjust width and height settings for box-model issues (default to true)
10666      */
10667     ep.autoBoxAdjust = true;
10668
10669     // private
10670     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10671
10672     // private
10673     El.addUnits = function(v, defaultUnit){
10674         if(v === "" || v == "auto"){
10675             return v;
10676         }
10677         if(v === undefined){
10678             return '';
10679         }
10680         if(typeof v == "number" || !El.unitPattern.test(v)){
10681             return v + (defaultUnit || 'px');
10682         }
10683         return v;
10684     };
10685
10686     // special markup used throughout Roo when box wrapping elements
10687     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>';
10688     /**
10689      * Visibility mode constant - Use visibility to hide element
10690      * @static
10691      * @type Number
10692      */
10693     El.VISIBILITY = 1;
10694     /**
10695      * Visibility mode constant - Use display to hide element
10696      * @static
10697      * @type Number
10698      */
10699     El.DISPLAY = 2;
10700
10701     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10702     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10703     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10704
10705
10706
10707     /**
10708      * @private
10709      */
10710     El.cache = {};
10711
10712     var docEl;
10713
10714     /**
10715      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10716      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10717      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10718      * @return {Element} The Element object
10719      * @static
10720      */
10721     El.get = function(el){
10722         var ex, elm, id;
10723         if(!el){ return null; }
10724         if(typeof el == "string"){ // element id
10725             if(!(elm = document.getElementById(el))){
10726                 return null;
10727             }
10728             if(ex = El.cache[el]){
10729                 ex.dom = elm;
10730             }else{
10731                 ex = El.cache[el] = new El(elm);
10732             }
10733             return ex;
10734         }else if(el.tagName){ // dom element
10735             if(!(id = el.id)){
10736                 id = Roo.id(el);
10737             }
10738             if(ex = El.cache[id]){
10739                 ex.dom = el;
10740             }else{
10741                 ex = El.cache[id] = new El(el);
10742             }
10743             return ex;
10744         }else if(el instanceof El){
10745             if(el != docEl){
10746                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10747                                                               // catch case where it hasn't been appended
10748                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10749             }
10750             return el;
10751         }else if(el.isComposite){
10752             return el;
10753         }else if(el instanceof Array){
10754             return El.select(el);
10755         }else if(el == document){
10756             // create a bogus element object representing the document object
10757             if(!docEl){
10758                 var f = function(){};
10759                 f.prototype = El.prototype;
10760                 docEl = new f();
10761                 docEl.dom = document;
10762             }
10763             return docEl;
10764         }
10765         return null;
10766     };
10767
10768     // private
10769     El.uncache = function(el){
10770         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10771             if(a[i]){
10772                 delete El.cache[a[i].id || a[i]];
10773             }
10774         }
10775     };
10776
10777     // private
10778     // Garbage collection - uncache elements/purge listeners on orphaned elements
10779     // so we don't hold a reference and cause the browser to retain them
10780     El.garbageCollect = function(){
10781         if(!Roo.enableGarbageCollector){
10782             clearInterval(El.collectorThread);
10783             return;
10784         }
10785         for(var eid in El.cache){
10786             var el = El.cache[eid], d = el.dom;
10787             // -------------------------------------------------------
10788             // Determining what is garbage:
10789             // -------------------------------------------------------
10790             // !d
10791             // dom node is null, definitely garbage
10792             // -------------------------------------------------------
10793             // !d.parentNode
10794             // no parentNode == direct orphan, definitely garbage
10795             // -------------------------------------------------------
10796             // !d.offsetParent && !document.getElementById(eid)
10797             // display none elements have no offsetParent so we will
10798             // also try to look it up by it's id. However, check
10799             // offsetParent first so we don't do unneeded lookups.
10800             // This enables collection of elements that are not orphans
10801             // directly, but somewhere up the line they have an orphan
10802             // parent.
10803             // -------------------------------------------------------
10804             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10805                 delete El.cache[eid];
10806                 if(d && Roo.enableListenerCollection){
10807                     E.purgeElement(d);
10808                 }
10809             }
10810         }
10811     }
10812     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10813
10814
10815     // dom is optional
10816     El.Flyweight = function(dom){
10817         this.dom = dom;
10818     };
10819     El.Flyweight.prototype = El.prototype;
10820
10821     El._flyweights = {};
10822     /**
10823      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10824      * the dom node can be overwritten by other code.
10825      * @param {String/HTMLElement} el The dom node or id
10826      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10827      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10828      * @static
10829      * @return {Element} The shared Element object
10830      */
10831     El.fly = function(el, named){
10832         named = named || '_global';
10833         el = Roo.getDom(el);
10834         if(!el){
10835             return null;
10836         }
10837         if(!El._flyweights[named]){
10838             El._flyweights[named] = new El.Flyweight();
10839         }
10840         El._flyweights[named].dom = el;
10841         return El._flyweights[named];
10842     };
10843
10844     /**
10845      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10846      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10847      * Shorthand of {@link Roo.Element#get}
10848      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10849      * @return {Element} The Element object
10850      * @member Roo
10851      * @method get
10852      */
10853     Roo.get = El.get;
10854     /**
10855      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10856      * the dom node can be overwritten by other code.
10857      * Shorthand of {@link Roo.Element#fly}
10858      * @param {String/HTMLElement} el The dom node or id
10859      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10860      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10861      * @static
10862      * @return {Element} The shared Element object
10863      * @member Roo
10864      * @method fly
10865      */
10866     Roo.fly = El.fly;
10867
10868     // speedy lookup for elements never to box adjust
10869     var noBoxAdjust = Roo.isStrict ? {
10870         select:1
10871     } : {
10872         input:1, select:1, textarea:1
10873     };
10874     if(Roo.isIE || Roo.isGecko){
10875         noBoxAdjust['button'] = 1;
10876     }
10877
10878
10879     Roo.EventManager.on(window, 'unload', function(){
10880         delete El.cache;
10881         delete El._flyweights;
10882     });
10883 })();
10884
10885
10886
10887
10888 if(Roo.DomQuery){
10889     Roo.Element.selectorFunction = Roo.DomQuery.select;
10890 }
10891
10892 Roo.Element.select = function(selector, unique, root){
10893     var els;
10894     if(typeof selector == "string"){
10895         els = Roo.Element.selectorFunction(selector, root);
10896     }else if(selector.length !== undefined){
10897         els = selector;
10898     }else{
10899         throw "Invalid selector";
10900     }
10901     if(unique === true){
10902         return new Roo.CompositeElement(els);
10903     }else{
10904         return new Roo.CompositeElementLite(els);
10905     }
10906 };
10907 /**
10908  * Selects elements based on the passed CSS selector to enable working on them as 1.
10909  * @param {String/Array} selector The CSS selector or an array of elements
10910  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10911  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10912  * @return {CompositeElementLite/CompositeElement}
10913  * @member Roo
10914  * @method select
10915  */
10916 Roo.select = Roo.Element.select;
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
10930
10931 /*
10932  * Based on:
10933  * Ext JS Library 1.1.1
10934  * Copyright(c) 2006-2007, Ext JS, LLC.
10935  *
10936  * Originally Released Under LGPL - original licence link has changed is not relivant.
10937  *
10938  * Fork - LGPL
10939  * <script type="text/javascript">
10940  */
10941
10942
10943
10944 //Notifies Element that fx methods are available
10945 Roo.enableFx = true;
10946
10947 /**
10948  * @class Roo.Fx
10949  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10950  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10951  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10952  * Element effects to work.</p><br/>
10953  *
10954  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10955  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10956  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10957  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10958  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10959  * expected results and should be done with care.</p><br/>
10960  *
10961  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10962  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10963 <pre>
10964 Value  Description
10965 -----  -----------------------------
10966 tl     The top left corner
10967 t      The center of the top edge
10968 tr     The top right corner
10969 l      The center of the left edge
10970 r      The center of the right edge
10971 bl     The bottom left corner
10972 b      The center of the bottom edge
10973 br     The bottom right corner
10974 </pre>
10975  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10976  * below are common options that can be passed to any Fx method.</b>
10977  * @cfg {Function} callback A function called when the effect is finished
10978  * @cfg {Object} scope The scope of the effect function
10979  * @cfg {String} easing A valid Easing value for the effect
10980  * @cfg {String} afterCls A css class to apply after the effect
10981  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10982  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10983  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10984  * effects that end with the element being visually hidden, ignored otherwise)
10985  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10986  * a function which returns such a specification that will be applied to the Element after the effect finishes
10987  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10988  * @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
10989  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10990  */
10991 Roo.Fx = {
10992         /**
10993          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10994          * origin for the slide effect.  This function automatically handles wrapping the element with
10995          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10996          * Usage:
10997          *<pre><code>
10998 // default: slide the element in from the top
10999 el.slideIn();
11000
11001 // custom: slide the element in from the right with a 2-second duration
11002 el.slideIn('r', { duration: 2 });
11003
11004 // common config options shown with default values
11005 el.slideIn('t', {
11006     easing: 'easeOut',
11007     duration: .5
11008 });
11009 </code></pre>
11010          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11011          * @param {Object} options (optional) Object literal with any of the Fx config options
11012          * @return {Roo.Element} The Element
11013          */
11014     slideIn : function(anchor, o){
11015         var el = this.getFxEl();
11016         o = o || {};
11017
11018         el.queueFx(o, function(){
11019
11020             anchor = anchor || "t";
11021
11022             // fix display to visibility
11023             this.fixDisplay();
11024
11025             // restore values after effect
11026             var r = this.getFxRestore();
11027             var b = this.getBox();
11028             // fixed size for slide
11029             this.setSize(b);
11030
11031             // wrap if needed
11032             var wrap = this.fxWrap(r.pos, o, "hidden");
11033
11034             var st = this.dom.style;
11035             st.visibility = "visible";
11036             st.position = "absolute";
11037
11038             // clear out temp styles after slide and unwrap
11039             var after = function(){
11040                 el.fxUnwrap(wrap, r.pos, o);
11041                 st.width = r.width;
11042                 st.height = r.height;
11043                 el.afterFx(o);
11044             };
11045             // time to calc the positions
11046             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11047
11048             switch(anchor.toLowerCase()){
11049                 case "t":
11050                     wrap.setSize(b.width, 0);
11051                     st.left = st.bottom = "0";
11052                     a = {height: bh};
11053                 break;
11054                 case "l":
11055                     wrap.setSize(0, b.height);
11056                     st.right = st.top = "0";
11057                     a = {width: bw};
11058                 break;
11059                 case "r":
11060                     wrap.setSize(0, b.height);
11061                     wrap.setX(b.right);
11062                     st.left = st.top = "0";
11063                     a = {width: bw, points: pt};
11064                 break;
11065                 case "b":
11066                     wrap.setSize(b.width, 0);
11067                     wrap.setY(b.bottom);
11068                     st.left = st.top = "0";
11069                     a = {height: bh, points: pt};
11070                 break;
11071                 case "tl":
11072                     wrap.setSize(0, 0);
11073                     st.right = st.bottom = "0";
11074                     a = {width: bw, height: bh};
11075                 break;
11076                 case "bl":
11077                     wrap.setSize(0, 0);
11078                     wrap.setY(b.y+b.height);
11079                     st.right = st.top = "0";
11080                     a = {width: bw, height: bh, points: pt};
11081                 break;
11082                 case "br":
11083                     wrap.setSize(0, 0);
11084                     wrap.setXY([b.right, b.bottom]);
11085                     st.left = st.top = "0";
11086                     a = {width: bw, height: bh, points: pt};
11087                 break;
11088                 case "tr":
11089                     wrap.setSize(0, 0);
11090                     wrap.setX(b.x+b.width);
11091                     st.left = st.bottom = "0";
11092                     a = {width: bw, height: bh, points: pt};
11093                 break;
11094             }
11095             this.dom.style.visibility = "visible";
11096             wrap.show();
11097
11098             arguments.callee.anim = wrap.fxanim(a,
11099                 o,
11100                 'motion',
11101                 .5,
11102                 'easeOut', after);
11103         });
11104         return this;
11105     },
11106     
11107         /**
11108          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11109          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11110          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11111          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11112          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11113          * Usage:
11114          *<pre><code>
11115 // default: slide the element out to the top
11116 el.slideOut();
11117
11118 // custom: slide the element out to the right with a 2-second duration
11119 el.slideOut('r', { duration: 2 });
11120
11121 // common config options shown with default values
11122 el.slideOut('t', {
11123     easing: 'easeOut',
11124     duration: .5,
11125     remove: false,
11126     useDisplay: false
11127 });
11128 </code></pre>
11129          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11130          * @param {Object} options (optional) Object literal with any of the Fx config options
11131          * @return {Roo.Element} The Element
11132          */
11133     slideOut : function(anchor, o){
11134         var el = this.getFxEl();
11135         o = o || {};
11136
11137         el.queueFx(o, function(){
11138
11139             anchor = anchor || "t";
11140
11141             // restore values after effect
11142             var r = this.getFxRestore();
11143             
11144             var b = this.getBox();
11145             // fixed size for slide
11146             this.setSize(b);
11147
11148             // wrap if needed
11149             var wrap = this.fxWrap(r.pos, o, "visible");
11150
11151             var st = this.dom.style;
11152             st.visibility = "visible";
11153             st.position = "absolute";
11154
11155             wrap.setSize(b);
11156
11157             var after = function(){
11158                 if(o.useDisplay){
11159                     el.setDisplayed(false);
11160                 }else{
11161                     el.hide();
11162                 }
11163
11164                 el.fxUnwrap(wrap, r.pos, o);
11165
11166                 st.width = r.width;
11167                 st.height = r.height;
11168
11169                 el.afterFx(o);
11170             };
11171
11172             var a, zero = {to: 0};
11173             switch(anchor.toLowerCase()){
11174                 case "t":
11175                     st.left = st.bottom = "0";
11176                     a = {height: zero};
11177                 break;
11178                 case "l":
11179                     st.right = st.top = "0";
11180                     a = {width: zero};
11181                 break;
11182                 case "r":
11183                     st.left = st.top = "0";
11184                     a = {width: zero, points: {to:[b.right, b.y]}};
11185                 break;
11186                 case "b":
11187                     st.left = st.top = "0";
11188                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11189                 break;
11190                 case "tl":
11191                     st.right = st.bottom = "0";
11192                     a = {width: zero, height: zero};
11193                 break;
11194                 case "bl":
11195                     st.right = st.top = "0";
11196                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11197                 break;
11198                 case "br":
11199                     st.left = st.top = "0";
11200                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11201                 break;
11202                 case "tr":
11203                     st.left = st.bottom = "0";
11204                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11205                 break;
11206             }
11207
11208             arguments.callee.anim = wrap.fxanim(a,
11209                 o,
11210                 'motion',
11211                 .5,
11212                 "easeOut", after);
11213         });
11214         return this;
11215     },
11216
11217         /**
11218          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11219          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11220          * The element must be removed from the DOM using the 'remove' config option if desired.
11221          * Usage:
11222          *<pre><code>
11223 // default
11224 el.puff();
11225
11226 // common config options shown with default values
11227 el.puff({
11228     easing: 'easeOut',
11229     duration: .5,
11230     remove: false,
11231     useDisplay: false
11232 });
11233 </code></pre>
11234          * @param {Object} options (optional) Object literal with any of the Fx config options
11235          * @return {Roo.Element} The Element
11236          */
11237     puff : function(o){
11238         var el = this.getFxEl();
11239         o = o || {};
11240
11241         el.queueFx(o, function(){
11242             this.clearOpacity();
11243             this.show();
11244
11245             // restore values after effect
11246             var r = this.getFxRestore();
11247             var st = this.dom.style;
11248
11249             var after = function(){
11250                 if(o.useDisplay){
11251                     el.setDisplayed(false);
11252                 }else{
11253                     el.hide();
11254                 }
11255
11256                 el.clearOpacity();
11257
11258                 el.setPositioning(r.pos);
11259                 st.width = r.width;
11260                 st.height = r.height;
11261                 st.fontSize = '';
11262                 el.afterFx(o);
11263             };
11264
11265             var width = this.getWidth();
11266             var height = this.getHeight();
11267
11268             arguments.callee.anim = this.fxanim({
11269                     width : {to: this.adjustWidth(width * 2)},
11270                     height : {to: this.adjustHeight(height * 2)},
11271                     points : {by: [-(width * .5), -(height * .5)]},
11272                     opacity : {to: 0},
11273                     fontSize: {to:200, unit: "%"}
11274                 },
11275                 o,
11276                 'motion',
11277                 .5,
11278                 "easeOut", after);
11279         });
11280         return this;
11281     },
11282
11283         /**
11284          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11285          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11286          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11287          * Usage:
11288          *<pre><code>
11289 // default
11290 el.switchOff();
11291
11292 // all config options shown with default values
11293 el.switchOff({
11294     easing: 'easeIn',
11295     duration: .3,
11296     remove: false,
11297     useDisplay: false
11298 });
11299 </code></pre>
11300          * @param {Object} options (optional) Object literal with any of the Fx config options
11301          * @return {Roo.Element} The Element
11302          */
11303     switchOff : function(o){
11304         var el = this.getFxEl();
11305         o = o || {};
11306
11307         el.queueFx(o, function(){
11308             this.clearOpacity();
11309             this.clip();
11310
11311             // restore values after effect
11312             var r = this.getFxRestore();
11313             var st = this.dom.style;
11314
11315             var after = function(){
11316                 if(o.useDisplay){
11317                     el.setDisplayed(false);
11318                 }else{
11319                     el.hide();
11320                 }
11321
11322                 el.clearOpacity();
11323                 el.setPositioning(r.pos);
11324                 st.width = r.width;
11325                 st.height = r.height;
11326
11327                 el.afterFx(o);
11328             };
11329
11330             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11331                 this.clearOpacity();
11332                 (function(){
11333                     this.fxanim({
11334                         height:{to:1},
11335                         points:{by:[0, this.getHeight() * .5]}
11336                     }, o, 'motion', 0.3, 'easeIn', after);
11337                 }).defer(100, this);
11338             });
11339         });
11340         return this;
11341     },
11342
11343     /**
11344      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11345      * changed using the "attr" config option) and then fading back to the original color. If no original
11346      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11347      * Usage:
11348 <pre><code>
11349 // default: highlight background to yellow
11350 el.highlight();
11351
11352 // custom: highlight foreground text to blue for 2 seconds
11353 el.highlight("0000ff", { attr: 'color', duration: 2 });
11354
11355 // common config options shown with default values
11356 el.highlight("ffff9c", {
11357     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11358     endColor: (current color) or "ffffff",
11359     easing: 'easeIn',
11360     duration: 1
11361 });
11362 </code></pre>
11363      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11364      * @param {Object} options (optional) Object literal with any of the Fx config options
11365      * @return {Roo.Element} The Element
11366      */ 
11367     highlight : function(color, o){
11368         var el = this.getFxEl();
11369         o = o || {};
11370
11371         el.queueFx(o, function(){
11372             color = color || "ffff9c";
11373             attr = o.attr || "backgroundColor";
11374
11375             this.clearOpacity();
11376             this.show();
11377
11378             var origColor = this.getColor(attr);
11379             var restoreColor = this.dom.style[attr];
11380             endColor = (o.endColor || origColor) || "ffffff";
11381
11382             var after = function(){
11383                 el.dom.style[attr] = restoreColor;
11384                 el.afterFx(o);
11385             };
11386
11387             var a = {};
11388             a[attr] = {from: color, to: endColor};
11389             arguments.callee.anim = this.fxanim(a,
11390                 o,
11391                 'color',
11392                 1,
11393                 'easeIn', after);
11394         });
11395         return this;
11396     },
11397
11398    /**
11399     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11400     * Usage:
11401 <pre><code>
11402 // default: a single light blue ripple
11403 el.frame();
11404
11405 // custom: 3 red ripples lasting 3 seconds total
11406 el.frame("ff0000", 3, { duration: 3 });
11407
11408 // common config options shown with default values
11409 el.frame("C3DAF9", 1, {
11410     duration: 1 //duration of entire animation (not each individual ripple)
11411     // Note: Easing is not configurable and will be ignored if included
11412 });
11413 </code></pre>
11414     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11415     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11416     * @param {Object} options (optional) Object literal with any of the Fx config options
11417     * @return {Roo.Element} The Element
11418     */
11419     frame : function(color, count, o){
11420         var el = this.getFxEl();
11421         o = o || {};
11422
11423         el.queueFx(o, function(){
11424             color = color || "#C3DAF9";
11425             if(color.length == 6){
11426                 color = "#" + color;
11427             }
11428             count = count || 1;
11429             duration = o.duration || 1;
11430             this.show();
11431
11432             var b = this.getBox();
11433             var animFn = function(){
11434                 var proxy = this.createProxy({
11435
11436                      style:{
11437                         visbility:"hidden",
11438                         position:"absolute",
11439                         "z-index":"35000", // yee haw
11440                         border:"0px solid " + color
11441                      }
11442                   });
11443                 var scale = Roo.isBorderBox ? 2 : 1;
11444                 proxy.animate({
11445                     top:{from:b.y, to:b.y - 20},
11446                     left:{from:b.x, to:b.x - 20},
11447                     borderWidth:{from:0, to:10},
11448                     opacity:{from:1, to:0},
11449                     height:{from:b.height, to:(b.height + (20*scale))},
11450                     width:{from:b.width, to:(b.width + (20*scale))}
11451                 }, duration, function(){
11452                     proxy.remove();
11453                 });
11454                 if(--count > 0){
11455                      animFn.defer((duration/2)*1000, this);
11456                 }else{
11457                     el.afterFx(o);
11458                 }
11459             };
11460             animFn.call(this);
11461         });
11462         return this;
11463     },
11464
11465    /**
11466     * Creates a pause before any subsequent queued effects begin.  If there are
11467     * no effects queued after the pause it will have no effect.
11468     * Usage:
11469 <pre><code>
11470 el.pause(1);
11471 </code></pre>
11472     * @param {Number} seconds The length of time to pause (in seconds)
11473     * @return {Roo.Element} The Element
11474     */
11475     pause : function(seconds){
11476         var el = this.getFxEl();
11477         var o = {};
11478
11479         el.queueFx(o, function(){
11480             setTimeout(function(){
11481                 el.afterFx(o);
11482             }, seconds * 1000);
11483         });
11484         return this;
11485     },
11486
11487    /**
11488     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11489     * using the "endOpacity" config option.
11490     * Usage:
11491 <pre><code>
11492 // default: fade in from opacity 0 to 100%
11493 el.fadeIn();
11494
11495 // custom: fade in from opacity 0 to 75% over 2 seconds
11496 el.fadeIn({ endOpacity: .75, duration: 2});
11497
11498 // common config options shown with default values
11499 el.fadeIn({
11500     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11501     easing: 'easeOut',
11502     duration: .5
11503 });
11504 </code></pre>
11505     * @param {Object} options (optional) Object literal with any of the Fx config options
11506     * @return {Roo.Element} The Element
11507     */
11508     fadeIn : function(o){
11509         var el = this.getFxEl();
11510         o = o || {};
11511         el.queueFx(o, function(){
11512             this.setOpacity(0);
11513             this.fixDisplay();
11514             this.dom.style.visibility = 'visible';
11515             var to = o.endOpacity || 1;
11516             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11517                 o, null, .5, "easeOut", function(){
11518                 if(to == 1){
11519                     this.clearOpacity();
11520                 }
11521                 el.afterFx(o);
11522             });
11523         });
11524         return this;
11525     },
11526
11527    /**
11528     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11529     * using the "endOpacity" config option.
11530     * Usage:
11531 <pre><code>
11532 // default: fade out from the element's current opacity to 0
11533 el.fadeOut();
11534
11535 // custom: fade out from the element's current opacity to 25% over 2 seconds
11536 el.fadeOut({ endOpacity: .25, duration: 2});
11537
11538 // common config options shown with default values
11539 el.fadeOut({
11540     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11541     easing: 'easeOut',
11542     duration: .5
11543     remove: false,
11544     useDisplay: false
11545 });
11546 </code></pre>
11547     * @param {Object} options (optional) Object literal with any of the Fx config options
11548     * @return {Roo.Element} The Element
11549     */
11550     fadeOut : function(o){
11551         var el = this.getFxEl();
11552         o = o || {};
11553         el.queueFx(o, function(){
11554             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11555                 o, null, .5, "easeOut", function(){
11556                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11557                      this.dom.style.display = "none";
11558                 }else{
11559                      this.dom.style.visibility = "hidden";
11560                 }
11561                 this.clearOpacity();
11562                 el.afterFx(o);
11563             });
11564         });
11565         return this;
11566     },
11567
11568    /**
11569     * Animates the transition of an element's dimensions from a starting height/width
11570     * to an ending height/width.
11571     * Usage:
11572 <pre><code>
11573 // change height and width to 100x100 pixels
11574 el.scale(100, 100);
11575
11576 // common config options shown with default values.  The height and width will default to
11577 // the element's existing values if passed as null.
11578 el.scale(
11579     [element's width],
11580     [element's height], {
11581     easing: 'easeOut',
11582     duration: .35
11583 });
11584 </code></pre>
11585     * @param {Number} width  The new width (pass undefined to keep the original width)
11586     * @param {Number} height  The new height (pass undefined to keep the original height)
11587     * @param {Object} options (optional) Object literal with any of the Fx config options
11588     * @return {Roo.Element} The Element
11589     */
11590     scale : function(w, h, o){
11591         this.shift(Roo.apply({}, o, {
11592             width: w,
11593             height: h
11594         }));
11595         return this;
11596     },
11597
11598    /**
11599     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11600     * Any of these properties not specified in the config object will not be changed.  This effect 
11601     * requires that at least one new dimension, position or opacity setting must be passed in on
11602     * the config object in order for the function to have any effect.
11603     * Usage:
11604 <pre><code>
11605 // slide the element horizontally to x position 200 while changing the height and opacity
11606 el.shift({ x: 200, height: 50, opacity: .8 });
11607
11608 // common config options shown with default values.
11609 el.shift({
11610     width: [element's width],
11611     height: [element's height],
11612     x: [element's x position],
11613     y: [element's y position],
11614     opacity: [element's opacity],
11615     easing: 'easeOut',
11616     duration: .35
11617 });
11618 </code></pre>
11619     * @param {Object} options  Object literal with any of the Fx config options
11620     * @return {Roo.Element} The Element
11621     */
11622     shift : function(o){
11623         var el = this.getFxEl();
11624         o = o || {};
11625         el.queueFx(o, function(){
11626             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11627             if(w !== undefined){
11628                 a.width = {to: this.adjustWidth(w)};
11629             }
11630             if(h !== undefined){
11631                 a.height = {to: this.adjustHeight(h)};
11632             }
11633             if(x !== undefined || y !== undefined){
11634                 a.points = {to: [
11635                     x !== undefined ? x : this.getX(),
11636                     y !== undefined ? y : this.getY()
11637                 ]};
11638             }
11639             if(op !== undefined){
11640                 a.opacity = {to: op};
11641             }
11642             if(o.xy !== undefined){
11643                 a.points = {to: o.xy};
11644             }
11645             arguments.callee.anim = this.fxanim(a,
11646                 o, 'motion', .35, "easeOut", function(){
11647                 el.afterFx(o);
11648             });
11649         });
11650         return this;
11651     },
11652
11653         /**
11654          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11655          * ending point of the effect.
11656          * Usage:
11657          *<pre><code>
11658 // default: slide the element downward while fading out
11659 el.ghost();
11660
11661 // custom: slide the element out to the right with a 2-second duration
11662 el.ghost('r', { duration: 2 });
11663
11664 // common config options shown with default values
11665 el.ghost('b', {
11666     easing: 'easeOut',
11667     duration: .5
11668     remove: false,
11669     useDisplay: false
11670 });
11671 </code></pre>
11672          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11673          * @param {Object} options (optional) Object literal with any of the Fx config options
11674          * @return {Roo.Element} The Element
11675          */
11676     ghost : function(anchor, o){
11677         var el = this.getFxEl();
11678         o = o || {};
11679
11680         el.queueFx(o, function(){
11681             anchor = anchor || "b";
11682
11683             // restore values after effect
11684             var r = this.getFxRestore();
11685             var w = this.getWidth(),
11686                 h = this.getHeight();
11687
11688             var st = this.dom.style;
11689
11690             var after = function(){
11691                 if(o.useDisplay){
11692                     el.setDisplayed(false);
11693                 }else{
11694                     el.hide();
11695                 }
11696
11697                 el.clearOpacity();
11698                 el.setPositioning(r.pos);
11699                 st.width = r.width;
11700                 st.height = r.height;
11701
11702                 el.afterFx(o);
11703             };
11704
11705             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11706             switch(anchor.toLowerCase()){
11707                 case "t":
11708                     pt.by = [0, -h];
11709                 break;
11710                 case "l":
11711                     pt.by = [-w, 0];
11712                 break;
11713                 case "r":
11714                     pt.by = [w, 0];
11715                 break;
11716                 case "b":
11717                     pt.by = [0, h];
11718                 break;
11719                 case "tl":
11720                     pt.by = [-w, -h];
11721                 break;
11722                 case "bl":
11723                     pt.by = [-w, h];
11724                 break;
11725                 case "br":
11726                     pt.by = [w, h];
11727                 break;
11728                 case "tr":
11729                     pt.by = [w, -h];
11730                 break;
11731             }
11732
11733             arguments.callee.anim = this.fxanim(a,
11734                 o,
11735                 'motion',
11736                 .5,
11737                 "easeOut", after);
11738         });
11739         return this;
11740     },
11741
11742         /**
11743          * Ensures that all effects queued after syncFx is called on the element are
11744          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11745          * @return {Roo.Element} The Element
11746          */
11747     syncFx : function(){
11748         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11749             block : false,
11750             concurrent : true,
11751             stopFx : false
11752         });
11753         return this;
11754     },
11755
11756         /**
11757          * Ensures that all effects queued after sequenceFx is called on the element are
11758          * run in sequence.  This is the opposite of {@link #syncFx}.
11759          * @return {Roo.Element} The Element
11760          */
11761     sequenceFx : function(){
11762         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11763             block : false,
11764             concurrent : false,
11765             stopFx : false
11766         });
11767         return this;
11768     },
11769
11770         /* @private */
11771     nextFx : function(){
11772         var ef = this.fxQueue[0];
11773         if(ef){
11774             ef.call(this);
11775         }
11776     },
11777
11778         /**
11779          * Returns true if the element has any effects actively running or queued, else returns false.
11780          * @return {Boolean} True if element has active effects, else false
11781          */
11782     hasActiveFx : function(){
11783         return this.fxQueue && this.fxQueue[0];
11784     },
11785
11786         /**
11787          * Stops any running effects and clears the element's internal effects queue if it contains
11788          * any additional effects that haven't started yet.
11789          * @return {Roo.Element} The Element
11790          */
11791     stopFx : function(){
11792         if(this.hasActiveFx()){
11793             var cur = this.fxQueue[0];
11794             if(cur && cur.anim && cur.anim.isAnimated()){
11795                 this.fxQueue = [cur]; // clear out others
11796                 cur.anim.stop(true);
11797             }
11798         }
11799         return this;
11800     },
11801
11802         /* @private */
11803     beforeFx : function(o){
11804         if(this.hasActiveFx() && !o.concurrent){
11805            if(o.stopFx){
11806                this.stopFx();
11807                return true;
11808            }
11809            return false;
11810         }
11811         return true;
11812     },
11813
11814         /**
11815          * Returns true if the element is currently blocking so that no other effect can be queued
11816          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11817          * used to ensure that an effect initiated by a user action runs to completion prior to the
11818          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11819          * @return {Boolean} True if blocking, else false
11820          */
11821     hasFxBlock : function(){
11822         var q = this.fxQueue;
11823         return q && q[0] && q[0].block;
11824     },
11825
11826         /* @private */
11827     queueFx : function(o, fn){
11828         if(!this.fxQueue){
11829             this.fxQueue = [];
11830         }
11831         if(!this.hasFxBlock()){
11832             Roo.applyIf(o, this.fxDefaults);
11833             if(!o.concurrent){
11834                 var run = this.beforeFx(o);
11835                 fn.block = o.block;
11836                 this.fxQueue.push(fn);
11837                 if(run){
11838                     this.nextFx();
11839                 }
11840             }else{
11841                 fn.call(this);
11842             }
11843         }
11844         return this;
11845     },
11846
11847         /* @private */
11848     fxWrap : function(pos, o, vis){
11849         var wrap;
11850         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11851             var wrapXY;
11852             if(o.fixPosition){
11853                 wrapXY = this.getXY();
11854             }
11855             var div = document.createElement("div");
11856             div.style.visibility = vis;
11857             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11858             wrap.setPositioning(pos);
11859             if(wrap.getStyle("position") == "static"){
11860                 wrap.position("relative");
11861             }
11862             this.clearPositioning('auto');
11863             wrap.clip();
11864             wrap.dom.appendChild(this.dom);
11865             if(wrapXY){
11866                 wrap.setXY(wrapXY);
11867             }
11868         }
11869         return wrap;
11870     },
11871
11872         /* @private */
11873     fxUnwrap : function(wrap, pos, o){
11874         this.clearPositioning();
11875         this.setPositioning(pos);
11876         if(!o.wrap){
11877             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11878             wrap.remove();
11879         }
11880     },
11881
11882         /* @private */
11883     getFxRestore : function(){
11884         var st = this.dom.style;
11885         return {pos: this.getPositioning(), width: st.width, height : st.height};
11886     },
11887
11888         /* @private */
11889     afterFx : function(o){
11890         if(o.afterStyle){
11891             this.applyStyles(o.afterStyle);
11892         }
11893         if(o.afterCls){
11894             this.addClass(o.afterCls);
11895         }
11896         if(o.remove === true){
11897             this.remove();
11898         }
11899         Roo.callback(o.callback, o.scope, [this]);
11900         if(!o.concurrent){
11901             this.fxQueue.shift();
11902             this.nextFx();
11903         }
11904     },
11905
11906         /* @private */
11907     getFxEl : function(){ // support for composite element fx
11908         return Roo.get(this.dom);
11909     },
11910
11911         /* @private */
11912     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11913         animType = animType || 'run';
11914         opt = opt || {};
11915         var anim = Roo.lib.Anim[animType](
11916             this.dom, args,
11917             (opt.duration || defaultDur) || .35,
11918             (opt.easing || defaultEase) || 'easeOut',
11919             function(){
11920                 Roo.callback(cb, this);
11921             },
11922             this
11923         );
11924         opt.anim = anim;
11925         return anim;
11926     }
11927 };
11928
11929 // backwords compat
11930 Roo.Fx.resize = Roo.Fx.scale;
11931
11932 //When included, Roo.Fx is automatically applied to Element so that all basic
11933 //effects are available directly via the Element API
11934 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11935  * Based on:
11936  * Ext JS Library 1.1.1
11937  * Copyright(c) 2006-2007, Ext JS, LLC.
11938  *
11939  * Originally Released Under LGPL - original licence link has changed is not relivant.
11940  *
11941  * Fork - LGPL
11942  * <script type="text/javascript">
11943  */
11944
11945
11946 /**
11947  * @class Roo.CompositeElement
11948  * Standard composite class. Creates a Roo.Element for every element in the collection.
11949  * <br><br>
11950  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11951  * actions will be performed on all the elements in this collection.</b>
11952  * <br><br>
11953  * All methods return <i>this</i> and can be chained.
11954  <pre><code>
11955  var els = Roo.select("#some-el div.some-class", true);
11956  // or select directly from an existing element
11957  var el = Roo.get('some-el');
11958  el.select('div.some-class', true);
11959
11960  els.setWidth(100); // all elements become 100 width
11961  els.hide(true); // all elements fade out and hide
11962  // or
11963  els.setWidth(100).hide(true);
11964  </code></pre>
11965  */
11966 Roo.CompositeElement = function(els){
11967     this.elements = [];
11968     this.addElements(els);
11969 };
11970 Roo.CompositeElement.prototype = {
11971     isComposite: true,
11972     addElements : function(els){
11973         if(!els) {
11974             return this;
11975         }
11976         if(typeof els == "string"){
11977             els = Roo.Element.selectorFunction(els);
11978         }
11979         var yels = this.elements;
11980         var index = yels.length-1;
11981         for(var i = 0, len = els.length; i < len; i++) {
11982                 yels[++index] = Roo.get(els[i]);
11983         }
11984         return this;
11985     },
11986
11987     /**
11988     * Clears this composite and adds the elements returned by the passed selector.
11989     * @param {String/Array} els A string CSS selector, an array of elements or an element
11990     * @return {CompositeElement} this
11991     */
11992     fill : function(els){
11993         this.elements = [];
11994         this.add(els);
11995         return this;
11996     },
11997
11998     /**
11999     * Filters this composite to only elements that match the passed selector.
12000     * @param {String} selector A string CSS selector
12001     * @param {Boolean} inverse return inverse filter (not matches)
12002     * @return {CompositeElement} this
12003     */
12004     filter : function(selector, inverse){
12005         var els = [];
12006         inverse = inverse || false;
12007         this.each(function(el){
12008             var match = inverse ? !el.is(selector) : el.is(selector);
12009             if(match){
12010                 els[els.length] = el.dom;
12011             }
12012         });
12013         this.fill(els);
12014         return this;
12015     },
12016
12017     invoke : function(fn, args){
12018         var els = this.elements;
12019         for(var i = 0, len = els.length; i < len; i++) {
12020                 Roo.Element.prototype[fn].apply(els[i], args);
12021         }
12022         return this;
12023     },
12024     /**
12025     * Adds elements to this composite.
12026     * @param {String/Array} els A string CSS selector, an array of elements or an element
12027     * @return {CompositeElement} this
12028     */
12029     add : function(els){
12030         if(typeof els == "string"){
12031             this.addElements(Roo.Element.selectorFunction(els));
12032         }else if(els.length !== undefined){
12033             this.addElements(els);
12034         }else{
12035             this.addElements([els]);
12036         }
12037         return this;
12038     },
12039     /**
12040     * Calls the passed function passing (el, this, index) for each element in this composite.
12041     * @param {Function} fn The function to call
12042     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12043     * @return {CompositeElement} this
12044     */
12045     each : function(fn, scope){
12046         var els = this.elements;
12047         for(var i = 0, len = els.length; i < len; i++){
12048             if(fn.call(scope || els[i], els[i], this, i) === false) {
12049                 break;
12050             }
12051         }
12052         return this;
12053     },
12054
12055     /**
12056      * Returns the Element object at the specified index
12057      * @param {Number} index
12058      * @return {Roo.Element}
12059      */
12060     item : function(index){
12061         return this.elements[index] || null;
12062     },
12063
12064     /**
12065      * Returns the first Element
12066      * @return {Roo.Element}
12067      */
12068     first : function(){
12069         return this.item(0);
12070     },
12071
12072     /**
12073      * Returns the last Element
12074      * @return {Roo.Element}
12075      */
12076     last : function(){
12077         return this.item(this.elements.length-1);
12078     },
12079
12080     /**
12081      * Returns the number of elements in this composite
12082      * @return Number
12083      */
12084     getCount : function(){
12085         return this.elements.length;
12086     },
12087
12088     /**
12089      * Returns true if this composite contains the passed element
12090      * @return Boolean
12091      */
12092     contains : function(el){
12093         return this.indexOf(el) !== -1;
12094     },
12095
12096     /**
12097      * Returns true if this composite contains the passed element
12098      * @return Boolean
12099      */
12100     indexOf : function(el){
12101         return this.elements.indexOf(Roo.get(el));
12102     },
12103
12104
12105     /**
12106     * Removes the specified element(s).
12107     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12108     * or an array of any of those.
12109     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12110     * @return {CompositeElement} this
12111     */
12112     removeElement : function(el, removeDom){
12113         if(el instanceof Array){
12114             for(var i = 0, len = el.length; i < len; i++){
12115                 this.removeElement(el[i]);
12116             }
12117             return this;
12118         }
12119         var index = typeof el == 'number' ? el : this.indexOf(el);
12120         if(index !== -1){
12121             if(removeDom){
12122                 var d = this.elements[index];
12123                 if(d.dom){
12124                     d.remove();
12125                 }else{
12126                     d.parentNode.removeChild(d);
12127                 }
12128             }
12129             this.elements.splice(index, 1);
12130         }
12131         return this;
12132     },
12133
12134     /**
12135     * Replaces the specified element with the passed element.
12136     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12137     * to replace.
12138     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12139     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12140     * @return {CompositeElement} this
12141     */
12142     replaceElement : function(el, replacement, domReplace){
12143         var index = typeof el == 'number' ? el : this.indexOf(el);
12144         if(index !== -1){
12145             if(domReplace){
12146                 this.elements[index].replaceWith(replacement);
12147             }else{
12148                 this.elements.splice(index, 1, Roo.get(replacement))
12149             }
12150         }
12151         return this;
12152     },
12153
12154     /**
12155      * Removes all elements.
12156      */
12157     clear : function(){
12158         this.elements = [];
12159     }
12160 };
12161 (function(){
12162     Roo.CompositeElement.createCall = function(proto, fnName){
12163         if(!proto[fnName]){
12164             proto[fnName] = function(){
12165                 return this.invoke(fnName, arguments);
12166             };
12167         }
12168     };
12169     for(var fnName in Roo.Element.prototype){
12170         if(typeof Roo.Element.prototype[fnName] == "function"){
12171             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12172         }
12173     };
12174 })();
12175 /*
12176  * Based on:
12177  * Ext JS Library 1.1.1
12178  * Copyright(c) 2006-2007, Ext JS, LLC.
12179  *
12180  * Originally Released Under LGPL - original licence link has changed is not relivant.
12181  *
12182  * Fork - LGPL
12183  * <script type="text/javascript">
12184  */
12185
12186 /**
12187  * @class Roo.CompositeElementLite
12188  * @extends Roo.CompositeElement
12189  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12190  <pre><code>
12191  var els = Roo.select("#some-el div.some-class");
12192  // or select directly from an existing element
12193  var el = Roo.get('some-el');
12194  el.select('div.some-class');
12195
12196  els.setWidth(100); // all elements become 100 width
12197  els.hide(true); // all elements fade out and hide
12198  // or
12199  els.setWidth(100).hide(true);
12200  </code></pre><br><br>
12201  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12202  * actions will be performed on all the elements in this collection.</b>
12203  */
12204 Roo.CompositeElementLite = function(els){
12205     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12206     this.el = new Roo.Element.Flyweight();
12207 };
12208 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12209     addElements : function(els){
12210         if(els){
12211             if(els instanceof Array){
12212                 this.elements = this.elements.concat(els);
12213             }else{
12214                 var yels = this.elements;
12215                 var index = yels.length-1;
12216                 for(var i = 0, len = els.length; i < len; i++) {
12217                     yels[++index] = els[i];
12218                 }
12219             }
12220         }
12221         return this;
12222     },
12223     invoke : function(fn, args){
12224         var els = this.elements;
12225         var el = this.el;
12226         for(var i = 0, len = els.length; i < len; i++) {
12227             el.dom = els[i];
12228                 Roo.Element.prototype[fn].apply(el, args);
12229         }
12230         return this;
12231     },
12232     /**
12233      * Returns a flyweight Element of the dom element object at the specified index
12234      * @param {Number} index
12235      * @return {Roo.Element}
12236      */
12237     item : function(index){
12238         if(!this.elements[index]){
12239             return null;
12240         }
12241         this.el.dom = this.elements[index];
12242         return this.el;
12243     },
12244
12245     // fixes scope with flyweight
12246     addListener : function(eventName, handler, scope, opt){
12247         var els = this.elements;
12248         for(var i = 0, len = els.length; i < len; i++) {
12249             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12250         }
12251         return this;
12252     },
12253
12254     /**
12255     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12256     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12257     * a reference to the dom node, use el.dom.</b>
12258     * @param {Function} fn The function to call
12259     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12260     * @return {CompositeElement} this
12261     */
12262     each : function(fn, scope){
12263         var els = this.elements;
12264         var el = this.el;
12265         for(var i = 0, len = els.length; i < len; i++){
12266             el.dom = els[i];
12267                 if(fn.call(scope || el, el, this, i) === false){
12268                 break;
12269             }
12270         }
12271         return this;
12272     },
12273
12274     indexOf : function(el){
12275         return this.elements.indexOf(Roo.getDom(el));
12276     },
12277
12278     replaceElement : function(el, replacement, domReplace){
12279         var index = typeof el == 'number' ? el : this.indexOf(el);
12280         if(index !== -1){
12281             replacement = Roo.getDom(replacement);
12282             if(domReplace){
12283                 var d = this.elements[index];
12284                 d.parentNode.insertBefore(replacement, d);
12285                 d.parentNode.removeChild(d);
12286             }
12287             this.elements.splice(index, 1, replacement);
12288         }
12289         return this;
12290     }
12291 });
12292 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12293
12294 /*
12295  * Based on:
12296  * Ext JS Library 1.1.1
12297  * Copyright(c) 2006-2007, Ext JS, LLC.
12298  *
12299  * Originally Released Under LGPL - original licence link has changed is not relivant.
12300  *
12301  * Fork - LGPL
12302  * <script type="text/javascript">
12303  */
12304
12305  
12306
12307 /**
12308  * @class Roo.data.Connection
12309  * @extends Roo.util.Observable
12310  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12311  * either to a configured URL, or to a URL specified at request time. 
12312  * 
12313  * Requests made by this class are asynchronous, and will return immediately. No data from
12314  * the server will be available to the statement immediately following the {@link #request} call.
12315  * To process returned data, use a callback in the request options object, or an event listener.
12316  * 
12317  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12318  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12319  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12320  * property and, if present, the IFRAME's XML document as the responseXML property.
12321  * 
12322  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12323  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12324  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12325  * standard DOM methods.
12326  * @constructor
12327  * @param {Object} config a configuration object.
12328  */
12329 Roo.data.Connection = function(config){
12330     Roo.apply(this, config);
12331     this.addEvents({
12332         /**
12333          * @event beforerequest
12334          * Fires before a network request is made to retrieve a data object.
12335          * @param {Connection} conn This Connection object.
12336          * @param {Object} options The options config object passed to the {@link #request} method.
12337          */
12338         "beforerequest" : true,
12339         /**
12340          * @event requestcomplete
12341          * Fires if the request was successfully completed.
12342          * @param {Connection} conn This Connection object.
12343          * @param {Object} response The XHR object containing the response data.
12344          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12345          * @param {Object} options The options config object passed to the {@link #request} method.
12346          */
12347         "requestcomplete" : true,
12348         /**
12349          * @event requestexception
12350          * Fires if an error HTTP status was returned from the server.
12351          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12352          * @param {Connection} conn This Connection object.
12353          * @param {Object} response The XHR object containing the response data.
12354          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12355          * @param {Object} options The options config object passed to the {@link #request} method.
12356          */
12357         "requestexception" : true
12358     });
12359     Roo.data.Connection.superclass.constructor.call(this);
12360 };
12361
12362 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12363     /**
12364      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12365      */
12366     /**
12367      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12368      * extra parameters to each request made by this object. (defaults to undefined)
12369      */
12370     /**
12371      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12372      *  to each request made by this object. (defaults to undefined)
12373      */
12374     /**
12375      * @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)
12376      */
12377     /**
12378      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12379      */
12380     timeout : 30000,
12381     /**
12382      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12383      * @type Boolean
12384      */
12385     autoAbort:false,
12386
12387     /**
12388      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12389      * @type Boolean
12390      */
12391     disableCaching: true,
12392
12393     /**
12394      * Sends an HTTP request to a remote server.
12395      * @param {Object} options An object which may contain the following properties:<ul>
12396      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12397      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12398      * request, a url encoded string or a function to call to get either.</li>
12399      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12400      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12401      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12402      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12403      * <li>options {Object} The parameter to the request call.</li>
12404      * <li>success {Boolean} True if the request succeeded.</li>
12405      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12406      * </ul></li>
12407      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12408      * The callback is passed the following parameters:<ul>
12409      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12410      * <li>options {Object} The parameter to the request call.</li>
12411      * </ul></li>
12412      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12413      * The callback is passed the following parameters:<ul>
12414      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12415      * <li>options {Object} The parameter to the request call.</li>
12416      * </ul></li>
12417      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12418      * for the callback function. Defaults to the browser window.</li>
12419      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12420      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12421      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12422      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12423      * params for the post data. Any params will be appended to the URL.</li>
12424      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12425      * </ul>
12426      * @return {Number} transactionId
12427      */
12428     request : function(o){
12429         if(this.fireEvent("beforerequest", this, o) !== false){
12430             var p = o.params;
12431
12432             if(typeof p == "function"){
12433                 p = p.call(o.scope||window, o);
12434             }
12435             if(typeof p == "object"){
12436                 p = Roo.urlEncode(o.params);
12437             }
12438             if(this.extraParams){
12439                 var extras = Roo.urlEncode(this.extraParams);
12440                 p = p ? (p + '&' + extras) : extras;
12441             }
12442
12443             var url = o.url || this.url;
12444             if(typeof url == 'function'){
12445                 url = url.call(o.scope||window, o);
12446             }
12447
12448             if(o.form){
12449                 var form = Roo.getDom(o.form);
12450                 url = url || form.action;
12451
12452                 var enctype = form.getAttribute("enctype");
12453                 
12454                 if (o.formData) {
12455                     return this.doFormDataUpload(o, url);
12456                 }
12457                 
12458                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12459                     return this.doFormUpload(o, p, url);
12460                 }
12461                 var f = Roo.lib.Ajax.serializeForm(form);
12462                 p = p ? (p + '&' + f) : f;
12463             }
12464             
12465             if (!o.form && o.formData) {
12466                 o.formData = o.formData === true ? new FormData() : o.formData;
12467                 for (var k in o.params) {
12468                     o.formData.append(k,o.params[k]);
12469                 }
12470                     
12471                 return this.doFormDataUpload(o, url);
12472             }
12473             
12474
12475             var hs = o.headers;
12476             if(this.defaultHeaders){
12477                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12478                 if(!o.headers){
12479                     o.headers = hs;
12480                 }
12481             }
12482
12483             var cb = {
12484                 success: this.handleResponse,
12485                 failure: this.handleFailure,
12486                 scope: this,
12487                 argument: {options: o},
12488                 timeout : o.timeout || this.timeout
12489             };
12490
12491             var method = o.method||this.method||(p ? "POST" : "GET");
12492
12493             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12494                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12495             }
12496
12497             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12498                 if(o.autoAbort){
12499                     this.abort();
12500                 }
12501             }else if(this.autoAbort !== false){
12502                 this.abort();
12503             }
12504
12505             if((method == 'GET' && p) || o.xmlData){
12506                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12507                 p = '';
12508             }
12509             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12510             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12511             Roo.lib.Ajax.useDefaultHeader == true;
12512             return this.transId;
12513         }else{
12514             Roo.callback(o.callback, o.scope, [o, null, null]);
12515             return null;
12516         }
12517     },
12518
12519     /**
12520      * Determine whether this object has a request outstanding.
12521      * @param {Number} transactionId (Optional) defaults to the last transaction
12522      * @return {Boolean} True if there is an outstanding request.
12523      */
12524     isLoading : function(transId){
12525         if(transId){
12526             return Roo.lib.Ajax.isCallInProgress(transId);
12527         }else{
12528             return this.transId ? true : false;
12529         }
12530     },
12531
12532     /**
12533      * Aborts any outstanding request.
12534      * @param {Number} transactionId (Optional) defaults to the last transaction
12535      */
12536     abort : function(transId){
12537         if(transId || this.isLoading()){
12538             Roo.lib.Ajax.abort(transId || this.transId);
12539         }
12540     },
12541
12542     // private
12543     handleResponse : function(response){
12544         this.transId = false;
12545         var options = response.argument.options;
12546         response.argument = options ? options.argument : null;
12547         this.fireEvent("requestcomplete", this, response, options);
12548         Roo.callback(options.success, options.scope, [response, options]);
12549         Roo.callback(options.callback, options.scope, [options, true, response]);
12550     },
12551
12552     // private
12553     handleFailure : function(response, e){
12554         this.transId = false;
12555         var options = response.argument.options;
12556         response.argument = options ? options.argument : null;
12557         this.fireEvent("requestexception", this, response, options, e);
12558         Roo.callback(options.failure, options.scope, [response, options]);
12559         Roo.callback(options.callback, options.scope, [options, false, response]);
12560     },
12561
12562     // private
12563     doFormUpload : function(o, ps, url){
12564         var id = Roo.id();
12565         var frame = document.createElement('iframe');
12566         frame.id = id;
12567         frame.name = id;
12568         frame.className = 'x-hidden';
12569         if(Roo.isIE){
12570             frame.src = Roo.SSL_SECURE_URL;
12571         }
12572         document.body.appendChild(frame);
12573
12574         if(Roo.isIE){
12575            document.frames[id].name = id;
12576         }
12577
12578         var form = Roo.getDom(o.form);
12579         form.target = id;
12580         form.method = 'POST';
12581         form.enctype = form.encoding = 'multipart/form-data';
12582         if(url){
12583             form.action = url;
12584         }
12585
12586         var hiddens, hd;
12587         if(ps){ // add dynamic params
12588             hiddens = [];
12589             ps = Roo.urlDecode(ps, false);
12590             for(var k in ps){
12591                 if(ps.hasOwnProperty(k)){
12592                     hd = document.createElement('input');
12593                     hd.type = 'hidden';
12594                     hd.name = k;
12595                     hd.value = ps[k];
12596                     form.appendChild(hd);
12597                     hiddens.push(hd);
12598                 }
12599             }
12600         }
12601
12602         function cb(){
12603             var r = {  // bogus response object
12604                 responseText : '',
12605                 responseXML : null
12606             };
12607
12608             r.argument = o ? o.argument : null;
12609
12610             try { //
12611                 var doc;
12612                 if(Roo.isIE){
12613                     doc = frame.contentWindow.document;
12614                 }else {
12615                     doc = (frame.contentDocument || window.frames[id].document);
12616                 }
12617                 if(doc && doc.body){
12618                     r.responseText = doc.body.innerHTML;
12619                 }
12620                 if(doc && doc.XMLDocument){
12621                     r.responseXML = doc.XMLDocument;
12622                 }else {
12623                     r.responseXML = doc;
12624                 }
12625             }
12626             catch(e) {
12627                 // ignore
12628             }
12629
12630             Roo.EventManager.removeListener(frame, 'load', cb, this);
12631
12632             this.fireEvent("requestcomplete", this, r, o);
12633             Roo.callback(o.success, o.scope, [r, o]);
12634             Roo.callback(o.callback, o.scope, [o, true, r]);
12635
12636             setTimeout(function(){document.body.removeChild(frame);}, 100);
12637         }
12638
12639         Roo.EventManager.on(frame, 'load', cb, this);
12640         form.submit();
12641
12642         if(hiddens){ // remove dynamic params
12643             for(var i = 0, len = hiddens.length; i < len; i++){
12644                 form.removeChild(hiddens[i]);
12645             }
12646         }
12647     },
12648     // this is a 'formdata version???'
12649     
12650     
12651     doFormDataUpload : function(o,  url)
12652     {
12653         var formData;
12654         if (o.form) {
12655             var form =  Roo.getDom(o.form);
12656             form.enctype = form.encoding = 'multipart/form-data';
12657             formData = o.formData === true ? new FormData(form) : o.formData;
12658         } else {
12659             formData = o.formData === true ? new FormData() : o.formData;
12660         }
12661         
12662       
12663         var cb = {
12664             success: this.handleResponse,
12665             failure: this.handleFailure,
12666             scope: this,
12667             argument: {options: o},
12668             timeout : o.timeout || this.timeout
12669         };
12670  
12671         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12672             if(o.autoAbort){
12673                 this.abort();
12674             }
12675         }else if(this.autoAbort !== false){
12676             this.abort();
12677         }
12678
12679         //Roo.lib.Ajax.defaultPostHeader = null;
12680         Roo.lib.Ajax.useDefaultHeader = false;
12681         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12682         Roo.lib.Ajax.useDefaultHeader = true;
12683  
12684          
12685     }
12686     
12687 });
12688 /*
12689  * Based on:
12690  * Ext JS Library 1.1.1
12691  * Copyright(c) 2006-2007, Ext JS, LLC.
12692  *
12693  * Originally Released Under LGPL - original licence link has changed is not relivant.
12694  *
12695  * Fork - LGPL
12696  * <script type="text/javascript">
12697  */
12698  
12699 /**
12700  * Global Ajax request class.
12701  * 
12702  * @class Roo.Ajax
12703  * @extends Roo.data.Connection
12704  * @static
12705  * 
12706  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12707  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12708  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12709  * @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)
12710  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12711  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12712  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12713  */
12714 Roo.Ajax = new Roo.data.Connection({
12715     // fix up the docs
12716     /**
12717      * @scope Roo.Ajax
12718      * @type {Boolear} 
12719      */
12720     autoAbort : false,
12721
12722     /**
12723      * Serialize the passed form into a url encoded string
12724      * @scope Roo.Ajax
12725      * @param {String/HTMLElement} form
12726      * @return {String}
12727      */
12728     serializeForm : function(form){
12729         return Roo.lib.Ajax.serializeForm(form);
12730     }
12731 });/*
12732  * Based on:
12733  * Ext JS Library 1.1.1
12734  * Copyright(c) 2006-2007, Ext JS, LLC.
12735  *
12736  * Originally Released Under LGPL - original licence link has changed is not relivant.
12737  *
12738  * Fork - LGPL
12739  * <script type="text/javascript">
12740  */
12741
12742  
12743 /**
12744  * @class Roo.UpdateManager
12745  * @extends Roo.util.Observable
12746  * Provides AJAX-style update for Element object.<br><br>
12747  * Usage:<br>
12748  * <pre><code>
12749  * // Get it from a Roo.Element object
12750  * var el = Roo.get("foo");
12751  * var mgr = el.getUpdateManager();
12752  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12753  * ...
12754  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12755  * <br>
12756  * // or directly (returns the same UpdateManager instance)
12757  * var mgr = new Roo.UpdateManager("myElementId");
12758  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12759  * mgr.on("update", myFcnNeedsToKnow);
12760  * <br>
12761    // short handed call directly from the element object
12762    Roo.get("foo").load({
12763         url: "bar.php",
12764         scripts:true,
12765         params: "for=bar",
12766         text: "Loading Foo..."
12767    });
12768  * </code></pre>
12769  * @constructor
12770  * Create new UpdateManager directly.
12771  * @param {String/HTMLElement/Roo.Element} el The element to update
12772  * @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).
12773  */
12774 Roo.UpdateManager = function(el, forceNew){
12775     el = Roo.get(el);
12776     if(!forceNew && el.updateManager){
12777         return el.updateManager;
12778     }
12779     /**
12780      * The Element object
12781      * @type Roo.Element
12782      */
12783     this.el = el;
12784     /**
12785      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12786      * @type String
12787      */
12788     this.defaultUrl = null;
12789
12790     this.addEvents({
12791         /**
12792          * @event beforeupdate
12793          * Fired before an update is made, return false from your handler and the update is cancelled.
12794          * @param {Roo.Element} el
12795          * @param {String/Object/Function} url
12796          * @param {String/Object} params
12797          */
12798         "beforeupdate": true,
12799         /**
12800          * @event update
12801          * Fired after successful update is made.
12802          * @param {Roo.Element} el
12803          * @param {Object} oResponseObject The response Object
12804          */
12805         "update": true,
12806         /**
12807          * @event failure
12808          * Fired on update failure.
12809          * @param {Roo.Element} el
12810          * @param {Object} oResponseObject The response Object
12811          */
12812         "failure": true
12813     });
12814     var d = Roo.UpdateManager.defaults;
12815     /**
12816      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12817      * @type String
12818      */
12819     this.sslBlankUrl = d.sslBlankUrl;
12820     /**
12821      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12822      * @type Boolean
12823      */
12824     this.disableCaching = d.disableCaching;
12825     /**
12826      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12827      * @type String
12828      */
12829     this.indicatorText = d.indicatorText;
12830     /**
12831      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12832      * @type String
12833      */
12834     this.showLoadIndicator = d.showLoadIndicator;
12835     /**
12836      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12837      * @type Number
12838      */
12839     this.timeout = d.timeout;
12840
12841     /**
12842      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12843      * @type Boolean
12844      */
12845     this.loadScripts = d.loadScripts;
12846
12847     /**
12848      * Transaction object of current executing transaction
12849      */
12850     this.transaction = null;
12851
12852     /**
12853      * @private
12854      */
12855     this.autoRefreshProcId = null;
12856     /**
12857      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12858      * @type Function
12859      */
12860     this.refreshDelegate = this.refresh.createDelegate(this);
12861     /**
12862      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12863      * @type Function
12864      */
12865     this.updateDelegate = this.update.createDelegate(this);
12866     /**
12867      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12868      * @type Function
12869      */
12870     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12871     /**
12872      * @private
12873      */
12874     this.successDelegate = this.processSuccess.createDelegate(this);
12875     /**
12876      * @private
12877      */
12878     this.failureDelegate = this.processFailure.createDelegate(this);
12879
12880     if(!this.renderer){
12881      /**
12882       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12883       */
12884     this.renderer = new Roo.UpdateManager.BasicRenderer();
12885     }
12886     
12887     Roo.UpdateManager.superclass.constructor.call(this);
12888 };
12889
12890 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12891     /**
12892      * Get the Element this UpdateManager is bound to
12893      * @return {Roo.Element} The element
12894      */
12895     getEl : function(){
12896         return this.el;
12897     },
12898     /**
12899      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12900      * @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:
12901 <pre><code>
12902 um.update({<br/>
12903     url: "your-url.php",<br/>
12904     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12905     callback: yourFunction,<br/>
12906     scope: yourObject, //(optional scope)  <br/>
12907     discardUrl: false, <br/>
12908     nocache: false,<br/>
12909     text: "Loading...",<br/>
12910     timeout: 30,<br/>
12911     scripts: false<br/>
12912 });
12913 </code></pre>
12914      * The only required property is url. The optional properties nocache, text and scripts
12915      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12916      * @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}
12917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12918      * @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.
12919      */
12920     update : function(url, params, callback, discardUrl){
12921         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12922             var method = this.method,
12923                 cfg;
12924             if(typeof url == "object"){ // must be config object
12925                 cfg = url;
12926                 url = cfg.url;
12927                 params = params || cfg.params;
12928                 callback = callback || cfg.callback;
12929                 discardUrl = discardUrl || cfg.discardUrl;
12930                 if(callback && cfg.scope){
12931                     callback = callback.createDelegate(cfg.scope);
12932                 }
12933                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12934                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12935                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12936                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12937                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12938             }
12939             this.showLoading();
12940             if(!discardUrl){
12941                 this.defaultUrl = url;
12942             }
12943             if(typeof url == "function"){
12944                 url = url.call(this);
12945             }
12946
12947             method = method || (params ? "POST" : "GET");
12948             if(method == "GET"){
12949                 url = this.prepareUrl(url);
12950             }
12951
12952             var o = Roo.apply(cfg ||{}, {
12953                 url : url,
12954                 params: params,
12955                 success: this.successDelegate,
12956                 failure: this.failureDelegate,
12957                 callback: undefined,
12958                 timeout: (this.timeout*1000),
12959                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12960             });
12961             Roo.log("updated manager called with timeout of " + o.timeout);
12962             this.transaction = Roo.Ajax.request(o);
12963         }
12964     },
12965
12966     /**
12967      * 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.
12968      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12969      * @param {String/HTMLElement} form The form Id or form element
12970      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12971      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12972      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12973      */
12974     formUpdate : function(form, url, reset, callback){
12975         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12976             if(typeof url == "function"){
12977                 url = url.call(this);
12978             }
12979             form = Roo.getDom(form);
12980             this.transaction = Roo.Ajax.request({
12981                 form: form,
12982                 url:url,
12983                 success: this.successDelegate,
12984                 failure: this.failureDelegate,
12985                 timeout: (this.timeout*1000),
12986                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12987             });
12988             this.showLoading.defer(1, this);
12989         }
12990     },
12991
12992     /**
12993      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12994      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12995      */
12996     refresh : function(callback){
12997         if(this.defaultUrl == null){
12998             return;
12999         }
13000         this.update(this.defaultUrl, null, callback, true);
13001     },
13002
13003     /**
13004      * Set this element to auto refresh.
13005      * @param {Number} interval How often to update (in seconds).
13006      * @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)
13007      * @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}
13008      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13009      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13010      */
13011     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13012         if(refreshNow){
13013             this.update(url || this.defaultUrl, params, callback, true);
13014         }
13015         if(this.autoRefreshProcId){
13016             clearInterval(this.autoRefreshProcId);
13017         }
13018         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13019     },
13020
13021     /**
13022      * Stop auto refresh on this element.
13023      */
13024      stopAutoRefresh : function(){
13025         if(this.autoRefreshProcId){
13026             clearInterval(this.autoRefreshProcId);
13027             delete this.autoRefreshProcId;
13028         }
13029     },
13030
13031     isAutoRefreshing : function(){
13032        return this.autoRefreshProcId ? true : false;
13033     },
13034     /**
13035      * Called to update the element to "Loading" state. Override to perform custom action.
13036      */
13037     showLoading : function(){
13038         if(this.showLoadIndicator){
13039             this.el.update(this.indicatorText);
13040         }
13041     },
13042
13043     /**
13044      * Adds unique parameter to query string if disableCaching = true
13045      * @private
13046      */
13047     prepareUrl : function(url){
13048         if(this.disableCaching){
13049             var append = "_dc=" + (new Date().getTime());
13050             if(url.indexOf("?") !== -1){
13051                 url += "&" + append;
13052             }else{
13053                 url += "?" + append;
13054             }
13055         }
13056         return url;
13057     },
13058
13059     /**
13060      * @private
13061      */
13062     processSuccess : function(response){
13063         this.transaction = null;
13064         if(response.argument.form && response.argument.reset){
13065             try{ // put in try/catch since some older FF releases had problems with this
13066                 response.argument.form.reset();
13067             }catch(e){}
13068         }
13069         if(this.loadScripts){
13070             this.renderer.render(this.el, response, this,
13071                 this.updateComplete.createDelegate(this, [response]));
13072         }else{
13073             this.renderer.render(this.el, response, this);
13074             this.updateComplete(response);
13075         }
13076     },
13077
13078     updateComplete : function(response){
13079         this.fireEvent("update", this.el, response);
13080         if(typeof response.argument.callback == "function"){
13081             response.argument.callback(this.el, true, response);
13082         }
13083     },
13084
13085     /**
13086      * @private
13087      */
13088     processFailure : function(response){
13089         this.transaction = null;
13090         this.fireEvent("failure", this.el, response);
13091         if(typeof response.argument.callback == "function"){
13092             response.argument.callback(this.el, false, response);
13093         }
13094     },
13095
13096     /**
13097      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13098      * @param {Object} renderer The object implementing the render() method
13099      */
13100     setRenderer : function(renderer){
13101         this.renderer = renderer;
13102     },
13103
13104     getRenderer : function(){
13105        return this.renderer;
13106     },
13107
13108     /**
13109      * Set the defaultUrl used for updates
13110      * @param {String/Function} defaultUrl The url or a function to call to get the url
13111      */
13112     setDefaultUrl : function(defaultUrl){
13113         this.defaultUrl = defaultUrl;
13114     },
13115
13116     /**
13117      * Aborts the executing transaction
13118      */
13119     abort : function(){
13120         if(this.transaction){
13121             Roo.Ajax.abort(this.transaction);
13122         }
13123     },
13124
13125     /**
13126      * Returns true if an update is in progress
13127      * @return {Boolean}
13128      */
13129     isUpdating : function(){
13130         if(this.transaction){
13131             return Roo.Ajax.isLoading(this.transaction);
13132         }
13133         return false;
13134     }
13135 });
13136
13137 /**
13138  * @class Roo.UpdateManager.defaults
13139  * @static (not really - but it helps the doc tool)
13140  * The defaults collection enables customizing the default properties of UpdateManager
13141  */
13142    Roo.UpdateManager.defaults = {
13143        /**
13144          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13145          * @type Number
13146          */
13147          timeout : 30,
13148
13149          /**
13150          * True to process scripts by default (Defaults to false).
13151          * @type Boolean
13152          */
13153         loadScripts : false,
13154
13155         /**
13156         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13157         * @type String
13158         */
13159         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13160         /**
13161          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13162          * @type Boolean
13163          */
13164         disableCaching : false,
13165         /**
13166          * Whether to show indicatorText when loading (Defaults to true).
13167          * @type Boolean
13168          */
13169         showLoadIndicator : true,
13170         /**
13171          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13172          * @type String
13173          */
13174         indicatorText : '<div class="loading-indicator">Loading...</div>'
13175    };
13176
13177 /**
13178  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13179  *Usage:
13180  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13181  * @param {String/HTMLElement/Roo.Element} el The element to update
13182  * @param {String} url The url
13183  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13184  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13185  * @static
13186  * @deprecated
13187  * @member Roo.UpdateManager
13188  */
13189 Roo.UpdateManager.updateElement = function(el, url, params, options){
13190     var um = Roo.get(el, true).getUpdateManager();
13191     Roo.apply(um, options);
13192     um.update(url, params, options ? options.callback : null);
13193 };
13194 // alias for backwards compat
13195 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13196 /**
13197  * @class Roo.UpdateManager.BasicRenderer
13198  * Default Content renderer. Updates the elements innerHTML with the responseText.
13199  */
13200 Roo.UpdateManager.BasicRenderer = function(){};
13201
13202 Roo.UpdateManager.BasicRenderer.prototype = {
13203     /**
13204      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13205      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13206      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13207      * @param {Roo.Element} el The element being rendered
13208      * @param {Object} response The YUI Connect response object
13209      * @param {UpdateManager} updateManager The calling update manager
13210      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13211      */
13212      render : function(el, response, updateManager, callback){
13213         el.update(response.responseText, updateManager.loadScripts, callback);
13214     }
13215 };
13216 /*
13217  * Based on:
13218  * Roo JS
13219  * (c)) Alan Knowles
13220  * Licence : LGPL
13221  */
13222
13223
13224 /**
13225  * @class Roo.DomTemplate
13226  * @extends Roo.Template
13227  * An effort at a dom based template engine..
13228  *
13229  * Similar to XTemplate, except it uses dom parsing to create the template..
13230  *
13231  * Supported features:
13232  *
13233  *  Tags:
13234
13235 <pre><code>
13236       {a_variable} - output encoded.
13237       {a_variable.format:("Y-m-d")} - call a method on the variable
13238       {a_variable:raw} - unencoded output
13239       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13240       {a_variable:this.method_on_template(...)} - call a method on the template object.
13241  
13242 </code></pre>
13243  *  The tpl tag:
13244 <pre><code>
13245         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13246         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13247         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13248         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13249   
13250 </code></pre>
13251  *      
13252  */
13253 Roo.DomTemplate = function()
13254 {
13255      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13256      if (this.html) {
13257         this.compile();
13258      }
13259 };
13260
13261
13262 Roo.extend(Roo.DomTemplate, Roo.Template, {
13263     /**
13264      * id counter for sub templates.
13265      */
13266     id : 0,
13267     /**
13268      * flag to indicate if dom parser is inside a pre,
13269      * it will strip whitespace if not.
13270      */
13271     inPre : false,
13272     
13273     /**
13274      * The various sub templates
13275      */
13276     tpls : false,
13277     
13278     
13279     
13280     /**
13281      *
13282      * basic tag replacing syntax
13283      * WORD:WORD()
13284      *
13285      * // you can fake an object call by doing this
13286      *  x.t:(test,tesT) 
13287      * 
13288      */
13289     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13290     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13291     
13292     iterChild : function (node, method) {
13293         
13294         var oldPre = this.inPre;
13295         if (node.tagName == 'PRE') {
13296             this.inPre = true;
13297         }
13298         for( var i = 0; i < node.childNodes.length; i++) {
13299             method.call(this, node.childNodes[i]);
13300         }
13301         this.inPre = oldPre;
13302     },
13303     
13304     
13305     
13306     /**
13307      * compile the template
13308      *
13309      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13310      *
13311      */
13312     compile: function()
13313     {
13314         var s = this.html;
13315         
13316         // covert the html into DOM...
13317         var doc = false;
13318         var div =false;
13319         try {
13320             doc = document.implementation.createHTMLDocument("");
13321             doc.documentElement.innerHTML =   this.html  ;
13322             div = doc.documentElement;
13323         } catch (e) {
13324             // old IE... - nasty -- it causes all sorts of issues.. with
13325             // images getting pulled from server..
13326             div = document.createElement('div');
13327             div.innerHTML = this.html;
13328         }
13329         //doc.documentElement.innerHTML = htmlBody
13330          
13331         
13332         
13333         this.tpls = [];
13334         var _t = this;
13335         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13336         
13337         var tpls = this.tpls;
13338         
13339         // create a top level template from the snippet..
13340         
13341         //Roo.log(div.innerHTML);
13342         
13343         var tpl = {
13344             uid : 'master',
13345             id : this.id++,
13346             attr : false,
13347             value : false,
13348             body : div.innerHTML,
13349             
13350             forCall : false,
13351             execCall : false,
13352             dom : div,
13353             isTop : true
13354             
13355         };
13356         tpls.unshift(tpl);
13357         
13358         
13359         // compile them...
13360         this.tpls = [];
13361         Roo.each(tpls, function(tp){
13362             this.compileTpl(tp);
13363             this.tpls[tp.id] = tp;
13364         }, this);
13365         
13366         this.master = tpls[0];
13367         return this;
13368         
13369         
13370     },
13371     
13372     compileNode : function(node, istop) {
13373         // test for
13374         //Roo.log(node);
13375         
13376         
13377         // skip anything not a tag..
13378         if (node.nodeType != 1) {
13379             if (node.nodeType == 3 && !this.inPre) {
13380                 // reduce white space..
13381                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13382                 
13383             }
13384             return;
13385         }
13386         
13387         var tpl = {
13388             uid : false,
13389             id : false,
13390             attr : false,
13391             value : false,
13392             body : '',
13393             
13394             forCall : false,
13395             execCall : false,
13396             dom : false,
13397             isTop : istop
13398             
13399             
13400         };
13401         
13402         
13403         switch(true) {
13404             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13405             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13406             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13407             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13408             // no default..
13409         }
13410         
13411         
13412         if (!tpl.attr) {
13413             // just itterate children..
13414             this.iterChild(node,this.compileNode);
13415             return;
13416         }
13417         tpl.uid = this.id++;
13418         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13419         node.removeAttribute('roo-'+ tpl.attr);
13420         if (tpl.attr != 'name') {
13421             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13422             node.parentNode.replaceChild(placeholder,  node);
13423         } else {
13424             
13425             var placeholder =  document.createElement('span');
13426             placeholder.className = 'roo-tpl-' + tpl.value;
13427             node.parentNode.replaceChild(placeholder,  node);
13428         }
13429         
13430         // parent now sees '{domtplXXXX}
13431         this.iterChild(node,this.compileNode);
13432         
13433         // we should now have node body...
13434         var div = document.createElement('div');
13435         div.appendChild(node);
13436         tpl.dom = node;
13437         // this has the unfortunate side effect of converting tagged attributes
13438         // eg. href="{...}" into %7C...%7D
13439         // this has been fixed by searching for those combo's although it's a bit hacky..
13440         
13441         
13442         tpl.body = div.innerHTML;
13443         
13444         
13445          
13446         tpl.id = tpl.uid;
13447         switch(tpl.attr) {
13448             case 'for' :
13449                 switch (tpl.value) {
13450                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13451                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13452                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13453                 }
13454                 break;
13455             
13456             case 'exec':
13457                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13458                 break;
13459             
13460             case 'if':     
13461                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13462                 break;
13463             
13464             case 'name':
13465                 tpl.id  = tpl.value; // replace non characters???
13466                 break;
13467             
13468         }
13469         
13470         
13471         this.tpls.push(tpl);
13472         
13473         
13474         
13475     },
13476     
13477     
13478     
13479     
13480     /**
13481      * Compile a segment of the template into a 'sub-template'
13482      *
13483      * 
13484      * 
13485      *
13486      */
13487     compileTpl : function(tpl)
13488     {
13489         var fm = Roo.util.Format;
13490         var useF = this.disableFormats !== true;
13491         
13492         var sep = Roo.isGecko ? "+\n" : ",\n";
13493         
13494         var undef = function(str) {
13495             Roo.debug && Roo.log("Property not found :"  + str);
13496             return '';
13497         };
13498           
13499         //Roo.log(tpl.body);
13500         
13501         
13502         
13503         var fn = function(m, lbrace, name, format, args)
13504         {
13505             //Roo.log("ARGS");
13506             //Roo.log(arguments);
13507             args = args ? args.replace(/\\'/g,"'") : args;
13508             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13509             if (typeof(format) == 'undefined') {
13510                 format =  'htmlEncode'; 
13511             }
13512             if (format == 'raw' ) {
13513                 format = false;
13514             }
13515             
13516             if(name.substr(0, 6) == 'domtpl'){
13517                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13518             }
13519             
13520             // build an array of options to determine if value is undefined..
13521             
13522             // basically get 'xxxx.yyyy' then do
13523             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13524             //    (function () { Roo.log("Property not found"); return ''; })() :
13525             //    ......
13526             
13527             var udef_ar = [];
13528             var lookfor = '';
13529             Roo.each(name.split('.'), function(st) {
13530                 lookfor += (lookfor.length ? '.': '') + st;
13531                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13532             });
13533             
13534             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13535             
13536             
13537             if(format && useF){
13538                 
13539                 args = args ? ',' + args : "";
13540                  
13541                 if(format.substr(0, 5) != "this."){
13542                     format = "fm." + format + '(';
13543                 }else{
13544                     format = 'this.call("'+ format.substr(5) + '", ';
13545                     args = ", values";
13546                 }
13547                 
13548                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13549             }
13550              
13551             if (args && args.length) {
13552                 // called with xxyx.yuu:(test,test)
13553                 // change to ()
13554                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13555             }
13556             // raw.. - :raw modifier..
13557             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13558             
13559         };
13560         var body;
13561         // branched to use + in gecko and [].join() in others
13562         if(Roo.isGecko){
13563             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13564                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13565                     "';};};";
13566         }else{
13567             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13568             body.push(tpl.body.replace(/(\r\n|\n)/g,
13569                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13570             body.push("'].join('');};};");
13571             body = body.join('');
13572         }
13573         
13574         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13575        
13576         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13577         eval(body);
13578         
13579         return this;
13580     },
13581      
13582     /**
13583      * same as applyTemplate, except it's done to one of the subTemplates
13584      * when using named templates, you can do:
13585      *
13586      * var str = pl.applySubTemplate('your-name', values);
13587      *
13588      * 
13589      * @param {Number} id of the template
13590      * @param {Object} values to apply to template
13591      * @param {Object} parent (normaly the instance of this object)
13592      */
13593     applySubTemplate : function(id, values, parent)
13594     {
13595         
13596         
13597         var t = this.tpls[id];
13598         
13599         
13600         try { 
13601             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13602                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13603                 return '';
13604             }
13605         } catch(e) {
13606             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13607             Roo.log(values);
13608           
13609             return '';
13610         }
13611         try { 
13612             
13613             if(t.execCall && t.execCall.call(this, values, parent)){
13614                 return '';
13615             }
13616         } catch(e) {
13617             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13618             Roo.log(values);
13619             return '';
13620         }
13621         
13622         try {
13623             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13624             parent = t.target ? values : parent;
13625             if(t.forCall && vs instanceof Array){
13626                 var buf = [];
13627                 for(var i = 0, len = vs.length; i < len; i++){
13628                     try {
13629                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13630                     } catch (e) {
13631                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13632                         Roo.log(e.body);
13633                         //Roo.log(t.compiled);
13634                         Roo.log(vs[i]);
13635                     }   
13636                 }
13637                 return buf.join('');
13638             }
13639         } catch (e) {
13640             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13641             Roo.log(values);
13642             return '';
13643         }
13644         try {
13645             return t.compiled.call(this, vs, parent);
13646         } catch (e) {
13647             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13648             Roo.log(e.body);
13649             //Roo.log(t.compiled);
13650             Roo.log(values);
13651             return '';
13652         }
13653     },
13654
13655    
13656
13657     applyTemplate : function(values){
13658         return this.master.compiled.call(this, values, {});
13659         //var s = this.subs;
13660     },
13661
13662     apply : function(){
13663         return this.applyTemplate.apply(this, arguments);
13664     }
13665
13666  });
13667
13668 Roo.DomTemplate.from = function(el){
13669     el = Roo.getDom(el);
13670     return new Roo.Domtemplate(el.value || el.innerHTML);
13671 };/*
13672  * Based on:
13673  * Ext JS Library 1.1.1
13674  * Copyright(c) 2006-2007, Ext JS, LLC.
13675  *
13676  * Originally Released Under LGPL - original licence link has changed is not relivant.
13677  *
13678  * Fork - LGPL
13679  * <script type="text/javascript">
13680  */
13681
13682 /**
13683  * @class Roo.util.DelayedTask
13684  * Provides a convenient method of performing setTimeout where a new
13685  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13686  * You can use this class to buffer
13687  * the keypress events for a certain number of milliseconds, and perform only if they stop
13688  * for that amount of time.
13689  * @constructor The parameters to this constructor serve as defaults and are not required.
13690  * @param {Function} fn (optional) The default function to timeout
13691  * @param {Object} scope (optional) The default scope of that timeout
13692  * @param {Array} args (optional) The default Array of arguments
13693  */
13694 Roo.util.DelayedTask = function(fn, scope, args){
13695     var id = null, d, t;
13696
13697     var call = function(){
13698         var now = new Date().getTime();
13699         if(now - t >= d){
13700             clearInterval(id);
13701             id = null;
13702             fn.apply(scope, args || []);
13703         }
13704     };
13705     /**
13706      * Cancels any pending timeout and queues a new one
13707      * @param {Number} delay The milliseconds to delay
13708      * @param {Function} newFn (optional) Overrides function passed to constructor
13709      * @param {Object} newScope (optional) Overrides scope passed to constructor
13710      * @param {Array} newArgs (optional) Overrides args passed to constructor
13711      */
13712     this.delay = function(delay, newFn, newScope, newArgs){
13713         if(id && delay != d){
13714             this.cancel();
13715         }
13716         d = delay;
13717         t = new Date().getTime();
13718         fn = newFn || fn;
13719         scope = newScope || scope;
13720         args = newArgs || args;
13721         if(!id){
13722             id = setInterval(call, d);
13723         }
13724     };
13725
13726     /**
13727      * Cancel the last queued timeout
13728      */
13729     this.cancel = function(){
13730         if(id){
13731             clearInterval(id);
13732             id = null;
13733         }
13734     };
13735 };/*
13736  * Based on:
13737  * Ext JS Library 1.1.1
13738  * Copyright(c) 2006-2007, Ext JS, LLC.
13739  *
13740  * Originally Released Under LGPL - original licence link has changed is not relivant.
13741  *
13742  * Fork - LGPL
13743  * <script type="text/javascript">
13744  */
13745 /**
13746  * @class Roo.util.TaskRunner
13747  * Manage background tasks - not sure why this is better that setInterval?
13748  * @static
13749  *
13750  */
13751  
13752 Roo.util.TaskRunner = function(interval){
13753     interval = interval || 10;
13754     var tasks = [], removeQueue = [];
13755     var id = 0;
13756     var running = false;
13757
13758     var stopThread = function(){
13759         running = false;
13760         clearInterval(id);
13761         id = 0;
13762     };
13763
13764     var startThread = function(){
13765         if(!running){
13766             running = true;
13767             id = setInterval(runTasks, interval);
13768         }
13769     };
13770
13771     var removeTask = function(task){
13772         removeQueue.push(task);
13773         if(task.onStop){
13774             task.onStop();
13775         }
13776     };
13777
13778     var runTasks = function(){
13779         if(removeQueue.length > 0){
13780             for(var i = 0, len = removeQueue.length; i < len; i++){
13781                 tasks.remove(removeQueue[i]);
13782             }
13783             removeQueue = [];
13784             if(tasks.length < 1){
13785                 stopThread();
13786                 return;
13787             }
13788         }
13789         var now = new Date().getTime();
13790         for(var i = 0, len = tasks.length; i < len; ++i){
13791             var t = tasks[i];
13792             var itime = now - t.taskRunTime;
13793             if(t.interval <= itime){
13794                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13795                 t.taskRunTime = now;
13796                 if(rt === false || t.taskRunCount === t.repeat){
13797                     removeTask(t);
13798                     return;
13799                 }
13800             }
13801             if(t.duration && t.duration <= (now - t.taskStartTime)){
13802                 removeTask(t);
13803             }
13804         }
13805     };
13806
13807     /**
13808      * Queues a new task.
13809      * @param {Object} task
13810      *
13811      * Task property : interval = how frequent to run.
13812      * Task object should implement
13813      * function run()
13814      * Task object may implement
13815      * function onStop()
13816      */
13817     this.start = function(task){
13818         tasks.push(task);
13819         task.taskStartTime = new Date().getTime();
13820         task.taskRunTime = 0;
13821         task.taskRunCount = 0;
13822         startThread();
13823         return task;
13824     };
13825     /**
13826      * Stop  new task.
13827      * @param {Object} task
13828      */
13829     this.stop = function(task){
13830         removeTask(task);
13831         return task;
13832     };
13833     /**
13834      * Stop all Tasks
13835      */
13836     this.stopAll = function(){
13837         stopThread();
13838         for(var i = 0, len = tasks.length; i < len; i++){
13839             if(tasks[i].onStop){
13840                 tasks[i].onStop();
13841             }
13842         }
13843         tasks = [];
13844         removeQueue = [];
13845     };
13846 };
13847
13848 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13849  * Based on:
13850  * Ext JS Library 1.1.1
13851  * Copyright(c) 2006-2007, Ext JS, LLC.
13852  *
13853  * Originally Released Under LGPL - original licence link has changed is not relivant.
13854  *
13855  * Fork - LGPL
13856  * <script type="text/javascript">
13857  */
13858
13859  
13860 /**
13861  * @class Roo.util.MixedCollection
13862  * @extends Roo.util.Observable
13863  * A Collection class that maintains both numeric indexes and keys and exposes events.
13864  * @constructor
13865  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13866  * collection (defaults to false)
13867  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13868  * and return the key value for that item.  This is used when available to look up the key on items that
13869  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13870  * equivalent to providing an implementation for the {@link #getKey} method.
13871  */
13872 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13873     this.items = [];
13874     this.map = {};
13875     this.keys = [];
13876     this.length = 0;
13877     this.addEvents({
13878         /**
13879          * @event clear
13880          * Fires when the collection is cleared.
13881          */
13882         "clear" : true,
13883         /**
13884          * @event add
13885          * Fires when an item is added to the collection.
13886          * @param {Number} index The index at which the item was added.
13887          * @param {Object} o The item added.
13888          * @param {String} key The key associated with the added item.
13889          */
13890         "add" : true,
13891         /**
13892          * @event replace
13893          * Fires when an item is replaced in the collection.
13894          * @param {String} key he key associated with the new added.
13895          * @param {Object} old The item being replaced.
13896          * @param {Object} new The new item.
13897          */
13898         "replace" : true,
13899         /**
13900          * @event remove
13901          * Fires when an item is removed from the collection.
13902          * @param {Object} o The item being removed.
13903          * @param {String} key (optional) The key associated with the removed item.
13904          */
13905         "remove" : true,
13906         "sort" : true
13907     });
13908     this.allowFunctions = allowFunctions === true;
13909     if(keyFn){
13910         this.getKey = keyFn;
13911     }
13912     Roo.util.MixedCollection.superclass.constructor.call(this);
13913 };
13914
13915 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13916     allowFunctions : false,
13917     
13918 /**
13919  * Adds an item to the collection.
13920  * @param {String} key The key to associate with the item
13921  * @param {Object} o The item to add.
13922  * @return {Object} The item added.
13923  */
13924     add : function(key, o){
13925         if(arguments.length == 1){
13926             o = arguments[0];
13927             key = this.getKey(o);
13928         }
13929         if(typeof key == "undefined" || key === null){
13930             this.length++;
13931             this.items.push(o);
13932             this.keys.push(null);
13933         }else{
13934             var old = this.map[key];
13935             if(old){
13936                 return this.replace(key, o);
13937             }
13938             this.length++;
13939             this.items.push(o);
13940             this.map[key] = o;
13941             this.keys.push(key);
13942         }
13943         this.fireEvent("add", this.length-1, o, key);
13944         return o;
13945     },
13946        
13947 /**
13948   * MixedCollection has a generic way to fetch keys if you implement getKey.
13949 <pre><code>
13950 // normal way
13951 var mc = new Roo.util.MixedCollection();
13952 mc.add(someEl.dom.id, someEl);
13953 mc.add(otherEl.dom.id, otherEl);
13954 //and so on
13955
13956 // using getKey
13957 var mc = new Roo.util.MixedCollection();
13958 mc.getKey = function(el){
13959    return el.dom.id;
13960 };
13961 mc.add(someEl);
13962 mc.add(otherEl);
13963
13964 // or via the constructor
13965 var mc = new Roo.util.MixedCollection(false, function(el){
13966    return el.dom.id;
13967 });
13968 mc.add(someEl);
13969 mc.add(otherEl);
13970 </code></pre>
13971  * @param o {Object} The item for which to find the key.
13972  * @return {Object} The key for the passed item.
13973  */
13974     getKey : function(o){
13975          return o.id; 
13976     },
13977    
13978 /**
13979  * Replaces an item in the collection.
13980  * @param {String} key The key associated with the item to replace, or the item to replace.
13981  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13982  * @return {Object}  The new item.
13983  */
13984     replace : function(key, o){
13985         if(arguments.length == 1){
13986             o = arguments[0];
13987             key = this.getKey(o);
13988         }
13989         var old = this.item(key);
13990         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13991              return this.add(key, o);
13992         }
13993         var index = this.indexOfKey(key);
13994         this.items[index] = o;
13995         this.map[key] = o;
13996         this.fireEvent("replace", key, old, o);
13997         return o;
13998     },
13999    
14000 /**
14001  * Adds all elements of an Array or an Object to the collection.
14002  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14003  * an Array of values, each of which are added to the collection.
14004  */
14005     addAll : function(objs){
14006         if(arguments.length > 1 || objs instanceof Array){
14007             var args = arguments.length > 1 ? arguments : objs;
14008             for(var i = 0, len = args.length; i < len; i++){
14009                 this.add(args[i]);
14010             }
14011         }else{
14012             for(var key in objs){
14013                 if(this.allowFunctions || typeof objs[key] != "function"){
14014                     this.add(key, objs[key]);
14015                 }
14016             }
14017         }
14018     },
14019    
14020 /**
14021  * Executes the specified function once for every item in the collection, passing each
14022  * item as the first and only parameter. returning false from the function will stop the iteration.
14023  * @param {Function} fn The function to execute for each item.
14024  * @param {Object} scope (optional) The scope in which to execute the function.
14025  */
14026     each : function(fn, scope){
14027         var items = [].concat(this.items); // each safe for removal
14028         for(var i = 0, len = items.length; i < len; i++){
14029             if(fn.call(scope || items[i], items[i], i, len) === false){
14030                 break;
14031             }
14032         }
14033     },
14034    
14035 /**
14036  * Executes the specified function once for every key in the collection, passing each
14037  * key, and its associated item as the first two parameters.
14038  * @param {Function} fn The function to execute for each item.
14039  * @param {Object} scope (optional) The scope in which to execute the function.
14040  */
14041     eachKey : function(fn, scope){
14042         for(var i = 0, len = this.keys.length; i < len; i++){
14043             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14044         }
14045     },
14046    
14047 /**
14048  * Returns the first item in the collection which elicits a true return value from the
14049  * passed selection function.
14050  * @param {Function} fn The selection function to execute for each item.
14051  * @param {Object} scope (optional) The scope in which to execute the function.
14052  * @return {Object} The first item in the collection which returned true from the selection function.
14053  */
14054     find : function(fn, scope){
14055         for(var i = 0, len = this.items.length; i < len; i++){
14056             if(fn.call(scope || window, this.items[i], this.keys[i])){
14057                 return this.items[i];
14058             }
14059         }
14060         return null;
14061     },
14062    
14063 /**
14064  * Inserts an item at the specified index in the collection.
14065  * @param {Number} index The index to insert the item at.
14066  * @param {String} key The key to associate with the new item, or the item itself.
14067  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14068  * @return {Object} The item inserted.
14069  */
14070     insert : function(index, key, o){
14071         if(arguments.length == 2){
14072             o = arguments[1];
14073             key = this.getKey(o);
14074         }
14075         if(index >= this.length){
14076             return this.add(key, o);
14077         }
14078         this.length++;
14079         this.items.splice(index, 0, o);
14080         if(typeof key != "undefined" && key != null){
14081             this.map[key] = o;
14082         }
14083         this.keys.splice(index, 0, key);
14084         this.fireEvent("add", index, o, key);
14085         return o;
14086     },
14087    
14088 /**
14089  * Removed an item from the collection.
14090  * @param {Object} o The item to remove.
14091  * @return {Object} The item removed.
14092  */
14093     remove : function(o){
14094         return this.removeAt(this.indexOf(o));
14095     },
14096    
14097 /**
14098  * Remove an item from a specified index in the collection.
14099  * @param {Number} index The index within the collection of the item to remove.
14100  */
14101     removeAt : function(index){
14102         if(index < this.length && index >= 0){
14103             this.length--;
14104             var o = this.items[index];
14105             this.items.splice(index, 1);
14106             var key = this.keys[index];
14107             if(typeof key != "undefined"){
14108                 delete this.map[key];
14109             }
14110             this.keys.splice(index, 1);
14111             this.fireEvent("remove", o, key);
14112         }
14113     },
14114    
14115 /**
14116  * Removed an item associated with the passed key fom the collection.
14117  * @param {String} key The key of the item to remove.
14118  */
14119     removeKey : function(key){
14120         return this.removeAt(this.indexOfKey(key));
14121     },
14122    
14123 /**
14124  * Returns the number of items in the collection.
14125  * @return {Number} the number of items in the collection.
14126  */
14127     getCount : function(){
14128         return this.length; 
14129     },
14130    
14131 /**
14132  * Returns index within the collection of the passed Object.
14133  * @param {Object} o The item to find the index of.
14134  * @return {Number} index of the item.
14135  */
14136     indexOf : function(o){
14137         if(!this.items.indexOf){
14138             for(var i = 0, len = this.items.length; i < len; i++){
14139                 if(this.items[i] == o) {
14140                     return i;
14141                 }
14142             }
14143             return -1;
14144         }else{
14145             return this.items.indexOf(o);
14146         }
14147     },
14148    
14149 /**
14150  * Returns index within the collection of the passed key.
14151  * @param {String} key The key to find the index of.
14152  * @return {Number} index of the key.
14153  */
14154     indexOfKey : function(key){
14155         if(!this.keys.indexOf){
14156             for(var i = 0, len = this.keys.length; i < len; i++){
14157                 if(this.keys[i] == key) {
14158                     return i;
14159                 }
14160             }
14161             return -1;
14162         }else{
14163             return this.keys.indexOf(key);
14164         }
14165     },
14166    
14167 /**
14168  * Returns the item associated with the passed key OR index. Key has priority over index.
14169  * @param {String/Number} key The key or index of the item.
14170  * @return {Object} The item associated with the passed key.
14171  */
14172     item : function(key){
14173         if (key === 'length') {
14174             return null;
14175         }
14176         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14177         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14178     },
14179     
14180 /**
14181  * Returns the item at the specified index.
14182  * @param {Number} index The index of the item.
14183  * @return {Object}
14184  */
14185     itemAt : function(index){
14186         return this.items[index];
14187     },
14188     
14189 /**
14190  * Returns the item associated with the passed key.
14191  * @param {String/Number} key The key of the item.
14192  * @return {Object} The item associated with the passed key.
14193  */
14194     key : function(key){
14195         return this.map[key];
14196     },
14197    
14198 /**
14199  * Returns true if the collection contains the passed Object as an item.
14200  * @param {Object} o  The Object to look for in the collection.
14201  * @return {Boolean} True if the collection contains the Object as an item.
14202  */
14203     contains : function(o){
14204         return this.indexOf(o) != -1;
14205     },
14206    
14207 /**
14208  * Returns true if the collection contains the passed Object as a key.
14209  * @param {String} key The key to look for in the collection.
14210  * @return {Boolean} True if the collection contains the Object as a key.
14211  */
14212     containsKey : function(key){
14213         return typeof this.map[key] != "undefined";
14214     },
14215    
14216 /**
14217  * Removes all items from the collection.
14218  */
14219     clear : function(){
14220         this.length = 0;
14221         this.items = [];
14222         this.keys = [];
14223         this.map = {};
14224         this.fireEvent("clear");
14225     },
14226    
14227 /**
14228  * Returns the first item in the collection.
14229  * @return {Object} the first item in the collection..
14230  */
14231     first : function(){
14232         return this.items[0]; 
14233     },
14234    
14235 /**
14236  * Returns the last item in the collection.
14237  * @return {Object} the last item in the collection..
14238  */
14239     last : function(){
14240         return this.items[this.length-1];   
14241     },
14242     
14243     _sort : function(property, dir, fn){
14244         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14245         fn = fn || function(a, b){
14246             return a-b;
14247         };
14248         var c = [], k = this.keys, items = this.items;
14249         for(var i = 0, len = items.length; i < len; i++){
14250             c[c.length] = {key: k[i], value: items[i], index: i};
14251         }
14252         c.sort(function(a, b){
14253             var v = fn(a[property], b[property]) * dsc;
14254             if(v == 0){
14255                 v = (a.index < b.index ? -1 : 1);
14256             }
14257             return v;
14258         });
14259         for(var i = 0, len = c.length; i < len; i++){
14260             items[i] = c[i].value;
14261             k[i] = c[i].key;
14262         }
14263         this.fireEvent("sort", this);
14264     },
14265     
14266     /**
14267      * Sorts this collection with the passed comparison function
14268      * @param {String} direction (optional) "ASC" or "DESC"
14269      * @param {Function} fn (optional) comparison function
14270      */
14271     sort : function(dir, fn){
14272         this._sort("value", dir, fn);
14273     },
14274     
14275     /**
14276      * Sorts this collection by keys
14277      * @param {String} direction (optional) "ASC" or "DESC"
14278      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14279      */
14280     keySort : function(dir, fn){
14281         this._sort("key", dir, fn || function(a, b){
14282             return String(a).toUpperCase()-String(b).toUpperCase();
14283         });
14284     },
14285     
14286     /**
14287      * Returns a range of items in this collection
14288      * @param {Number} startIndex (optional) defaults to 0
14289      * @param {Number} endIndex (optional) default to the last item
14290      * @return {Array} An array of items
14291      */
14292     getRange : function(start, end){
14293         var items = this.items;
14294         if(items.length < 1){
14295             return [];
14296         }
14297         start = start || 0;
14298         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14299         var r = [];
14300         if(start <= end){
14301             for(var i = start; i <= end; i++) {
14302                     r[r.length] = items[i];
14303             }
14304         }else{
14305             for(var i = start; i >= end; i--) {
14306                     r[r.length] = items[i];
14307             }
14308         }
14309         return r;
14310     },
14311         
14312     /**
14313      * Filter the <i>objects</i> in this collection by a specific property. 
14314      * Returns a new collection that has been filtered.
14315      * @param {String} property A property on your objects
14316      * @param {String/RegExp} value Either string that the property values 
14317      * should start with or a RegExp to test against the property
14318      * @return {MixedCollection} The new filtered collection
14319      */
14320     filter : function(property, value){
14321         if(!value.exec){ // not a regex
14322             value = String(value);
14323             if(value.length == 0){
14324                 return this.clone();
14325             }
14326             value = new RegExp("^" + Roo.escapeRe(value), "i");
14327         }
14328         return this.filterBy(function(o){
14329             return o && value.test(o[property]);
14330         });
14331         },
14332     
14333     /**
14334      * Filter by a function. * Returns a new collection that has been filtered.
14335      * The passed function will be called with each 
14336      * object in the collection. If the function returns true, the value is included 
14337      * otherwise it is filtered.
14338      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14339      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14340      * @return {MixedCollection} The new filtered collection
14341      */
14342     filterBy : function(fn, scope){
14343         var r = new Roo.util.MixedCollection();
14344         r.getKey = this.getKey;
14345         var k = this.keys, it = this.items;
14346         for(var i = 0, len = it.length; i < len; i++){
14347             if(fn.call(scope||this, it[i], k[i])){
14348                                 r.add(k[i], it[i]);
14349                         }
14350         }
14351         return r;
14352     },
14353     
14354     /**
14355      * Creates a duplicate of this collection
14356      * @return {MixedCollection}
14357      */
14358     clone : function(){
14359         var r = new Roo.util.MixedCollection();
14360         var k = this.keys, it = this.items;
14361         for(var i = 0, len = it.length; i < len; i++){
14362             r.add(k[i], it[i]);
14363         }
14364         r.getKey = this.getKey;
14365         return r;
14366     }
14367 });
14368 /**
14369  * Returns the item associated with the passed key or index.
14370  * @method
14371  * @param {String/Number} key The key or index of the item.
14372  * @return {Object} The item associated with the passed key.
14373  */
14374 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384 /**
14385  * @class Roo.util.JSON
14386  * Modified version of Douglas Crockford"s json.js that doesn"t
14387  * mess with the Object prototype 
14388  * http://www.json.org/js.html
14389  * @static
14390  */
14391 Roo.util.JSON = new (function(){
14392     var useHasOwn = {}.hasOwnProperty ? true : false;
14393     
14394     // crashes Safari in some instances
14395     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14396     
14397     var pad = function(n) {
14398         return n < 10 ? "0" + n : n;
14399     };
14400     
14401     var m = {
14402         "\b": '\\b',
14403         "\t": '\\t',
14404         "\n": '\\n',
14405         "\f": '\\f',
14406         "\r": '\\r',
14407         '"' : '\\"',
14408         "\\": '\\\\'
14409     };
14410
14411     var encodeString = function(s){
14412         if (/["\\\x00-\x1f]/.test(s)) {
14413             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14414                 var c = m[b];
14415                 if(c){
14416                     return c;
14417                 }
14418                 c = b.charCodeAt();
14419                 return "\\u00" +
14420                     Math.floor(c / 16).toString(16) +
14421                     (c % 16).toString(16);
14422             }) + '"';
14423         }
14424         return '"' + s + '"';
14425     };
14426     
14427     var encodeArray = function(o){
14428         var a = ["["], b, i, l = o.length, v;
14429             for (i = 0; i < l; i += 1) {
14430                 v = o[i];
14431                 switch (typeof v) {
14432                     case "undefined":
14433                     case "function":
14434                     case "unknown":
14435                         break;
14436                     default:
14437                         if (b) {
14438                             a.push(',');
14439                         }
14440                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14441                         b = true;
14442                 }
14443             }
14444             a.push("]");
14445             return a.join("");
14446     };
14447     
14448     var encodeDate = function(o){
14449         return '"' + o.getFullYear() + "-" +
14450                 pad(o.getMonth() + 1) + "-" +
14451                 pad(o.getDate()) + "T" +
14452                 pad(o.getHours()) + ":" +
14453                 pad(o.getMinutes()) + ":" +
14454                 pad(o.getSeconds()) + '"';
14455     };
14456     
14457     /**
14458      * Encodes an Object, Array or other value
14459      * @param {Mixed} o The variable to encode
14460      * @return {String} The JSON string
14461      */
14462     this.encode = function(o)
14463     {
14464         // should this be extended to fully wrap stringify..
14465         
14466         if(typeof o == "undefined" || o === null){
14467             return "null";
14468         }else if(o instanceof Array){
14469             return encodeArray(o);
14470         }else if(o instanceof Date){
14471             return encodeDate(o);
14472         }else if(typeof o == "string"){
14473             return encodeString(o);
14474         }else if(typeof o == "number"){
14475             return isFinite(o) ? String(o) : "null";
14476         }else if(typeof o == "boolean"){
14477             return String(o);
14478         }else {
14479             var a = ["{"], b, i, v;
14480             for (i in o) {
14481                 if(!useHasOwn || o.hasOwnProperty(i)) {
14482                     v = o[i];
14483                     switch (typeof v) {
14484                     case "undefined":
14485                     case "function":
14486                     case "unknown":
14487                         break;
14488                     default:
14489                         if(b){
14490                             a.push(',');
14491                         }
14492                         a.push(this.encode(i), ":",
14493                                 v === null ? "null" : this.encode(v));
14494                         b = true;
14495                     }
14496                 }
14497             }
14498             a.push("}");
14499             return a.join("");
14500         }
14501     };
14502     
14503     /**
14504      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14505      * @param {String} json The JSON string
14506      * @return {Object} The resulting object
14507      */
14508     this.decode = function(json){
14509         
14510         return  /** eval:var:json */ eval("(" + json + ')');
14511     };
14512 })();
14513 /** 
14514  * Shorthand for {@link Roo.util.JSON#encode}
14515  * @member Roo encode 
14516  * @method */
14517 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14518 /** 
14519  * Shorthand for {@link Roo.util.JSON#decode}
14520  * @member Roo decode 
14521  * @method */
14522 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14523 /*
14524  * Based on:
14525  * Ext JS Library 1.1.1
14526  * Copyright(c) 2006-2007, Ext JS, LLC.
14527  *
14528  * Originally Released Under LGPL - original licence link has changed is not relivant.
14529  *
14530  * Fork - LGPL
14531  * <script type="text/javascript">
14532  */
14533  
14534 /**
14535  * @class Roo.util.Format
14536  * Reusable data formatting functions
14537  * @static
14538  */
14539 Roo.util.Format = function(){
14540     var trimRe = /^\s+|\s+$/g;
14541     return {
14542         /**
14543          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14544          * @param {String} value The string to truncate
14545          * @param {Number} length The maximum length to allow before truncating
14546          * @return {String} The converted text
14547          */
14548         ellipsis : function(value, len){
14549             if(value && value.length > len){
14550                 return value.substr(0, len-3)+"...";
14551             }
14552             return value;
14553         },
14554
14555         /**
14556          * Checks a reference and converts it to empty string if it is undefined
14557          * @param {Mixed} value Reference to check
14558          * @return {Mixed} Empty string if converted, otherwise the original value
14559          */
14560         undef : function(value){
14561             return typeof value != "undefined" ? value : "";
14562         },
14563
14564         /**
14565          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14566          * @param {String} value The string to encode
14567          * @return {String} The encoded text
14568          */
14569         htmlEncode : function(value){
14570             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14571         },
14572
14573         /**
14574          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14575          * @param {String} value The string to decode
14576          * @return {String} The decoded text
14577          */
14578         htmlDecode : function(value){
14579             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14580         },
14581
14582         /**
14583          * Trims any whitespace from either side of a string
14584          * @param {String} value The text to trim
14585          * @return {String} The trimmed text
14586          */
14587         trim : function(value){
14588             return String(value).replace(trimRe, "");
14589         },
14590
14591         /**
14592          * Returns a substring from within an original string
14593          * @param {String} value The original text
14594          * @param {Number} start The start index of the substring
14595          * @param {Number} length The length of the substring
14596          * @return {String} The substring
14597          */
14598         substr : function(value, start, length){
14599             return String(value).substr(start, length);
14600         },
14601
14602         /**
14603          * Converts a string to all lower case letters
14604          * @param {String} value The text to convert
14605          * @return {String} The converted text
14606          */
14607         lowercase : function(value){
14608             return String(value).toLowerCase();
14609         },
14610
14611         /**
14612          * Converts a string to all upper case letters
14613          * @param {String} value The text to convert
14614          * @return {String} The converted text
14615          */
14616         uppercase : function(value){
14617             return String(value).toUpperCase();
14618         },
14619
14620         /**
14621          * Converts the first character only of a string to upper case
14622          * @param {String} value The text to convert
14623          * @return {String} The converted text
14624          */
14625         capitalize : function(value){
14626             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14627         },
14628
14629         // private
14630         call : function(value, fn){
14631             if(arguments.length > 2){
14632                 var args = Array.prototype.slice.call(arguments, 2);
14633                 args.unshift(value);
14634                  
14635                 return /** eval:var:value */  eval(fn).apply(window, args);
14636             }else{
14637                 /** eval:var:value */
14638                 return /** eval:var:value */ eval(fn).call(window, value);
14639             }
14640         },
14641
14642        
14643         /**
14644          * safer version of Math.toFixed..??/
14645          * @param {Number/String} value The numeric value to format
14646          * @param {Number/String} value Decimal places 
14647          * @return {String} The formatted currency string
14648          */
14649         toFixed : function(v, n)
14650         {
14651             // why not use to fixed - precision is buggered???
14652             if (!n) {
14653                 return Math.round(v-0);
14654             }
14655             var fact = Math.pow(10,n+1);
14656             v = (Math.round((v-0)*fact))/fact;
14657             var z = (''+fact).substring(2);
14658             if (v == Math.floor(v)) {
14659                 return Math.floor(v) + '.' + z;
14660             }
14661             
14662             // now just padd decimals..
14663             var ps = String(v).split('.');
14664             var fd = (ps[1] + z);
14665             var r = fd.substring(0,n); 
14666             var rm = fd.substring(n); 
14667             if (rm < 5) {
14668                 return ps[0] + '.' + r;
14669             }
14670             r*=1; // turn it into a number;
14671             r++;
14672             if (String(r).length != n) {
14673                 ps[0]*=1;
14674                 ps[0]++;
14675                 r = String(r).substring(1); // chop the end off.
14676             }
14677             
14678             return ps[0] + '.' + r;
14679              
14680         },
14681         
14682         /**
14683          * Format a number as US currency
14684          * @param {Number/String} value The numeric value to format
14685          * @return {String} The formatted currency string
14686          */
14687         usMoney : function(v){
14688             return '$' + Roo.util.Format.number(v);
14689         },
14690         
14691         /**
14692          * Format a number
14693          * eventually this should probably emulate php's number_format
14694          * @param {Number/String} value The numeric value to format
14695          * @param {Number} decimals number of decimal places
14696          * @param {String} delimiter for thousands (default comma)
14697          * @return {String} The formatted currency string
14698          */
14699         number : function(v, decimals, thousandsDelimiter)
14700         {
14701             // multiply and round.
14702             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14703             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14704             
14705             var mul = Math.pow(10, decimals);
14706             var zero = String(mul).substring(1);
14707             v = (Math.round((v-0)*mul))/mul;
14708             
14709             // if it's '0' number.. then
14710             
14711             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14712             v = String(v);
14713             var ps = v.split('.');
14714             var whole = ps[0];
14715             
14716             var r = /(\d+)(\d{3})/;
14717             // add comma's
14718             
14719             if(thousandsDelimiter.length != 0) {
14720                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14721             } 
14722             
14723             var sub = ps[1] ?
14724                     // has decimals..
14725                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14726                     // does not have decimals
14727                     (decimals ? ('.' + zero) : '');
14728             
14729             
14730             return whole + sub ;
14731         },
14732         
14733         /**
14734          * Parse a value into a formatted date using the specified format pattern.
14735          * @param {Mixed} value The value to format
14736          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14737          * @return {String} The formatted date string
14738          */
14739         date : function(v, format){
14740             if(!v){
14741                 return "";
14742             }
14743             if(!(v instanceof Date)){
14744                 v = new Date(Date.parse(v));
14745             }
14746             return v.dateFormat(format || Roo.util.Format.defaults.date);
14747         },
14748
14749         /**
14750          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14751          * @param {String} format Any valid date format string
14752          * @return {Function} The date formatting function
14753          */
14754         dateRenderer : function(format){
14755             return function(v){
14756                 return Roo.util.Format.date(v, format);  
14757             };
14758         },
14759
14760         // private
14761         stripTagsRE : /<\/?[^>]+>/gi,
14762         
14763         /**
14764          * Strips all HTML tags
14765          * @param {Mixed} value The text from which to strip tags
14766          * @return {String} The stripped text
14767          */
14768         stripTags : function(v){
14769             return !v ? v : String(v).replace(this.stripTagsRE, "");
14770         },
14771         
14772         /**
14773          * Size in Mb,Gb etc.
14774          * @param {Number} value The number to be formated
14775          * @param {number} decimals how many decimal places
14776          * @return {String} the formated string
14777          */
14778         size : function(value, decimals)
14779         {
14780             var sizes = ['b', 'k', 'M', 'G', 'T'];
14781             if (value == 0) {
14782                 return 0;
14783             }
14784             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14785             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14786         }
14787         
14788         
14789         
14790     };
14791 }();
14792 Roo.util.Format.defaults = {
14793     date : 'd/M/Y'
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805
14806  
14807
14808 /**
14809  * @class Roo.MasterTemplate
14810  * @extends Roo.Template
14811  * Provides a template that can have child templates. The syntax is:
14812 <pre><code>
14813 var t = new Roo.MasterTemplate(
14814         '&lt;select name="{name}"&gt;',
14815                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14816         '&lt;/select&gt;'
14817 );
14818 t.add('options', {value: 'foo', text: 'bar'});
14819 // or you can add multiple child elements in one shot
14820 t.addAll('options', [
14821     {value: 'foo', text: 'bar'},
14822     {value: 'foo2', text: 'bar2'},
14823     {value: 'foo3', text: 'bar3'}
14824 ]);
14825 // then append, applying the master template values
14826 t.append('my-form', {name: 'my-select'});
14827 </code></pre>
14828 * A name attribute for the child template is not required if you have only one child
14829 * template or you want to refer to them by index.
14830  */
14831 Roo.MasterTemplate = function(){
14832     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14833     this.originalHtml = this.html;
14834     var st = {};
14835     var m, re = this.subTemplateRe;
14836     re.lastIndex = 0;
14837     var subIndex = 0;
14838     while(m = re.exec(this.html)){
14839         var name = m[1], content = m[2];
14840         st[subIndex] = {
14841             name: name,
14842             index: subIndex,
14843             buffer: [],
14844             tpl : new Roo.Template(content)
14845         };
14846         if(name){
14847             st[name] = st[subIndex];
14848         }
14849         st[subIndex].tpl.compile();
14850         st[subIndex].tpl.call = this.call.createDelegate(this);
14851         subIndex++;
14852     }
14853     this.subCount = subIndex;
14854     this.subs = st;
14855 };
14856 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14857     /**
14858     * The regular expression used to match sub templates
14859     * @type RegExp
14860     * @property
14861     */
14862     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14863
14864     /**
14865      * Applies the passed values to a child template.
14866      * @param {String/Number} name (optional) The name or index of the child template
14867      * @param {Array/Object} values The values to be applied to the template
14868      * @return {MasterTemplate} this
14869      */
14870      add : function(name, values){
14871         if(arguments.length == 1){
14872             values = arguments[0];
14873             name = 0;
14874         }
14875         var s = this.subs[name];
14876         s.buffer[s.buffer.length] = s.tpl.apply(values);
14877         return this;
14878     },
14879
14880     /**
14881      * Applies all the passed values to a child template.
14882      * @param {String/Number} name (optional) The name or index of the child template
14883      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14884      * @param {Boolean} reset (optional) True to reset the template first
14885      * @return {MasterTemplate} this
14886      */
14887     fill : function(name, values, reset){
14888         var a = arguments;
14889         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14890             values = a[0];
14891             name = 0;
14892             reset = a[1];
14893         }
14894         if(reset){
14895             this.reset();
14896         }
14897         for(var i = 0, len = values.length; i < len; i++){
14898             this.add(name, values[i]);
14899         }
14900         return this;
14901     },
14902
14903     /**
14904      * Resets the template for reuse
14905      * @return {MasterTemplate} this
14906      */
14907      reset : function(){
14908         var s = this.subs;
14909         for(var i = 0; i < this.subCount; i++){
14910             s[i].buffer = [];
14911         }
14912         return this;
14913     },
14914
14915     applyTemplate : function(values){
14916         var s = this.subs;
14917         var replaceIndex = -1;
14918         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14919             return s[++replaceIndex].buffer.join("");
14920         });
14921         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14922     },
14923
14924     apply : function(){
14925         return this.applyTemplate.apply(this, arguments);
14926     },
14927
14928     compile : function(){return this;}
14929 });
14930
14931 /**
14932  * Alias for fill().
14933  * @method
14934  */
14935 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14936  /**
14937  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14938  * var tpl = Roo.MasterTemplate.from('element-id');
14939  * @param {String/HTMLElement} el
14940  * @param {Object} config
14941  * @static
14942  */
14943 Roo.MasterTemplate.from = function(el, config){
14944     el = Roo.getDom(el);
14945     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14946 };/*
14947  * Based on:
14948  * Ext JS Library 1.1.1
14949  * Copyright(c) 2006-2007, Ext JS, LLC.
14950  *
14951  * Originally Released Under LGPL - original licence link has changed is not relivant.
14952  *
14953  * Fork - LGPL
14954  * <script type="text/javascript">
14955  */
14956
14957  
14958 /**
14959  * @class Roo.util.CSS
14960  * Utility class for manipulating CSS rules
14961  * @static
14962
14963  */
14964 Roo.util.CSS = function(){
14965         var rules = null;
14966         var doc = document;
14967
14968     var camelRe = /(-[a-z])/gi;
14969     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14970
14971    return {
14972    /**
14973     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14974     * tag and appended to the HEAD of the document.
14975     * @param {String|Object} cssText The text containing the css rules
14976     * @param {String} id An id to add to the stylesheet for later removal
14977     * @return {StyleSheet}
14978     */
14979     createStyleSheet : function(cssText, id){
14980         var ss;
14981         var head = doc.getElementsByTagName("head")[0];
14982         var nrules = doc.createElement("style");
14983         nrules.setAttribute("type", "text/css");
14984         if(id){
14985             nrules.setAttribute("id", id);
14986         }
14987         if (typeof(cssText) != 'string') {
14988             // support object maps..
14989             // not sure if this a good idea.. 
14990             // perhaps it should be merged with the general css handling
14991             // and handle js style props.
14992             var cssTextNew = [];
14993             for(var n in cssText) {
14994                 var citems = [];
14995                 for(var k in cssText[n]) {
14996                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14997                 }
14998                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14999                 
15000             }
15001             cssText = cssTextNew.join("\n");
15002             
15003         }
15004        
15005        
15006        if(Roo.isIE){
15007            head.appendChild(nrules);
15008            ss = nrules.styleSheet;
15009            ss.cssText = cssText;
15010        }else{
15011            try{
15012                 nrules.appendChild(doc.createTextNode(cssText));
15013            }catch(e){
15014                nrules.cssText = cssText; 
15015            }
15016            head.appendChild(nrules);
15017            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15018        }
15019        this.cacheStyleSheet(ss);
15020        return ss;
15021    },
15022
15023    /**
15024     * Removes a style or link tag by id
15025     * @param {String} id The id of the tag
15026     */
15027    removeStyleSheet : function(id){
15028        var existing = doc.getElementById(id);
15029        if(existing){
15030            existing.parentNode.removeChild(existing);
15031        }
15032    },
15033
15034    /**
15035     * Dynamically swaps an existing stylesheet reference for a new one
15036     * @param {String} id The id of an existing link tag to remove
15037     * @param {String} url The href of the new stylesheet to include
15038     */
15039    swapStyleSheet : function(id, url){
15040        this.removeStyleSheet(id);
15041        var ss = doc.createElement("link");
15042        ss.setAttribute("rel", "stylesheet");
15043        ss.setAttribute("type", "text/css");
15044        ss.setAttribute("id", id);
15045        ss.setAttribute("href", url);
15046        doc.getElementsByTagName("head")[0].appendChild(ss);
15047    },
15048    
15049    /**
15050     * Refresh the rule cache if you have dynamically added stylesheets
15051     * @return {Object} An object (hash) of rules indexed by selector
15052     */
15053    refreshCache : function(){
15054        return this.getRules(true);
15055    },
15056
15057    // private
15058    cacheStyleSheet : function(stylesheet){
15059        if(!rules){
15060            rules = {};
15061        }
15062        try{// try catch for cross domain access issue
15063            var ssRules = stylesheet.cssRules || stylesheet.rules;
15064            for(var j = ssRules.length-1; j >= 0; --j){
15065                rules[ssRules[j].selectorText] = ssRules[j];
15066            }
15067        }catch(e){}
15068    },
15069    
15070    /**
15071     * Gets all css rules for the document
15072     * @param {Boolean} refreshCache true to refresh the internal cache
15073     * @return {Object} An object (hash) of rules indexed by selector
15074     */
15075    getRules : function(refreshCache){
15076                 if(rules == null || refreshCache){
15077                         rules = {};
15078                         var ds = doc.styleSheets;
15079                         for(var i =0, len = ds.length; i < len; i++){
15080                             try{
15081                         this.cacheStyleSheet(ds[i]);
15082                     }catch(e){} 
15083                 }
15084                 }
15085                 return rules;
15086         },
15087         
15088         /**
15089     * Gets an an individual CSS rule by selector(s)
15090     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15091     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15092     * @return {CSSRule} The CSS rule or null if one is not found
15093     */
15094    getRule : function(selector, refreshCache){
15095                 var rs = this.getRules(refreshCache);
15096                 if(!(selector instanceof Array)){
15097                     return rs[selector];
15098                 }
15099                 for(var i = 0; i < selector.length; i++){
15100                         if(rs[selector[i]]){
15101                                 return rs[selector[i]];
15102                         }
15103                 }
15104                 return null;
15105         },
15106         
15107         
15108         /**
15109     * Updates a rule property
15110     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15111     * @param {String} property The css property
15112     * @param {String} value The new value for the property
15113     * @return {Boolean} true If a rule was found and updated
15114     */
15115    updateRule : function(selector, property, value){
15116                 if(!(selector instanceof Array)){
15117                         var rule = this.getRule(selector);
15118                         if(rule){
15119                                 rule.style[property.replace(camelRe, camelFn)] = value;
15120                                 return true;
15121                         }
15122                 }else{
15123                         for(var i = 0; i < selector.length; i++){
15124                                 if(this.updateRule(selector[i], property, value)){
15125                                         return true;
15126                                 }
15127                         }
15128                 }
15129                 return false;
15130         }
15131    };   
15132 }();/*
15133  * Based on:
15134  * Ext JS Library 1.1.1
15135  * Copyright(c) 2006-2007, Ext JS, LLC.
15136  *
15137  * Originally Released Under LGPL - original licence link has changed is not relivant.
15138  *
15139  * Fork - LGPL
15140  * <script type="text/javascript">
15141  */
15142
15143  
15144
15145 /**
15146  * @class Roo.util.ClickRepeater
15147  * @extends Roo.util.Observable
15148  * 
15149  * A wrapper class which can be applied to any element. Fires a "click" event while the
15150  * mouse is pressed. The interval between firings may be specified in the config but
15151  * defaults to 10 milliseconds.
15152  * 
15153  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15154  * 
15155  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15156  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15157  * Similar to an autorepeat key delay.
15158  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15159  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15160  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15161  *           "interval" and "delay" are ignored. "immediate" is honored.
15162  * @cfg {Boolean} preventDefault True to prevent the default click event
15163  * @cfg {Boolean} stopDefault True to stop the default click event
15164  * 
15165  * @history
15166  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15167  *     2007-02-02 jvs Renamed to ClickRepeater
15168  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15169  *
15170  *  @constructor
15171  * @param {String/HTMLElement/Element} el The element to listen on
15172  * @param {Object} config
15173  **/
15174 Roo.util.ClickRepeater = function(el, config)
15175 {
15176     this.el = Roo.get(el);
15177     this.el.unselectable();
15178
15179     Roo.apply(this, config);
15180
15181     this.addEvents({
15182     /**
15183      * @event mousedown
15184      * Fires when the mouse button is depressed.
15185      * @param {Roo.util.ClickRepeater} this
15186      */
15187         "mousedown" : true,
15188     /**
15189      * @event click
15190      * Fires on a specified interval during the time the element is pressed.
15191      * @param {Roo.util.ClickRepeater} this
15192      */
15193         "click" : true,
15194     /**
15195      * @event mouseup
15196      * Fires when the mouse key is released.
15197      * @param {Roo.util.ClickRepeater} this
15198      */
15199         "mouseup" : true
15200     });
15201
15202     this.el.on("mousedown", this.handleMouseDown, this);
15203     if(this.preventDefault || this.stopDefault){
15204         this.el.on("click", function(e){
15205             if(this.preventDefault){
15206                 e.preventDefault();
15207             }
15208             if(this.stopDefault){
15209                 e.stopEvent();
15210             }
15211         }, this);
15212     }
15213
15214     // allow inline handler
15215     if(this.handler){
15216         this.on("click", this.handler,  this.scope || this);
15217     }
15218
15219     Roo.util.ClickRepeater.superclass.constructor.call(this);
15220 };
15221
15222 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15223     interval : 20,
15224     delay: 250,
15225     preventDefault : true,
15226     stopDefault : false,
15227     timer : 0,
15228
15229     // private
15230     handleMouseDown : function(){
15231         clearTimeout(this.timer);
15232         this.el.blur();
15233         if(this.pressClass){
15234             this.el.addClass(this.pressClass);
15235         }
15236         this.mousedownTime = new Date();
15237
15238         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15239         this.el.on("mouseout", this.handleMouseOut, this);
15240
15241         this.fireEvent("mousedown", this);
15242         this.fireEvent("click", this);
15243         
15244         this.timer = this.click.defer(this.delay || this.interval, this);
15245     },
15246
15247     // private
15248     click : function(){
15249         this.fireEvent("click", this);
15250         this.timer = this.click.defer(this.getInterval(), this);
15251     },
15252
15253     // private
15254     getInterval: function(){
15255         if(!this.accelerate){
15256             return this.interval;
15257         }
15258         var pressTime = this.mousedownTime.getElapsed();
15259         if(pressTime < 500){
15260             return 400;
15261         }else if(pressTime < 1700){
15262             return 320;
15263         }else if(pressTime < 2600){
15264             return 250;
15265         }else if(pressTime < 3500){
15266             return 180;
15267         }else if(pressTime < 4400){
15268             return 140;
15269         }else if(pressTime < 5300){
15270             return 80;
15271         }else if(pressTime < 6200){
15272             return 50;
15273         }else{
15274             return 10;
15275         }
15276     },
15277
15278     // private
15279     handleMouseOut : function(){
15280         clearTimeout(this.timer);
15281         if(this.pressClass){
15282             this.el.removeClass(this.pressClass);
15283         }
15284         this.el.on("mouseover", this.handleMouseReturn, this);
15285     },
15286
15287     // private
15288     handleMouseReturn : function(){
15289         this.el.un("mouseover", this.handleMouseReturn);
15290         if(this.pressClass){
15291             this.el.addClass(this.pressClass);
15292         }
15293         this.click();
15294     },
15295
15296     // private
15297     handleMouseUp : function(){
15298         clearTimeout(this.timer);
15299         this.el.un("mouseover", this.handleMouseReturn);
15300         this.el.un("mouseout", this.handleMouseOut);
15301         Roo.get(document).un("mouseup", this.handleMouseUp);
15302         this.el.removeClass(this.pressClass);
15303         this.fireEvent("mouseup", this);
15304     }
15305 });/**
15306  * @class Roo.util.Clipboard
15307  * @static
15308  * 
15309  * Clipboard UTILS
15310  * 
15311  **/
15312 Roo.util.Clipboard = {
15313     /**
15314      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15315      * @param {String} text to copy to clipboard
15316      */
15317     write : function(text) {
15318         // navigator clipboard api needs a secure context (https)
15319         if (navigator.clipboard && window.isSecureContext) {
15320             // navigator clipboard api method'
15321             navigator.clipboard.writeText(text);
15322             return ;
15323         } 
15324         // text area method
15325         var ta = document.createElement("textarea");
15326         ta.value = text;
15327         // make the textarea out of viewport
15328         ta.style.position = "fixed";
15329         ta.style.left = "-999999px";
15330         ta.style.top = "-999999px";
15331         document.body.appendChild(ta);
15332         ta.focus();
15333         ta.select();
15334         document.execCommand('copy');
15335         (function() {
15336             ta.remove();
15337         }).defer(100);
15338         
15339     }
15340         
15341 }
15342     /*
15343  * Based on:
15344  * Ext JS Library 1.1.1
15345  * Copyright(c) 2006-2007, Ext JS, LLC.
15346  *
15347  * Originally Released Under LGPL - original licence link has changed is not relivant.
15348  *
15349  * Fork - LGPL
15350  * <script type="text/javascript">
15351  */
15352
15353  
15354 /**
15355  * @class Roo.KeyNav
15356  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15357  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15358  * way to implement custom navigation schemes for any UI component.</p>
15359  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15360  * pageUp, pageDown, del, home, end.  Usage:</p>
15361  <pre><code>
15362 var nav = new Roo.KeyNav("my-element", {
15363     "left" : function(e){
15364         this.moveLeft(e.ctrlKey);
15365     },
15366     "right" : function(e){
15367         this.moveRight(e.ctrlKey);
15368     },
15369     "enter" : function(e){
15370         this.save();
15371     },
15372     scope : this
15373 });
15374 </code></pre>
15375  * @constructor
15376  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15377  * @param {Object} config The config
15378  */
15379 Roo.KeyNav = function(el, config){
15380     this.el = Roo.get(el);
15381     Roo.apply(this, config);
15382     if(!this.disabled){
15383         this.disabled = true;
15384         this.enable();
15385     }
15386 };
15387
15388 Roo.KeyNav.prototype = {
15389     /**
15390      * @cfg {Boolean} disabled
15391      * True to disable this KeyNav instance (defaults to false)
15392      */
15393     disabled : false,
15394     /**
15395      * @cfg {String} defaultEventAction
15396      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15397      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15398      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15399      */
15400     defaultEventAction: "stopEvent",
15401     /**
15402      * @cfg {Boolean} forceKeyDown
15403      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15404      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15405      * handle keydown instead of keypress.
15406      */
15407     forceKeyDown : false,
15408
15409     // private
15410     prepareEvent : function(e){
15411         var k = e.getKey();
15412         var h = this.keyToHandler[k];
15413         //if(h && this[h]){
15414         //    e.stopPropagation();
15415         //}
15416         if(Roo.isSafari && h && k >= 37 && k <= 40){
15417             e.stopEvent();
15418         }
15419     },
15420
15421     // private
15422     relay : function(e){
15423         var k = e.getKey();
15424         var h = this.keyToHandler[k];
15425         if(h && this[h]){
15426             if(this.doRelay(e, this[h], h) !== true){
15427                 e[this.defaultEventAction]();
15428             }
15429         }
15430     },
15431
15432     // private
15433     doRelay : function(e, h, hname){
15434         return h.call(this.scope || this, e);
15435     },
15436
15437     // possible handlers
15438     enter : false,
15439     left : false,
15440     right : false,
15441     up : false,
15442     down : false,
15443     tab : false,
15444     esc : false,
15445     pageUp : false,
15446     pageDown : false,
15447     del : false,
15448     home : false,
15449     end : false,
15450
15451     // quick lookup hash
15452     keyToHandler : {
15453         37 : "left",
15454         39 : "right",
15455         38 : "up",
15456         40 : "down",
15457         33 : "pageUp",
15458         34 : "pageDown",
15459         46 : "del",
15460         36 : "home",
15461         35 : "end",
15462         13 : "enter",
15463         27 : "esc",
15464         9  : "tab"
15465     },
15466
15467         /**
15468          * Enable this KeyNav
15469          */
15470         enable: function(){
15471                 if(this.disabled){
15472             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15473             // the EventObject will normalize Safari automatically
15474             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15475                 this.el.on("keydown", this.relay,  this);
15476             }else{
15477                 this.el.on("keydown", this.prepareEvent,  this);
15478                 this.el.on("keypress", this.relay,  this);
15479             }
15480                     this.disabled = false;
15481                 }
15482         },
15483
15484         /**
15485          * Disable this KeyNav
15486          */
15487         disable: function(){
15488                 if(!this.disabled){
15489                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15490                 this.el.un("keydown", this.relay);
15491             }else{
15492                 this.el.un("keydown", this.prepareEvent);
15493                 this.el.un("keypress", this.relay);
15494             }
15495                     this.disabled = true;
15496                 }
15497         }
15498 };/*
15499  * Based on:
15500  * Ext JS Library 1.1.1
15501  * Copyright(c) 2006-2007, Ext JS, LLC.
15502  *
15503  * Originally Released Under LGPL - original licence link has changed is not relivant.
15504  *
15505  * Fork - LGPL
15506  * <script type="text/javascript">
15507  */
15508
15509  
15510 /**
15511  * @class Roo.KeyMap
15512  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15513  * The constructor accepts the same config object as defined by {@link #addBinding}.
15514  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15515  * combination it will call the function with this signature (if the match is a multi-key
15516  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15517  * A KeyMap can also handle a string representation of keys.<br />
15518  * Usage:
15519  <pre><code>
15520 // map one key by key code
15521 var map = new Roo.KeyMap("my-element", {
15522     key: 13, // or Roo.EventObject.ENTER
15523     fn: myHandler,
15524     scope: myObject
15525 });
15526
15527 // map multiple keys to one action by string
15528 var map = new Roo.KeyMap("my-element", {
15529     key: "a\r\n\t",
15530     fn: myHandler,
15531     scope: myObject
15532 });
15533
15534 // map multiple keys to multiple actions by strings and array of codes
15535 var map = new Roo.KeyMap("my-element", [
15536     {
15537         key: [10,13],
15538         fn: function(){ alert("Return was pressed"); }
15539     }, {
15540         key: "abc",
15541         fn: function(){ alert('a, b or c was pressed'); }
15542     }, {
15543         key: "\t",
15544         ctrl:true,
15545         shift:true,
15546         fn: function(){ alert('Control + shift + tab was pressed.'); }
15547     }
15548 ]);
15549 </code></pre>
15550  * <b>Note: A KeyMap starts enabled</b>
15551  * @constructor
15552  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15553  * @param {Object} config The config (see {@link #addBinding})
15554  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15555  */
15556 Roo.KeyMap = function(el, config, eventName){
15557     this.el  = Roo.get(el);
15558     this.eventName = eventName || "keydown";
15559     this.bindings = [];
15560     if(config){
15561         this.addBinding(config);
15562     }
15563     this.enable();
15564 };
15565
15566 Roo.KeyMap.prototype = {
15567     /**
15568      * True to stop the event from bubbling and prevent the default browser action if the
15569      * key was handled by the KeyMap (defaults to false)
15570      * @type Boolean
15571      */
15572     stopEvent : false,
15573
15574     /**
15575      * Add a new binding to this KeyMap. The following config object properties are supported:
15576      * <pre>
15577 Property    Type             Description
15578 ----------  ---------------  ----------------------------------------------------------------------
15579 key         String/Array     A single keycode or an array of keycodes to handle
15580 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15581 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15582 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15583 fn          Function         The function to call when KeyMap finds the expected key combination
15584 scope       Object           The scope of the callback function
15585 </pre>
15586      *
15587      * Usage:
15588      * <pre><code>
15589 // Create a KeyMap
15590 var map = new Roo.KeyMap(document, {
15591     key: Roo.EventObject.ENTER,
15592     fn: handleKey,
15593     scope: this
15594 });
15595
15596 //Add a new binding to the existing KeyMap later
15597 map.addBinding({
15598     key: 'abc',
15599     shift: true,
15600     fn: handleKey,
15601     scope: this
15602 });
15603 </code></pre>
15604      * @param {Object/Array} config A single KeyMap config or an array of configs
15605      */
15606         addBinding : function(config){
15607         if(config instanceof Array){
15608             for(var i = 0, len = config.length; i < len; i++){
15609                 this.addBinding(config[i]);
15610             }
15611             return;
15612         }
15613         var keyCode = config.key,
15614             shift = config.shift, 
15615             ctrl = config.ctrl, 
15616             alt = config.alt,
15617             fn = config.fn,
15618             scope = config.scope;
15619         if(typeof keyCode == "string"){
15620             var ks = [];
15621             var keyString = keyCode.toUpperCase();
15622             for(var j = 0, len = keyString.length; j < len; j++){
15623                 ks.push(keyString.charCodeAt(j));
15624             }
15625             keyCode = ks;
15626         }
15627         var keyArray = keyCode instanceof Array;
15628         var handler = function(e){
15629             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15630                 var k = e.getKey();
15631                 if(keyArray){
15632                     for(var i = 0, len = keyCode.length; i < len; i++){
15633                         if(keyCode[i] == k){
15634                           if(this.stopEvent){
15635                               e.stopEvent();
15636                           }
15637                           fn.call(scope || window, k, e);
15638                           return;
15639                         }
15640                     }
15641                 }else{
15642                     if(k == keyCode){
15643                         if(this.stopEvent){
15644                            e.stopEvent();
15645                         }
15646                         fn.call(scope || window, k, e);
15647                     }
15648                 }
15649             }
15650         };
15651         this.bindings.push(handler);  
15652         },
15653
15654     /**
15655      * Shorthand for adding a single key listener
15656      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15657      * following options:
15658      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15659      * @param {Function} fn The function to call
15660      * @param {Object} scope (optional) The scope of the function
15661      */
15662     on : function(key, fn, scope){
15663         var keyCode, shift, ctrl, alt;
15664         if(typeof key == "object" && !(key instanceof Array)){
15665             keyCode = key.key;
15666             shift = key.shift;
15667             ctrl = key.ctrl;
15668             alt = key.alt;
15669         }else{
15670             keyCode = key;
15671         }
15672         this.addBinding({
15673             key: keyCode,
15674             shift: shift,
15675             ctrl: ctrl,
15676             alt: alt,
15677             fn: fn,
15678             scope: scope
15679         })
15680     },
15681
15682     // private
15683     handleKeyDown : function(e){
15684             if(this.enabled){ //just in case
15685             var b = this.bindings;
15686             for(var i = 0, len = b.length; i < len; i++){
15687                 b[i].call(this, e);
15688             }
15689             }
15690         },
15691         
15692         /**
15693          * Returns true if this KeyMap is enabled
15694          * @return {Boolean} 
15695          */
15696         isEnabled : function(){
15697             return this.enabled;  
15698         },
15699         
15700         /**
15701          * Enables this KeyMap
15702          */
15703         enable: function(){
15704                 if(!this.enabled){
15705                     this.el.on(this.eventName, this.handleKeyDown, this);
15706                     this.enabled = true;
15707                 }
15708         },
15709
15710         /**
15711          * Disable this KeyMap
15712          */
15713         disable: function(){
15714                 if(this.enabled){
15715                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15716                     this.enabled = false;
15717                 }
15718         }
15719 };/*
15720  * Based on:
15721  * Ext JS Library 1.1.1
15722  * Copyright(c) 2006-2007, Ext JS, LLC.
15723  *
15724  * Originally Released Under LGPL - original licence link has changed is not relivant.
15725  *
15726  * Fork - LGPL
15727  * <script type="text/javascript">
15728  */
15729
15730  
15731 /**
15732  * @class Roo.util.TextMetrics
15733  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15734  * wide, in pixels, a given block of text will be.
15735  * @static
15736  */
15737 Roo.util.TextMetrics = function(){
15738     var shared;
15739     return {
15740         /**
15741          * Measures the size of the specified text
15742          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15743          * that can affect the size of the rendered text
15744          * @param {String} text The text to measure
15745          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15746          * in order to accurately measure the text height
15747          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15748          */
15749         measure : function(el, text, fixedWidth){
15750             if(!shared){
15751                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15752             }
15753             shared.bind(el);
15754             shared.setFixedWidth(fixedWidth || 'auto');
15755             return shared.getSize(text);
15756         },
15757
15758         /**
15759          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15760          * the overhead of multiple calls to initialize the style properties on each measurement.
15761          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15762          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15763          * in order to accurately measure the text height
15764          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15765          */
15766         createInstance : function(el, fixedWidth){
15767             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15768         }
15769     };
15770 }();
15771
15772 /**
15773  * @class Roo.util.TextMetrics.Instance
15774  * Instance of  TextMetrics Calcuation
15775  * @constructor
15776  * Create a new TextMetrics Instance
15777  * @param {Object} bindto
15778  * @param {Boolean} fixedWidth
15779  */
15780
15781 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15782 {
15783     var ml = new Roo.Element(document.createElement('div'));
15784     document.body.appendChild(ml.dom);
15785     ml.position('absolute');
15786     ml.setLeftTop(-1000, -1000);
15787     ml.hide();
15788
15789     if(fixedWidth){
15790         ml.setWidth(fixedWidth);
15791     }
15792      
15793     var instance = {
15794         /**
15795          * Returns the size of the specified text based on the internal element's style and width properties
15796          * @param {String} text The text to measure
15797          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15798          */
15799         getSize : function(text){
15800             ml.update(text);
15801             var s = ml.getSize();
15802             ml.update('');
15803             return s;
15804         },
15805
15806         /**
15807          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15808          * that can affect the size of the rendered text
15809          * @param {String/HTMLElement} el The element, dom node or id
15810          */
15811         bind : function(el){
15812             ml.setStyle(
15813                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15814             );
15815         },
15816
15817         /**
15818          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15819          * to set a fixed width in order to accurately measure the text height.
15820          * @param {Number} width The width to set on the element
15821          */
15822         setFixedWidth : function(width){
15823             ml.setWidth(width);
15824         },
15825
15826         /**
15827          * Returns the measured width of the specified text
15828          * @param {String} text The text to measure
15829          * @return {Number} width The width in pixels
15830          */
15831         getWidth : function(text){
15832             ml.dom.style.width = 'auto';
15833             return this.getSize(text).width;
15834         },
15835
15836         /**
15837          * Returns the measured height of the specified text.  For multiline text, be sure to call
15838          * {@link #setFixedWidth} if necessary.
15839          * @param {String} text The text to measure
15840          * @return {Number} height The height in pixels
15841          */
15842         getHeight : function(text){
15843             return this.getSize(text).height;
15844         }
15845     };
15846
15847     instance.bind(bindTo);
15848
15849     return instance;
15850 };
15851
15852 // backwards compat
15853 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15854  * Based on:
15855  * Ext JS Library 1.1.1
15856  * Copyright(c) 2006-2007, Ext JS, LLC.
15857  *
15858  * Originally Released Under LGPL - original licence link has changed is not relivant.
15859  *
15860  * Fork - LGPL
15861  * <script type="text/javascript">
15862  */
15863
15864 /**
15865  * @class Roo.state.Provider
15866  * Abstract base class for state provider implementations. This class provides methods
15867  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15868  * Provider interface.
15869  */
15870 Roo.state.Provider = function(){
15871     /**
15872      * @event statechange
15873      * Fires when a state change occurs.
15874      * @param {Provider} this This state provider
15875      * @param {String} key The state key which was changed
15876      * @param {String} value The encoded value for the state
15877      */
15878     this.addEvents({
15879         "statechange": true
15880     });
15881     this.state = {};
15882     Roo.state.Provider.superclass.constructor.call(this);
15883 };
15884 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15885     /**
15886      * Returns the current value for a key
15887      * @param {String} name The key name
15888      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15889      * @return {Mixed} The state data
15890      */
15891     get : function(name, defaultValue){
15892         return typeof this.state[name] == "undefined" ?
15893             defaultValue : this.state[name];
15894     },
15895     
15896     /**
15897      * Clears a value from the state
15898      * @param {String} name The key name
15899      */
15900     clear : function(name){
15901         delete this.state[name];
15902         this.fireEvent("statechange", this, name, null);
15903     },
15904     
15905     /**
15906      * Sets the value for a key
15907      * @param {String} name The key name
15908      * @param {Mixed} value The value to set
15909      */
15910     set : function(name, value){
15911         this.state[name] = value;
15912         this.fireEvent("statechange", this, name, value);
15913     },
15914     
15915     /**
15916      * Decodes a string previously encoded with {@link #encodeValue}.
15917      * @param {String} value The value to decode
15918      * @return {Mixed} The decoded value
15919      */
15920     decodeValue : function(cookie){
15921         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15922         var matches = re.exec(unescape(cookie));
15923         if(!matches || !matches[1]) {
15924             return; // non state cookie
15925         }
15926         var type = matches[1];
15927         var v = matches[2];
15928         switch(type){
15929             case "n":
15930                 return parseFloat(v);
15931             case "d":
15932                 return new Date(Date.parse(v));
15933             case "b":
15934                 return (v == "1");
15935             case "a":
15936                 var all = [];
15937                 var values = v.split("^");
15938                 for(var i = 0, len = values.length; i < len; i++){
15939                     all.push(this.decodeValue(values[i]));
15940                 }
15941                 return all;
15942            case "o":
15943                 var all = {};
15944                 var values = v.split("^");
15945                 for(var i = 0, len = values.length; i < len; i++){
15946                     var kv = values[i].split("=");
15947                     all[kv[0]] = this.decodeValue(kv[1]);
15948                 }
15949                 return all;
15950            default:
15951                 return v;
15952         }
15953     },
15954     
15955     /**
15956      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15957      * @param {Mixed} value The value to encode
15958      * @return {String} The encoded value
15959      */
15960     encodeValue : function(v){
15961         var enc;
15962         if(typeof v == "number"){
15963             enc = "n:" + v;
15964         }else if(typeof v == "boolean"){
15965             enc = "b:" + (v ? "1" : "0");
15966         }else if(v instanceof Date){
15967             enc = "d:" + v.toGMTString();
15968         }else if(v instanceof Array){
15969             var flat = "";
15970             for(var i = 0, len = v.length; i < len; i++){
15971                 flat += this.encodeValue(v[i]);
15972                 if(i != len-1) {
15973                     flat += "^";
15974                 }
15975             }
15976             enc = "a:" + flat;
15977         }else if(typeof v == "object"){
15978             var flat = "";
15979             for(var key in v){
15980                 if(typeof v[key] != "function"){
15981                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15982                 }
15983             }
15984             enc = "o:" + flat.substring(0, flat.length-1);
15985         }else{
15986             enc = "s:" + v;
15987         }
15988         return escape(enc);        
15989     }
15990 });
15991
15992 /*
15993  * Based on:
15994  * Ext JS Library 1.1.1
15995  * Copyright(c) 2006-2007, Ext JS, LLC.
15996  *
15997  * Originally Released Under LGPL - original licence link has changed is not relivant.
15998  *
15999  * Fork - LGPL
16000  * <script type="text/javascript">
16001  */
16002 /**
16003  * @class Roo.state.Manager
16004  * This is the global state manager. By default all components that are "state aware" check this class
16005  * for state information if you don't pass them a custom state provider. In order for this class
16006  * to be useful, it must be initialized with a provider when your application initializes.
16007  <pre><code>
16008 // in your initialization function
16009 init : function(){
16010    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16011    ...
16012    // supposed you have a {@link Roo.BorderLayout}
16013    var layout = new Roo.BorderLayout(...);
16014    layout.restoreState();
16015    // or a {Roo.BasicDialog}
16016    var dialog = new Roo.BasicDialog(...);
16017    dialog.restoreState();
16018  </code></pre>
16019  * @static
16020  */
16021 Roo.state.Manager = function(){
16022     var provider = new Roo.state.Provider();
16023     
16024     return {
16025         /**
16026          * Configures the default state provider for your application
16027          * @param {Provider} stateProvider The state provider to set
16028          */
16029         setProvider : function(stateProvider){
16030             provider = stateProvider;
16031         },
16032         
16033         /**
16034          * Returns the current value for a key
16035          * @param {String} name The key name
16036          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16037          * @return {Mixed} The state data
16038          */
16039         get : function(key, defaultValue){
16040             return provider.get(key, defaultValue);
16041         },
16042         
16043         /**
16044          * Sets the value for a key
16045          * @param {String} name The key name
16046          * @param {Mixed} value The state data
16047          */
16048          set : function(key, value){
16049             provider.set(key, value);
16050         },
16051         
16052         /**
16053          * Clears a value from the state
16054          * @param {String} name The key name
16055          */
16056         clear : function(key){
16057             provider.clear(key);
16058         },
16059         
16060         /**
16061          * Gets the currently configured state provider
16062          * @return {Provider} The state provider
16063          */
16064         getProvider : function(){
16065             return provider;
16066         }
16067     };
16068 }();
16069 /*
16070  * Based on:
16071  * Ext JS Library 1.1.1
16072  * Copyright(c) 2006-2007, Ext JS, LLC.
16073  *
16074  * Originally Released Under LGPL - original licence link has changed is not relivant.
16075  *
16076  * Fork - LGPL
16077  * <script type="text/javascript">
16078  */
16079 /**
16080  * @class Roo.state.CookieProvider
16081  * @extends Roo.state.Provider
16082  * The default Provider implementation which saves state via cookies.
16083  * <br />Usage:
16084  <pre><code>
16085    var cp = new Roo.state.CookieProvider({
16086        path: "/cgi-bin/",
16087        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16088        domain: "roojs.com"
16089    })
16090    Roo.state.Manager.setProvider(cp);
16091  </code></pre>
16092  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16093  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16094  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16095  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16096  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16097  * domain the page is running on including the 'www' like 'www.roojs.com')
16098  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16099  * @constructor
16100  * Create a new CookieProvider
16101  * @param {Object} config The configuration object
16102  */
16103 Roo.state.CookieProvider = function(config){
16104     Roo.state.CookieProvider.superclass.constructor.call(this);
16105     this.path = "/";
16106     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16107     this.domain = null;
16108     this.secure = false;
16109     Roo.apply(this, config);
16110     this.state = this.readCookies();
16111 };
16112
16113 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16114     // private
16115     set : function(name, value){
16116         if(typeof value == "undefined" || value === null){
16117             this.clear(name);
16118             return;
16119         }
16120         this.setCookie(name, value);
16121         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16122     },
16123
16124     // private
16125     clear : function(name){
16126         this.clearCookie(name);
16127         Roo.state.CookieProvider.superclass.clear.call(this, name);
16128     },
16129
16130     // private
16131     readCookies : function(){
16132         var cookies = {};
16133         var c = document.cookie + ";";
16134         var re = /\s?(.*?)=(.*?);/g;
16135         var matches;
16136         while((matches = re.exec(c)) != null){
16137             var name = matches[1];
16138             var value = matches[2];
16139             if(name && name.substring(0,3) == "ys-"){
16140                 cookies[name.substr(3)] = this.decodeValue(value);
16141             }
16142         }
16143         return cookies;
16144     },
16145
16146     // private
16147     setCookie : function(name, value){
16148         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16149            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16150            ((this.path == null) ? "" : ("; path=" + this.path)) +
16151            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16152            ((this.secure == true) ? "; secure" : "");
16153     },
16154
16155     // private
16156     clearCookie : function(name){
16157         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16158            ((this.path == null) ? "" : ("; path=" + this.path)) +
16159            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16160            ((this.secure == true) ? "; secure" : "");
16161     }
16162 });/*
16163  * Based on:
16164  * Ext JS Library 1.1.1
16165  * Copyright(c) 2006-2007, Ext JS, LLC.
16166  *
16167  * Originally Released Under LGPL - original licence link has changed is not relivant.
16168  *
16169  * Fork - LGPL
16170  * <script type="text/javascript">
16171  */
16172  
16173
16174 /**
16175  * @class Roo.ComponentMgr
16176  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16177  * @static
16178  */
16179 Roo.ComponentMgr = function(){
16180     var all = new Roo.util.MixedCollection();
16181
16182     return {
16183         /**
16184          * Registers a component.
16185          * @param {Roo.Component} c The component
16186          */
16187         register : function(c){
16188             all.add(c);
16189         },
16190
16191         /**
16192          * Unregisters a component.
16193          * @param {Roo.Component} c The component
16194          */
16195         unregister : function(c){
16196             all.remove(c);
16197         },
16198
16199         /**
16200          * Returns a component by id
16201          * @param {String} id The component id
16202          */
16203         get : function(id){
16204             return all.get(id);
16205         },
16206
16207         /**
16208          * Registers a function that will be called when a specified component is added to ComponentMgr
16209          * @param {String} id The component id
16210          * @param {Funtction} fn The callback function
16211          * @param {Object} scope The scope of the callback
16212          */
16213         onAvailable : function(id, fn, scope){
16214             all.on("add", function(index, o){
16215                 if(o.id == id){
16216                     fn.call(scope || o, o);
16217                     all.un("add", fn, scope);
16218                 }
16219             });
16220         }
16221     };
16222 }();/*
16223  * Based on:
16224  * Ext JS Library 1.1.1
16225  * Copyright(c) 2006-2007, Ext JS, LLC.
16226  *
16227  * Originally Released Under LGPL - original licence link has changed is not relivant.
16228  *
16229  * Fork - LGPL
16230  * <script type="text/javascript">
16231  */
16232  
16233 /**
16234  * @class Roo.Component
16235  * @extends Roo.util.Observable
16236  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16237  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16238  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16239  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16240  * All visual components (widgets) that require rendering into a layout should subclass Component.
16241  * @constructor
16242  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16243  * 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
16244  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16245  */
16246 Roo.Component = function(config){
16247     config = config || {};
16248     if(config.tagName || config.dom || typeof config == "string"){ // element object
16249         config = {el: config, id: config.id || config};
16250     }
16251     this.initialConfig = config;
16252
16253     Roo.apply(this, config);
16254     this.addEvents({
16255         /**
16256          * @event disable
16257          * Fires after the component is disabled.
16258              * @param {Roo.Component} this
16259              */
16260         disable : true,
16261         /**
16262          * @event enable
16263          * Fires after the component is enabled.
16264              * @param {Roo.Component} this
16265              */
16266         enable : true,
16267         /**
16268          * @event beforeshow
16269          * Fires before the component is shown.  Return false to stop the show.
16270              * @param {Roo.Component} this
16271              */
16272         beforeshow : true,
16273         /**
16274          * @event show
16275          * Fires after the component is shown.
16276              * @param {Roo.Component} this
16277              */
16278         show : true,
16279         /**
16280          * @event beforehide
16281          * Fires before the component is hidden. Return false to stop the hide.
16282              * @param {Roo.Component} this
16283              */
16284         beforehide : true,
16285         /**
16286          * @event hide
16287          * Fires after the component is hidden.
16288              * @param {Roo.Component} this
16289              */
16290         hide : true,
16291         /**
16292          * @event beforerender
16293          * Fires before the component is rendered. Return false to stop the render.
16294              * @param {Roo.Component} this
16295              */
16296         beforerender : true,
16297         /**
16298          * @event render
16299          * Fires after the component is rendered.
16300              * @param {Roo.Component} this
16301              */
16302         render : true,
16303         /**
16304          * @event beforedestroy
16305          * Fires before the component is destroyed. Return false to stop the destroy.
16306              * @param {Roo.Component} this
16307              */
16308         beforedestroy : true,
16309         /**
16310          * @event destroy
16311          * Fires after the component is destroyed.
16312              * @param {Roo.Component} this
16313              */
16314         destroy : true
16315     });
16316     if(!this.id){
16317         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16318     }
16319     Roo.ComponentMgr.register(this);
16320     Roo.Component.superclass.constructor.call(this);
16321     this.initComponent();
16322     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16323         this.render(this.renderTo);
16324         delete this.renderTo;
16325     }
16326 };
16327
16328 /** @private */
16329 Roo.Component.AUTO_ID = 1000;
16330
16331 Roo.extend(Roo.Component, Roo.util.Observable, {
16332     /**
16333      * @scope Roo.Component.prototype
16334      * @type {Boolean}
16335      * true if this component is hidden. Read-only.
16336      */
16337     hidden : false,
16338     /**
16339      * @type {Boolean}
16340      * true if this component is disabled. Read-only.
16341      */
16342     disabled : false,
16343     /**
16344      * @type {Boolean}
16345      * true if this component has been rendered. Read-only.
16346      */
16347     rendered : false,
16348     
16349     /** @cfg {String} disableClass
16350      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16351      */
16352     disabledClass : "x-item-disabled",
16353         /** @cfg {Boolean} allowDomMove
16354          * Whether the component can move the Dom node when rendering (defaults to true).
16355          */
16356     allowDomMove : true,
16357     /** @cfg {String} hideMode (display|visibility)
16358      * How this component should hidden. Supported values are
16359      * "visibility" (css visibility), "offsets" (negative offset position) and
16360      * "display" (css display) - defaults to "display".
16361      */
16362     hideMode: 'display',
16363
16364     /** @private */
16365     ctype : "Roo.Component",
16366
16367     /**
16368      * @cfg {String} actionMode 
16369      * which property holds the element that used for  hide() / show() / disable() / enable()
16370      * default is 'el' for forms you probably want to set this to fieldEl 
16371      */
16372     actionMode : "el",
16373
16374     /** @private */
16375     getActionEl : function(){
16376         return this[this.actionMode];
16377     },
16378
16379     initComponent : Roo.emptyFn,
16380     /**
16381      * If this is a lazy rendering component, render it to its container element.
16382      * @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.
16383      */
16384     render : function(container, position){
16385         
16386         if(this.rendered){
16387             return this;
16388         }
16389         
16390         if(this.fireEvent("beforerender", this) === false){
16391             return false;
16392         }
16393         
16394         if(!container && this.el){
16395             this.el = Roo.get(this.el);
16396             container = this.el.dom.parentNode;
16397             this.allowDomMove = false;
16398         }
16399         this.container = Roo.get(container);
16400         this.rendered = true;
16401         if(position !== undefined){
16402             if(typeof position == 'number'){
16403                 position = this.container.dom.childNodes[position];
16404             }else{
16405                 position = Roo.getDom(position);
16406             }
16407         }
16408         this.onRender(this.container, position || null);
16409         if(this.cls){
16410             this.el.addClass(this.cls);
16411             delete this.cls;
16412         }
16413         if(this.style){
16414             this.el.applyStyles(this.style);
16415             delete this.style;
16416         }
16417         this.fireEvent("render", this);
16418         this.afterRender(this.container);
16419         if(this.hidden){
16420             this.hide();
16421         }
16422         if(this.disabled){
16423             this.disable();
16424         }
16425
16426         return this;
16427         
16428     },
16429
16430     /** @private */
16431     // default function is not really useful
16432     onRender : function(ct, position){
16433         if(this.el){
16434             this.el = Roo.get(this.el);
16435             if(this.allowDomMove !== false){
16436                 ct.dom.insertBefore(this.el.dom, position);
16437             }
16438         }
16439     },
16440
16441     /** @private */
16442     getAutoCreate : function(){
16443         var cfg = typeof this.autoCreate == "object" ?
16444                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16445         if(this.id && !cfg.id){
16446             cfg.id = this.id;
16447         }
16448         return cfg;
16449     },
16450
16451     /** @private */
16452     afterRender : Roo.emptyFn,
16453
16454     /**
16455      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16456      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16457      */
16458     destroy : function(){
16459         if(this.fireEvent("beforedestroy", this) !== false){
16460             this.purgeListeners();
16461             this.beforeDestroy();
16462             if(this.rendered){
16463                 this.el.removeAllListeners();
16464                 this.el.remove();
16465                 if(this.actionMode == "container"){
16466                     this.container.remove();
16467                 }
16468             }
16469             this.onDestroy();
16470             Roo.ComponentMgr.unregister(this);
16471             this.fireEvent("destroy", this);
16472         }
16473     },
16474
16475         /** @private */
16476     beforeDestroy : function(){
16477
16478     },
16479
16480         /** @private */
16481         onDestroy : function(){
16482
16483     },
16484
16485     /**
16486      * Returns the underlying {@link Roo.Element}.
16487      * @return {Roo.Element} The element
16488      */
16489     getEl : function(){
16490         return this.el;
16491     },
16492
16493     /**
16494      * Returns the id of this component.
16495      * @return {String}
16496      */
16497     getId : function(){
16498         return this.id;
16499     },
16500
16501     /**
16502      * Try to focus this component.
16503      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16504      * @return {Roo.Component} this
16505      */
16506     focus : function(selectText){
16507         if(this.rendered){
16508             this.el.focus();
16509             if(selectText === true){
16510                 this.el.dom.select();
16511             }
16512         }
16513         return this;
16514     },
16515
16516     /** @private */
16517     blur : function(){
16518         if(this.rendered){
16519             this.el.blur();
16520         }
16521         return this;
16522     },
16523
16524     /**
16525      * Disable this component.
16526      * @return {Roo.Component} this
16527      */
16528     disable : function(){
16529         if(this.rendered){
16530             this.onDisable();
16531         }
16532         this.disabled = true;
16533         this.fireEvent("disable", this);
16534         return this;
16535     },
16536
16537         // private
16538     onDisable : function(){
16539         this.getActionEl().addClass(this.disabledClass);
16540         this.el.dom.disabled = true;
16541     },
16542
16543     /**
16544      * Enable this component.
16545      * @return {Roo.Component} this
16546      */
16547     enable : function(){
16548         if(this.rendered){
16549             this.onEnable();
16550         }
16551         this.disabled = false;
16552         this.fireEvent("enable", this);
16553         return this;
16554     },
16555
16556         // private
16557     onEnable : function(){
16558         this.getActionEl().removeClass(this.disabledClass);
16559         this.el.dom.disabled = false;
16560     },
16561
16562     /**
16563      * Convenience function for setting disabled/enabled by boolean.
16564      * @param {Boolean} disabled
16565      */
16566     setDisabled : function(disabled){
16567         this[disabled ? "disable" : "enable"]();
16568     },
16569
16570     /**
16571      * Show this component.
16572      * @return {Roo.Component} this
16573      */
16574     show: function(){
16575         if(this.fireEvent("beforeshow", this) !== false){
16576             this.hidden = false;
16577             if(this.rendered){
16578                 this.onShow();
16579             }
16580             this.fireEvent("show", this);
16581         }
16582         return this;
16583     },
16584
16585     // private
16586     onShow : function(){
16587         var ae = this.getActionEl();
16588         if(this.hideMode == 'visibility'){
16589             ae.dom.style.visibility = "visible";
16590         }else if(this.hideMode == 'offsets'){
16591             ae.removeClass('x-hidden');
16592         }else{
16593             ae.dom.style.display = "";
16594         }
16595     },
16596
16597     /**
16598      * Hide this component.
16599      * @return {Roo.Component} this
16600      */
16601     hide: function(){
16602         if(this.fireEvent("beforehide", this) !== false){
16603             this.hidden = true;
16604             if(this.rendered){
16605                 this.onHide();
16606             }
16607             this.fireEvent("hide", this);
16608         }
16609         return this;
16610     },
16611
16612     // private
16613     onHide : function(){
16614         var ae = this.getActionEl();
16615         if(this.hideMode == 'visibility'){
16616             ae.dom.style.visibility = "hidden";
16617         }else if(this.hideMode == 'offsets'){
16618             ae.addClass('x-hidden');
16619         }else{
16620             ae.dom.style.display = "none";
16621         }
16622     },
16623
16624     /**
16625      * Convenience function to hide or show this component by boolean.
16626      * @param {Boolean} visible True to show, false to hide
16627      * @return {Roo.Component} this
16628      */
16629     setVisible: function(visible){
16630         if(visible) {
16631             this.show();
16632         }else{
16633             this.hide();
16634         }
16635         return this;
16636     },
16637
16638     /**
16639      * Returns true if this component is visible.
16640      */
16641     isVisible : function(){
16642         return this.getActionEl().isVisible();
16643     },
16644
16645     cloneConfig : function(overrides){
16646         overrides = overrides || {};
16647         var id = overrides.id || Roo.id();
16648         var cfg = Roo.applyIf(overrides, this.initialConfig);
16649         cfg.id = id; // prevent dup id
16650         return new this.constructor(cfg);
16651     }
16652 });/*
16653  * Based on:
16654  * Ext JS Library 1.1.1
16655  * Copyright(c) 2006-2007, Ext JS, LLC.
16656  *
16657  * Originally Released Under LGPL - original licence link has changed is not relivant.
16658  *
16659  * Fork - LGPL
16660  * <script type="text/javascript">
16661  */
16662
16663 /**
16664  * @class Roo.BoxComponent
16665  * @extends Roo.Component
16666  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16667  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16668  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16669  * layout containers.
16670  * @constructor
16671  * @param {Roo.Element/String/Object} config The configuration options.
16672  */
16673 Roo.BoxComponent = function(config){
16674     Roo.Component.call(this, config);
16675     this.addEvents({
16676         /**
16677          * @event resize
16678          * Fires after the component is resized.
16679              * @param {Roo.Component} this
16680              * @param {Number} adjWidth The box-adjusted width that was set
16681              * @param {Number} adjHeight The box-adjusted height that was set
16682              * @param {Number} rawWidth The width that was originally specified
16683              * @param {Number} rawHeight The height that was originally specified
16684              */
16685         resize : true,
16686         /**
16687          * @event move
16688          * Fires after the component is moved.
16689              * @param {Roo.Component} this
16690              * @param {Number} x The new x position
16691              * @param {Number} y The new y position
16692              */
16693         move : true
16694     });
16695 };
16696
16697 Roo.extend(Roo.BoxComponent, Roo.Component, {
16698     // private, set in afterRender to signify that the component has been rendered
16699     boxReady : false,
16700     // private, used to defer height settings to subclasses
16701     deferHeight: false,
16702     /** @cfg {Number} width
16703      * width (optional) size of component
16704      */
16705      /** @cfg {Number} height
16706      * height (optional) size of component
16707      */
16708      
16709     /**
16710      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16711      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16712      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16713      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16714      * @return {Roo.BoxComponent} this
16715      */
16716     setSize : function(w, h){
16717         // support for standard size objects
16718         if(typeof w == 'object'){
16719             h = w.height;
16720             w = w.width;
16721         }
16722         // not rendered
16723         if(!this.boxReady){
16724             this.width = w;
16725             this.height = h;
16726             return this;
16727         }
16728
16729         // prevent recalcs when not needed
16730         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16731             return this;
16732         }
16733         this.lastSize = {width: w, height: h};
16734
16735         var adj = this.adjustSize(w, h);
16736         var aw = adj.width, ah = adj.height;
16737         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16738             var rz = this.getResizeEl();
16739             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16740                 rz.setSize(aw, ah);
16741             }else if(!this.deferHeight && ah !== undefined){
16742                 rz.setHeight(ah);
16743             }else if(aw !== undefined){
16744                 rz.setWidth(aw);
16745             }
16746             this.onResize(aw, ah, w, h);
16747             this.fireEvent('resize', this, aw, ah, w, h);
16748         }
16749         return this;
16750     },
16751
16752     /**
16753      * Gets the current size of the component's underlying element.
16754      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16755      */
16756     getSize : function(){
16757         return this.el.getSize();
16758     },
16759
16760     /**
16761      * Gets the current XY position of the component's underlying element.
16762      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16763      * @return {Array} The XY position of the element (e.g., [100, 200])
16764      */
16765     getPosition : function(local){
16766         if(local === true){
16767             return [this.el.getLeft(true), this.el.getTop(true)];
16768         }
16769         return this.xy || this.el.getXY();
16770     },
16771
16772     /**
16773      * Gets the current box measurements of the component's underlying element.
16774      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16775      * @returns {Object} box An object in the format {x, y, width, height}
16776      */
16777     getBox : function(local){
16778         var s = this.el.getSize();
16779         if(local){
16780             s.x = this.el.getLeft(true);
16781             s.y = this.el.getTop(true);
16782         }else{
16783             var xy = this.xy || this.el.getXY();
16784             s.x = xy[0];
16785             s.y = xy[1];
16786         }
16787         return s;
16788     },
16789
16790     /**
16791      * Sets the current box measurements of the component's underlying element.
16792      * @param {Object} box An object in the format {x, y, width, height}
16793      * @returns {Roo.BoxComponent} this
16794      */
16795     updateBox : function(box){
16796         this.setSize(box.width, box.height);
16797         this.setPagePosition(box.x, box.y);
16798         return this;
16799     },
16800
16801     // protected
16802     getResizeEl : function(){
16803         return this.resizeEl || this.el;
16804     },
16805
16806     // protected
16807     getPositionEl : function(){
16808         return this.positionEl || this.el;
16809     },
16810
16811     /**
16812      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16813      * This method fires the move event.
16814      * @param {Number} left The new left
16815      * @param {Number} top The new top
16816      * @returns {Roo.BoxComponent} this
16817      */
16818     setPosition : function(x, y){
16819         this.x = x;
16820         this.y = y;
16821         if(!this.boxReady){
16822             return this;
16823         }
16824         var adj = this.adjustPosition(x, y);
16825         var ax = adj.x, ay = adj.y;
16826
16827         var el = this.getPositionEl();
16828         if(ax !== undefined || ay !== undefined){
16829             if(ax !== undefined && ay !== undefined){
16830                 el.setLeftTop(ax, ay);
16831             }else if(ax !== undefined){
16832                 el.setLeft(ax);
16833             }else if(ay !== undefined){
16834                 el.setTop(ay);
16835             }
16836             this.onPosition(ax, ay);
16837             this.fireEvent('move', this, ax, ay);
16838         }
16839         return this;
16840     },
16841
16842     /**
16843      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16844      * This method fires the move event.
16845      * @param {Number} x The new x position
16846      * @param {Number} y The new y position
16847      * @returns {Roo.BoxComponent} this
16848      */
16849     setPagePosition : function(x, y){
16850         this.pageX = x;
16851         this.pageY = y;
16852         if(!this.boxReady){
16853             return;
16854         }
16855         if(x === undefined || y === undefined){ // cannot translate undefined points
16856             return;
16857         }
16858         var p = this.el.translatePoints(x, y);
16859         this.setPosition(p.left, p.top);
16860         return this;
16861     },
16862
16863     // private
16864     onRender : function(ct, position){
16865         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16866         if(this.resizeEl){
16867             this.resizeEl = Roo.get(this.resizeEl);
16868         }
16869         if(this.positionEl){
16870             this.positionEl = Roo.get(this.positionEl);
16871         }
16872     },
16873
16874     // private
16875     afterRender : function(){
16876         Roo.BoxComponent.superclass.afterRender.call(this);
16877         this.boxReady = true;
16878         this.setSize(this.width, this.height);
16879         if(this.x || this.y){
16880             this.setPosition(this.x, this.y);
16881         }
16882         if(this.pageX || this.pageY){
16883             this.setPagePosition(this.pageX, this.pageY);
16884         }
16885     },
16886
16887     /**
16888      * Force the component's size to recalculate based on the underlying element's current height and width.
16889      * @returns {Roo.BoxComponent} this
16890      */
16891     syncSize : function(){
16892         delete this.lastSize;
16893         this.setSize(this.el.getWidth(), this.el.getHeight());
16894         return this;
16895     },
16896
16897     /**
16898      * Called after the component is resized, this method is empty by default but can be implemented by any
16899      * subclass that needs to perform custom logic after a resize occurs.
16900      * @param {Number} adjWidth The box-adjusted width that was set
16901      * @param {Number} adjHeight The box-adjusted height that was set
16902      * @param {Number} rawWidth The width that was originally specified
16903      * @param {Number} rawHeight The height that was originally specified
16904      */
16905     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16906
16907     },
16908
16909     /**
16910      * Called after the component is moved, this method is empty by default but can be implemented by any
16911      * subclass that needs to perform custom logic after a move occurs.
16912      * @param {Number} x The new x position
16913      * @param {Number} y The new y position
16914      */
16915     onPosition : function(x, y){
16916
16917     },
16918
16919     // private
16920     adjustSize : function(w, h){
16921         if(this.autoWidth){
16922             w = 'auto';
16923         }
16924         if(this.autoHeight){
16925             h = 'auto';
16926         }
16927         return {width : w, height: h};
16928     },
16929
16930     // private
16931     adjustPosition : function(x, y){
16932         return {x : x, y: y};
16933     }
16934 });/*
16935  * Based on:
16936  * Ext JS Library 1.1.1
16937  * Copyright(c) 2006-2007, Ext JS, LLC.
16938  *
16939  * Originally Released Under LGPL - original licence link has changed is not relivant.
16940  *
16941  * Fork - LGPL
16942  * <script type="text/javascript">
16943  */
16944  (function(){ 
16945 /**
16946  * @class Roo.Layer
16947  * @extends Roo.Element
16948  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16949  * automatic maintaining of shadow/shim positions.
16950  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16951  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16952  * you can pass a string with a CSS class name. False turns off the shadow.
16953  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16954  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16955  * @cfg {String} cls CSS class to add to the element
16956  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16957  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16958  * @constructor
16959  * @param {Object} config An object with config options.
16960  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16961  */
16962
16963 Roo.Layer = function(config, existingEl){
16964     config = config || {};
16965     var dh = Roo.DomHelper;
16966     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16967     if(existingEl){
16968         this.dom = Roo.getDom(existingEl);
16969     }
16970     if(!this.dom){
16971         var o = config.dh || {tag: "div", cls: "x-layer"};
16972         this.dom = dh.append(pel, o);
16973     }
16974     if(config.cls){
16975         this.addClass(config.cls);
16976     }
16977     this.constrain = config.constrain !== false;
16978     this.visibilityMode = Roo.Element.VISIBILITY;
16979     if(config.id){
16980         this.id = this.dom.id = config.id;
16981     }else{
16982         this.id = Roo.id(this.dom);
16983     }
16984     this.zindex = config.zindex || this.getZIndex();
16985     this.position("absolute", this.zindex);
16986     if(config.shadow){
16987         this.shadowOffset = config.shadowOffset || 4;
16988         this.shadow = new Roo.Shadow({
16989             offset : this.shadowOffset,
16990             mode : config.shadow
16991         });
16992     }else{
16993         this.shadowOffset = 0;
16994     }
16995     this.useShim = config.shim !== false && Roo.useShims;
16996     this.useDisplay = config.useDisplay;
16997     this.hide();
16998 };
16999
17000 var supr = Roo.Element.prototype;
17001
17002 // shims are shared among layer to keep from having 100 iframes
17003 var shims = [];
17004
17005 Roo.extend(Roo.Layer, Roo.Element, {
17006
17007     getZIndex : function(){
17008         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17009     },
17010
17011     getShim : function(){
17012         if(!this.useShim){
17013             return null;
17014         }
17015         if(this.shim){
17016             return this.shim;
17017         }
17018         var shim = shims.shift();
17019         if(!shim){
17020             shim = this.createShim();
17021             shim.enableDisplayMode('block');
17022             shim.dom.style.display = 'none';
17023             shim.dom.style.visibility = 'visible';
17024         }
17025         var pn = this.dom.parentNode;
17026         if(shim.dom.parentNode != pn){
17027             pn.insertBefore(shim.dom, this.dom);
17028         }
17029         shim.setStyle('z-index', this.getZIndex()-2);
17030         this.shim = shim;
17031         return shim;
17032     },
17033
17034     hideShim : function(){
17035         if(this.shim){
17036             this.shim.setDisplayed(false);
17037             shims.push(this.shim);
17038             delete this.shim;
17039         }
17040     },
17041
17042     disableShadow : function(){
17043         if(this.shadow){
17044             this.shadowDisabled = true;
17045             this.shadow.hide();
17046             this.lastShadowOffset = this.shadowOffset;
17047             this.shadowOffset = 0;
17048         }
17049     },
17050
17051     enableShadow : function(show){
17052         if(this.shadow){
17053             this.shadowDisabled = false;
17054             this.shadowOffset = this.lastShadowOffset;
17055             delete this.lastShadowOffset;
17056             if(show){
17057                 this.sync(true);
17058             }
17059         }
17060     },
17061
17062     // private
17063     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17064     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17065     sync : function(doShow){
17066         var sw = this.shadow;
17067         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17068             var sh = this.getShim();
17069
17070             var w = this.getWidth(),
17071                 h = this.getHeight();
17072
17073             var l = this.getLeft(true),
17074                 t = this.getTop(true);
17075
17076             if(sw && !this.shadowDisabled){
17077                 if(doShow && !sw.isVisible()){
17078                     sw.show(this);
17079                 }else{
17080                     sw.realign(l, t, w, h);
17081                 }
17082                 if(sh){
17083                     if(doShow){
17084                        sh.show();
17085                     }
17086                     // fit the shim behind the shadow, so it is shimmed too
17087                     var a = sw.adjusts, s = sh.dom.style;
17088                     s.left = (Math.min(l, l+a.l))+"px";
17089                     s.top = (Math.min(t, t+a.t))+"px";
17090                     s.width = (w+a.w)+"px";
17091                     s.height = (h+a.h)+"px";
17092                 }
17093             }else if(sh){
17094                 if(doShow){
17095                    sh.show();
17096                 }
17097                 sh.setSize(w, h);
17098                 sh.setLeftTop(l, t);
17099             }
17100             
17101         }
17102     },
17103
17104     // private
17105     destroy : function(){
17106         this.hideShim();
17107         if(this.shadow){
17108             this.shadow.hide();
17109         }
17110         this.removeAllListeners();
17111         var pn = this.dom.parentNode;
17112         if(pn){
17113             pn.removeChild(this.dom);
17114         }
17115         Roo.Element.uncache(this.id);
17116     },
17117
17118     remove : function(){
17119         this.destroy();
17120     },
17121
17122     // private
17123     beginUpdate : function(){
17124         this.updating = true;
17125     },
17126
17127     // private
17128     endUpdate : function(){
17129         this.updating = false;
17130         this.sync(true);
17131     },
17132
17133     // private
17134     hideUnders : function(negOffset){
17135         if(this.shadow){
17136             this.shadow.hide();
17137         }
17138         this.hideShim();
17139     },
17140
17141     // private
17142     constrainXY : function(){
17143         if(this.constrain){
17144             var vw = Roo.lib.Dom.getViewWidth(),
17145                 vh = Roo.lib.Dom.getViewHeight();
17146             var s = Roo.get(document).getScroll();
17147
17148             var xy = this.getXY();
17149             var x = xy[0], y = xy[1];   
17150             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17151             // only move it if it needs it
17152             var moved = false;
17153             // first validate right/bottom
17154             if((x + w) > vw+s.left){
17155                 x = vw - w - this.shadowOffset;
17156                 moved = true;
17157             }
17158             if((y + h) > vh+s.top){
17159                 y = vh - h - this.shadowOffset;
17160                 moved = true;
17161             }
17162             // then make sure top/left isn't negative
17163             if(x < s.left){
17164                 x = s.left;
17165                 moved = true;
17166             }
17167             if(y < s.top){
17168                 y = s.top;
17169                 moved = true;
17170             }
17171             if(moved){
17172                 if(this.avoidY){
17173                     var ay = this.avoidY;
17174                     if(y <= ay && (y+h) >= ay){
17175                         y = ay-h-5;   
17176                     }
17177                 }
17178                 xy = [x, y];
17179                 this.storeXY(xy);
17180                 supr.setXY.call(this, xy);
17181                 this.sync();
17182             }
17183         }
17184     },
17185
17186     isVisible : function(){
17187         return this.visible;    
17188     },
17189
17190     // private
17191     showAction : function(){
17192         this.visible = true; // track visibility to prevent getStyle calls
17193         if(this.useDisplay === true){
17194             this.setDisplayed("");
17195         }else if(this.lastXY){
17196             supr.setXY.call(this, this.lastXY);
17197         }else if(this.lastLT){
17198             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17199         }
17200     },
17201
17202     // private
17203     hideAction : function(){
17204         this.visible = false;
17205         if(this.useDisplay === true){
17206             this.setDisplayed(false);
17207         }else{
17208             this.setLeftTop(-10000,-10000);
17209         }
17210     },
17211
17212     // overridden Element method
17213     setVisible : function(v, a, d, c, e){
17214         if(v){
17215             this.showAction();
17216         }
17217         if(a && v){
17218             var cb = function(){
17219                 this.sync(true);
17220                 if(c){
17221                     c();
17222                 }
17223             }.createDelegate(this);
17224             supr.setVisible.call(this, true, true, d, cb, e);
17225         }else{
17226             if(!v){
17227                 this.hideUnders(true);
17228             }
17229             var cb = c;
17230             if(a){
17231                 cb = function(){
17232                     this.hideAction();
17233                     if(c){
17234                         c();
17235                     }
17236                 }.createDelegate(this);
17237             }
17238             supr.setVisible.call(this, v, a, d, cb, e);
17239             if(v){
17240                 this.sync(true);
17241             }else if(!a){
17242                 this.hideAction();
17243             }
17244         }
17245     },
17246
17247     storeXY : function(xy){
17248         delete this.lastLT;
17249         this.lastXY = xy;
17250     },
17251
17252     storeLeftTop : function(left, top){
17253         delete this.lastXY;
17254         this.lastLT = [left, top];
17255     },
17256
17257     // private
17258     beforeFx : function(){
17259         this.beforeAction();
17260         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17261     },
17262
17263     // private
17264     afterFx : function(){
17265         Roo.Layer.superclass.afterFx.apply(this, arguments);
17266         this.sync(this.isVisible());
17267     },
17268
17269     // private
17270     beforeAction : function(){
17271         if(!this.updating && this.shadow){
17272             this.shadow.hide();
17273         }
17274     },
17275
17276     // overridden Element method
17277     setLeft : function(left){
17278         this.storeLeftTop(left, this.getTop(true));
17279         supr.setLeft.apply(this, arguments);
17280         this.sync();
17281     },
17282
17283     setTop : function(top){
17284         this.storeLeftTop(this.getLeft(true), top);
17285         supr.setTop.apply(this, arguments);
17286         this.sync();
17287     },
17288
17289     setLeftTop : function(left, top){
17290         this.storeLeftTop(left, top);
17291         supr.setLeftTop.apply(this, arguments);
17292         this.sync();
17293     },
17294
17295     setXY : function(xy, a, d, c, e){
17296         this.fixDisplay();
17297         this.beforeAction();
17298         this.storeXY(xy);
17299         var cb = this.createCB(c);
17300         supr.setXY.call(this, xy, a, d, cb, e);
17301         if(!a){
17302             cb();
17303         }
17304     },
17305
17306     // private
17307     createCB : function(c){
17308         var el = this;
17309         return function(){
17310             el.constrainXY();
17311             el.sync(true);
17312             if(c){
17313                 c();
17314             }
17315         };
17316     },
17317
17318     // overridden Element method
17319     setX : function(x, a, d, c, e){
17320         this.setXY([x, this.getY()], a, d, c, e);
17321     },
17322
17323     // overridden Element method
17324     setY : function(y, a, d, c, e){
17325         this.setXY([this.getX(), y], a, d, c, e);
17326     },
17327
17328     // overridden Element method
17329     setSize : function(w, h, a, d, c, e){
17330         this.beforeAction();
17331         var cb = this.createCB(c);
17332         supr.setSize.call(this, w, h, a, d, cb, e);
17333         if(!a){
17334             cb();
17335         }
17336     },
17337
17338     // overridden Element method
17339     setWidth : function(w, a, d, c, e){
17340         this.beforeAction();
17341         var cb = this.createCB(c);
17342         supr.setWidth.call(this, w, a, d, cb, e);
17343         if(!a){
17344             cb();
17345         }
17346     },
17347
17348     // overridden Element method
17349     setHeight : function(h, a, d, c, e){
17350         this.beforeAction();
17351         var cb = this.createCB(c);
17352         supr.setHeight.call(this, h, a, d, cb, e);
17353         if(!a){
17354             cb();
17355         }
17356     },
17357
17358     // overridden Element method
17359     setBounds : function(x, y, w, h, a, d, c, e){
17360         this.beforeAction();
17361         var cb = this.createCB(c);
17362         if(!a){
17363             this.storeXY([x, y]);
17364             supr.setXY.call(this, [x, y]);
17365             supr.setSize.call(this, w, h, a, d, cb, e);
17366             cb();
17367         }else{
17368             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17369         }
17370         return this;
17371     },
17372     
17373     /**
17374      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17375      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17376      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17377      * @param {Number} zindex The new z-index to set
17378      * @return {this} The Layer
17379      */
17380     setZIndex : function(zindex){
17381         this.zindex = zindex;
17382         this.setStyle("z-index", zindex + 2);
17383         if(this.shadow){
17384             this.shadow.setZIndex(zindex + 1);
17385         }
17386         if(this.shim){
17387             this.shim.setStyle("z-index", zindex);
17388         }
17389     }
17390 });
17391 })();/*
17392  * Original code for Roojs - LGPL
17393  * <script type="text/javascript">
17394  */
17395  
17396 /**
17397  * @class Roo.XComponent
17398  * A delayed Element creator...
17399  * Or a way to group chunks of interface together.
17400  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17401  *  used in conjunction with XComponent.build() it will create an instance of each element,
17402  *  then call addxtype() to build the User interface.
17403  * 
17404  * Mypart.xyx = new Roo.XComponent({
17405
17406     parent : 'Mypart.xyz', // empty == document.element.!!
17407     order : '001',
17408     name : 'xxxx'
17409     region : 'xxxx'
17410     disabled : function() {} 
17411      
17412     tree : function() { // return an tree of xtype declared components
17413         var MODULE = this;
17414         return 
17415         {
17416             xtype : 'NestedLayoutPanel',
17417             // technicall
17418         }
17419      ]
17420  *})
17421  *
17422  *
17423  * It can be used to build a big heiracy, with parent etc.
17424  * or you can just use this to render a single compoent to a dom element
17425  * MYPART.render(Roo.Element | String(id) | dom_element )
17426  *
17427  *
17428  * Usage patterns.
17429  *
17430  * Classic Roo
17431  *
17432  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17433  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17434  *
17435  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17436  *
17437  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17438  * - if mulitple topModules exist, the last one is defined as the top module.
17439  *
17440  * Embeded Roo
17441  * 
17442  * When the top level or multiple modules are to embedded into a existing HTML page,
17443  * the parent element can container '#id' of the element where the module will be drawn.
17444  *
17445  * Bootstrap Roo
17446  *
17447  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17448  * it relies more on a include mechanism, where sub modules are included into an outer page.
17449  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17450  * 
17451  * Bootstrap Roo Included elements
17452  *
17453  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17454  * hence confusing the component builder as it thinks there are multiple top level elements. 
17455  *
17456  * String Over-ride & Translations
17457  *
17458  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17459  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17460  * are needed. @see Roo.XComponent.overlayString  
17461  * 
17462  * 
17463  * 
17464  * @extends Roo.util.Observable
17465  * @constructor
17466  * @param cfg {Object} configuration of component
17467  * 
17468  */
17469 Roo.XComponent = function(cfg) {
17470     Roo.apply(this, cfg);
17471     this.addEvents({ 
17472         /**
17473              * @event built
17474              * Fires when this the componnt is built
17475              * @param {Roo.XComponent} c the component
17476              */
17477         'built' : true
17478         
17479     });
17480     this.region = this.region || 'center'; // default..
17481     Roo.XComponent.register(this);
17482     this.modules = false;
17483     this.el = false; // where the layout goes..
17484     
17485     
17486 }
17487 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17488     /**
17489      * @property el
17490      * The created element (with Roo.factory())
17491      * @type {Roo.Layout}
17492      */
17493     el  : false,
17494     
17495     /**
17496      * @property el
17497      * for BC  - use el in new code
17498      * @type {Roo.Layout}
17499      */
17500     panel : false,
17501     
17502     /**
17503      * @property layout
17504      * for BC  - use el in new code
17505      * @type {Roo.Layout}
17506      */
17507     layout : false,
17508     
17509      /**
17510      * @cfg {Function|boolean} disabled
17511      * If this module is disabled by some rule, return true from the funtion
17512      */
17513     disabled : false,
17514     
17515     /**
17516      * @cfg {String} parent 
17517      * Name of parent element which it get xtype added to..
17518      */
17519     parent: false,
17520     
17521     /**
17522      * @cfg {String} order
17523      * Used to set the order in which elements are created (usefull for multiple tabs)
17524      */
17525     
17526     order : false,
17527     /**
17528      * @cfg {String} name
17529      * String to display while loading.
17530      */
17531     name : false,
17532     /**
17533      * @cfg {String} region
17534      * Region to render component to (defaults to center)
17535      */
17536     region : 'center',
17537     
17538     /**
17539      * @cfg {Array} items
17540      * A single item array - the first element is the root of the tree..
17541      * It's done this way to stay compatible with the Xtype system...
17542      */
17543     items : false,
17544     
17545     /**
17546      * @property _tree
17547      * The method that retuns the tree of parts that make up this compoennt 
17548      * @type {function}
17549      */
17550     _tree  : false,
17551     
17552      /**
17553      * render
17554      * render element to dom or tree
17555      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17556      */
17557     
17558     render : function(el)
17559     {
17560         
17561         el = el || false;
17562         var hp = this.parent ? 1 : 0;
17563         Roo.debug &&  Roo.log(this);
17564         
17565         var tree = this._tree ? this._tree() : this.tree();
17566
17567         
17568         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17569             // if parent is a '#.....' string, then let's use that..
17570             var ename = this.parent.substr(1);
17571             this.parent = false;
17572             Roo.debug && Roo.log(ename);
17573             switch (ename) {
17574                 case 'bootstrap-body':
17575                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17576                         // this is the BorderLayout standard?
17577                        this.parent = { el : true };
17578                        break;
17579                     }
17580                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17581                         // need to insert stuff...
17582                         this.parent =  {
17583                              el : new Roo.bootstrap.layout.Border({
17584                                  el : document.body, 
17585                      
17586                                  center: {
17587                                     titlebar: false,
17588                                     autoScroll:false,
17589                                     closeOnTab: true,
17590                                     tabPosition: 'top',
17591                                       //resizeTabs: true,
17592                                     alwaysShowTabs: true,
17593                                     hideTabs: false
17594                                      //minTabWidth: 140
17595                                  }
17596                              })
17597                         
17598                          };
17599                          break;
17600                     }
17601                          
17602                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17603                         this.parent = { el :  new  Roo.bootstrap.Body() };
17604                         Roo.debug && Roo.log("setting el to doc body");
17605                          
17606                     } else {
17607                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17608                     }
17609                     break;
17610                 case 'bootstrap':
17611                     this.parent = { el : true};
17612                     // fall through
17613                 default:
17614                     el = Roo.get(ename);
17615                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17616                         this.parent = { el : true};
17617                     }
17618                     
17619                     break;
17620             }
17621                 
17622             
17623             if (!el && !this.parent) {
17624                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17625                 return;
17626             }
17627         }
17628         
17629         Roo.debug && Roo.log("EL:");
17630         Roo.debug && Roo.log(el);
17631         Roo.debug && Roo.log("this.parent.el:");
17632         Roo.debug && Roo.log(this.parent.el);
17633         
17634
17635         // altertive root elements ??? - we need a better way to indicate these.
17636         var is_alt = Roo.XComponent.is_alt ||
17637                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17638                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17639                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17640         
17641         
17642         
17643         if (!this.parent && is_alt) {
17644             //el = Roo.get(document.body);
17645             this.parent = { el : true };
17646         }
17647             
17648             
17649         
17650         if (!this.parent) {
17651             
17652             Roo.debug && Roo.log("no parent - creating one");
17653             
17654             el = el ? Roo.get(el) : false;      
17655             
17656             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17657                 
17658                 this.parent =  {
17659                     el : new Roo.bootstrap.layout.Border({
17660                         el: el || document.body,
17661                     
17662                         center: {
17663                             titlebar: false,
17664                             autoScroll:false,
17665                             closeOnTab: true,
17666                             tabPosition: 'top',
17667                              //resizeTabs: true,
17668                             alwaysShowTabs: false,
17669                             hideTabs: true,
17670                             minTabWidth: 140,
17671                             overflow: 'visible'
17672                          }
17673                      })
17674                 };
17675             } else {
17676             
17677                 // it's a top level one..
17678                 this.parent =  {
17679                     el : new Roo.BorderLayout(el || document.body, {
17680                         center: {
17681                             titlebar: false,
17682                             autoScroll:false,
17683                             closeOnTab: true,
17684                             tabPosition: 'top',
17685                              //resizeTabs: true,
17686                             alwaysShowTabs: el && hp? false :  true,
17687                             hideTabs: el || !hp ? true :  false,
17688                             minTabWidth: 140
17689                          }
17690                     })
17691                 };
17692             }
17693         }
17694         
17695         if (!this.parent.el) {
17696                 // probably an old style ctor, which has been disabled.
17697                 return;
17698
17699         }
17700                 // The 'tree' method is  '_tree now' 
17701             
17702         tree.region = tree.region || this.region;
17703         var is_body = false;
17704         if (this.parent.el === true) {
17705             // bootstrap... - body..
17706             if (el) {
17707                 tree.el = el;
17708             }
17709             this.parent.el = Roo.factory(tree);
17710             is_body = true;
17711         }
17712         
17713         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17714         this.fireEvent('built', this);
17715         
17716         this.panel = this.el;
17717         this.layout = this.panel.layout;
17718         this.parentLayout = this.parent.layout  || false;  
17719          
17720     }
17721     
17722 });
17723
17724 Roo.apply(Roo.XComponent, {
17725     /**
17726      * @property  hideProgress
17727      * true to disable the building progress bar.. usefull on single page renders.
17728      * @type Boolean
17729      */
17730     hideProgress : false,
17731     /**
17732      * @property  buildCompleted
17733      * True when the builder has completed building the interface.
17734      * @type Boolean
17735      */
17736     buildCompleted : false,
17737      
17738     /**
17739      * @property  topModule
17740      * the upper most module - uses document.element as it's constructor.
17741      * @type Object
17742      */
17743      
17744     topModule  : false,
17745       
17746     /**
17747      * @property  modules
17748      * array of modules to be created by registration system.
17749      * @type {Array} of Roo.XComponent
17750      */
17751     
17752     modules : [],
17753     /**
17754      * @property  elmodules
17755      * array of modules to be created by which use #ID 
17756      * @type {Array} of Roo.XComponent
17757      */
17758      
17759     elmodules : [],
17760
17761      /**
17762      * @property  is_alt
17763      * Is an alternative Root - normally used by bootstrap or other systems,
17764      *    where the top element in the tree can wrap 'body' 
17765      * @type {boolean}  (default false)
17766      */
17767      
17768     is_alt : false,
17769     /**
17770      * @property  build_from_html
17771      * Build elements from html - used by bootstrap HTML stuff 
17772      *    - this is cleared after build is completed
17773      * @type {boolean}    (default false)
17774      */
17775      
17776     build_from_html : false,
17777     /**
17778      * Register components to be built later.
17779      *
17780      * This solves the following issues
17781      * - Building is not done on page load, but after an authentication process has occured.
17782      * - Interface elements are registered on page load
17783      * - Parent Interface elements may not be loaded before child, so this handles that..
17784      * 
17785      *
17786      * example:
17787      * 
17788      * MyApp.register({
17789           order : '000001',
17790           module : 'Pman.Tab.projectMgr',
17791           region : 'center',
17792           parent : 'Pman.layout',
17793           disabled : false,  // or use a function..
17794         })
17795      
17796      * * @param {Object} details about module
17797      */
17798     register : function(obj) {
17799                 
17800         Roo.XComponent.event.fireEvent('register', obj);
17801         switch(typeof(obj.disabled) ) {
17802                 
17803             case 'undefined':
17804                 break;
17805             
17806             case 'function':
17807                 if ( obj.disabled() ) {
17808                         return;
17809                 }
17810                 break;
17811             
17812             default:
17813                 if (obj.disabled || obj.region == '#disabled') {
17814                         return;
17815                 }
17816                 break;
17817         }
17818                 
17819         this.modules.push(obj);
17820          
17821     },
17822     /**
17823      * convert a string to an object..
17824      * eg. 'AAA.BBB' -> finds AAA.BBB
17825
17826      */
17827     
17828     toObject : function(str)
17829     {
17830         if (!str || typeof(str) == 'object') {
17831             return str;
17832         }
17833         if (str.substring(0,1) == '#') {
17834             return str;
17835         }
17836
17837         var ar = str.split('.');
17838         var rt, o;
17839         rt = ar.shift();
17840             /** eval:var:o */
17841         try {
17842             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17843         } catch (e) {
17844             throw "Module not found : " + str;
17845         }
17846         
17847         if (o === false) {
17848             throw "Module not found : " + str;
17849         }
17850         Roo.each(ar, function(e) {
17851             if (typeof(o[e]) == 'undefined') {
17852                 throw "Module not found : " + str;
17853             }
17854             o = o[e];
17855         });
17856         
17857         return o;
17858         
17859     },
17860     
17861     
17862     /**
17863      * move modules into their correct place in the tree..
17864      * 
17865      */
17866     preBuild : function ()
17867     {
17868         var _t = this;
17869         Roo.each(this.modules , function (obj)
17870         {
17871             Roo.XComponent.event.fireEvent('beforebuild', obj);
17872             
17873             var opar = obj.parent;
17874             try { 
17875                 obj.parent = this.toObject(opar);
17876             } catch(e) {
17877                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17878                 return;
17879             }
17880             
17881             if (!obj.parent) {
17882                 Roo.debug && Roo.log("GOT top level module");
17883                 Roo.debug && Roo.log(obj);
17884                 obj.modules = new Roo.util.MixedCollection(false, 
17885                     function(o) { return o.order + '' }
17886                 );
17887                 this.topModule = obj;
17888                 return;
17889             }
17890                         // parent is a string (usually a dom element name..)
17891             if (typeof(obj.parent) == 'string') {
17892                 this.elmodules.push(obj);
17893                 return;
17894             }
17895             if (obj.parent.constructor != Roo.XComponent) {
17896                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17897             }
17898             if (!obj.parent.modules) {
17899                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17900                     function(o) { return o.order + '' }
17901                 );
17902             }
17903             if (obj.parent.disabled) {
17904                 obj.disabled = true;
17905             }
17906             obj.parent.modules.add(obj);
17907         }, this);
17908     },
17909     
17910      /**
17911      * make a list of modules to build.
17912      * @return {Array} list of modules. 
17913      */ 
17914     
17915     buildOrder : function()
17916     {
17917         var _this = this;
17918         var cmp = function(a,b) {   
17919             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17920         };
17921         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17922             throw "No top level modules to build";
17923         }
17924         
17925         // make a flat list in order of modules to build.
17926         var mods = this.topModule ? [ this.topModule ] : [];
17927                 
17928         
17929         // elmodules (is a list of DOM based modules )
17930         Roo.each(this.elmodules, function(e) {
17931             mods.push(e);
17932             if (!this.topModule &&
17933                 typeof(e.parent) == 'string' &&
17934                 e.parent.substring(0,1) == '#' &&
17935                 Roo.get(e.parent.substr(1))
17936                ) {
17937                 
17938                 _this.topModule = e;
17939             }
17940             
17941         });
17942
17943         
17944         // add modules to their parents..
17945         var addMod = function(m) {
17946             Roo.debug && Roo.log("build Order: add: " + m.name);
17947                 
17948             mods.push(m);
17949             if (m.modules && !m.disabled) {
17950                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17951                 m.modules.keySort('ASC',  cmp );
17952                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17953     
17954                 m.modules.each(addMod);
17955             } else {
17956                 Roo.debug && Roo.log("build Order: no child modules");
17957             }
17958             // not sure if this is used any more..
17959             if (m.finalize) {
17960                 m.finalize.name = m.name + " (clean up) ";
17961                 mods.push(m.finalize);
17962             }
17963             
17964         }
17965         if (this.topModule && this.topModule.modules) { 
17966             this.topModule.modules.keySort('ASC',  cmp );
17967             this.topModule.modules.each(addMod);
17968         } 
17969         return mods;
17970     },
17971     
17972      /**
17973      * Build the registered modules.
17974      * @param {Object} parent element.
17975      * @param {Function} optional method to call after module has been added.
17976      * 
17977      */ 
17978    
17979     build : function(opts) 
17980     {
17981         
17982         if (typeof(opts) != 'undefined') {
17983             Roo.apply(this,opts);
17984         }
17985         
17986         this.preBuild();
17987         var mods = this.buildOrder();
17988       
17989         //this.allmods = mods;
17990         //Roo.debug && Roo.log(mods);
17991         //return;
17992         if (!mods.length) { // should not happen
17993             throw "NO modules!!!";
17994         }
17995         
17996         
17997         var msg = "Building Interface...";
17998         // flash it up as modal - so we store the mask!?
17999         if (!this.hideProgress && Roo.MessageBox) {
18000             Roo.MessageBox.show({ title: 'loading' });
18001             Roo.MessageBox.show({
18002                title: "Please wait...",
18003                msg: msg,
18004                width:450,
18005                progress:true,
18006                buttons : false,
18007                closable:false,
18008                modal: false
18009               
18010             });
18011         }
18012         var total = mods.length;
18013         
18014         var _this = this;
18015         var progressRun = function() {
18016             if (!mods.length) {
18017                 Roo.debug && Roo.log('hide?');
18018                 if (!this.hideProgress && Roo.MessageBox) {
18019                     Roo.MessageBox.hide();
18020                 }
18021                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18022                 
18023                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18024                 
18025                 // THE END...
18026                 return false;   
18027             }
18028             
18029             var m = mods.shift();
18030             
18031             
18032             Roo.debug && Roo.log(m);
18033             // not sure if this is supported any more.. - modules that are are just function
18034             if (typeof(m) == 'function') { 
18035                 m.call(this);
18036                 return progressRun.defer(10, _this);
18037             } 
18038             
18039             
18040             msg = "Building Interface " + (total  - mods.length) + 
18041                     " of " + total + 
18042                     (m.name ? (' - ' + m.name) : '');
18043                         Roo.debug && Roo.log(msg);
18044             if (!_this.hideProgress &&  Roo.MessageBox) { 
18045                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18046             }
18047             
18048          
18049             // is the module disabled?
18050             var disabled = (typeof(m.disabled) == 'function') ?
18051                 m.disabled.call(m.module.disabled) : m.disabled;    
18052             
18053             
18054             if (disabled) {
18055                 return progressRun(); // we do not update the display!
18056             }
18057             
18058             // now build 
18059             
18060                         
18061                         
18062             m.render();
18063             // it's 10 on top level, and 1 on others??? why...
18064             return progressRun.defer(10, _this);
18065              
18066         }
18067         progressRun.defer(1, _this);
18068      
18069         
18070         
18071     },
18072     /**
18073      * Overlay a set of modified strings onto a component
18074      * This is dependant on our builder exporting the strings and 'named strings' elements.
18075      * 
18076      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18077      * @param {Object} associative array of 'named' string and it's new value.
18078      * 
18079      */
18080         overlayStrings : function( component, strings )
18081     {
18082         if (typeof(component['_named_strings']) == 'undefined') {
18083             throw "ERROR: component does not have _named_strings";
18084         }
18085         for ( var k in strings ) {
18086             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18087             if (md !== false) {
18088                 component['_strings'][md] = strings[k];
18089             } else {
18090                 Roo.log('could not find named string: ' + k + ' in');
18091                 Roo.log(component);
18092             }
18093             
18094         }
18095         
18096     },
18097     
18098         
18099         /**
18100          * Event Object.
18101          *
18102          *
18103          */
18104         event: false, 
18105     /**
18106          * wrapper for event.on - aliased later..  
18107          * Typically use to register a event handler for register:
18108          *
18109          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18110          *
18111          */
18112     on : false
18113    
18114     
18115     
18116 });
18117
18118 Roo.XComponent.event = new Roo.util.Observable({
18119                 events : { 
18120                         /**
18121                          * @event register
18122                          * Fires when an Component is registered,
18123                          * set the disable property on the Component to stop registration.
18124                          * @param {Roo.XComponent} c the component being registerd.
18125                          * 
18126                          */
18127                         'register' : true,
18128             /**
18129                          * @event beforebuild
18130                          * Fires before each Component is built
18131                          * can be used to apply permissions.
18132                          * @param {Roo.XComponent} c the component being registerd.
18133                          * 
18134                          */
18135                         'beforebuild' : true,
18136                         /**
18137                          * @event buildcomplete
18138                          * Fires on the top level element when all elements have been built
18139                          * @param {Roo.XComponent} the top level component.
18140                          */
18141                         'buildcomplete' : true
18142                         
18143                 }
18144 });
18145
18146 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18147  //
18148  /**
18149  * marked - a markdown parser
18150  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18151  * https://github.com/chjj/marked
18152  */
18153
18154
18155 /**
18156  *
18157  * Roo.Markdown - is a very crude wrapper around marked..
18158  *
18159  * usage:
18160  * 
18161  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18162  * 
18163  * Note: move the sample code to the bottom of this
18164  * file before uncommenting it.
18165  *
18166  */
18167
18168 Roo.Markdown = {};
18169 Roo.Markdown.toHtml = function(text) {
18170     
18171     var c = new Roo.Markdown.marked.setOptions({
18172             renderer: new Roo.Markdown.marked.Renderer(),
18173             gfm: true,
18174             tables: true,
18175             breaks: false,
18176             pedantic: false,
18177             sanitize: false,
18178             smartLists: true,
18179             smartypants: false
18180           });
18181     // A FEW HACKS!!?
18182     
18183     text = text.replace(/\\\n/g,' ');
18184     return Roo.Markdown.marked(text);
18185 };
18186 //
18187 // converter
18188 //
18189 // Wraps all "globals" so that the only thing
18190 // exposed is makeHtml().
18191 //
18192 (function() {
18193     
18194      /**
18195          * eval:var:escape
18196          * eval:var:unescape
18197          * eval:var:replace
18198          */
18199       
18200     /**
18201      * Helpers
18202      */
18203     
18204     var escape = function (html, encode) {
18205       return html
18206         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18207         .replace(/</g, '&lt;')
18208         .replace(/>/g, '&gt;')
18209         .replace(/"/g, '&quot;')
18210         .replace(/'/g, '&#39;');
18211     }
18212     
18213     var unescape = function (html) {
18214         // explicitly match decimal, hex, and named HTML entities 
18215       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18216         n = n.toLowerCase();
18217         if (n === 'colon') { return ':'; }
18218         if (n.charAt(0) === '#') {
18219           return n.charAt(1) === 'x'
18220             ? String.fromCharCode(parseInt(n.substring(2), 16))
18221             : String.fromCharCode(+n.substring(1));
18222         }
18223         return '';
18224       });
18225     }
18226     
18227     var replace = function (regex, opt) {
18228       regex = regex.source;
18229       opt = opt || '';
18230       return function self(name, val) {
18231         if (!name) { return new RegExp(regex, opt); }
18232         val = val.source || val;
18233         val = val.replace(/(^|[^\[])\^/g, '$1');
18234         regex = regex.replace(name, val);
18235         return self;
18236       };
18237     }
18238
18239
18240          /**
18241          * eval:var:noop
18242     */
18243     var noop = function () {}
18244     noop.exec = noop;
18245     
18246          /**
18247          * eval:var:merge
18248     */
18249     var merge = function (obj) {
18250       var i = 1
18251         , target
18252         , key;
18253     
18254       for (; i < arguments.length; i++) {
18255         target = arguments[i];
18256         for (key in target) {
18257           if (Object.prototype.hasOwnProperty.call(target, key)) {
18258             obj[key] = target[key];
18259           }
18260         }
18261       }
18262     
18263       return obj;
18264     }
18265     
18266     
18267     /**
18268      * Block-Level Grammar
18269      */
18270     
18271     
18272     
18273     
18274     var block = {
18275       newline: /^\n+/,
18276       code: /^( {4}[^\n]+\n*)+/,
18277       fences: noop,
18278       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18279       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18280       nptable: noop,
18281       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18282       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18283       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18284       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18285       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18286       table: noop,
18287       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18288       text: /^[^\n]+/
18289     };
18290     
18291     block.bullet = /(?:[*+-]|\d+\.)/;
18292     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18293     block.item = replace(block.item, 'gm')
18294       (/bull/g, block.bullet)
18295       ();
18296     
18297     block.list = replace(block.list)
18298       (/bull/g, block.bullet)
18299       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18300       ('def', '\\n+(?=' + block.def.source + ')')
18301       ();
18302     
18303     block.blockquote = replace(block.blockquote)
18304       ('def', block.def)
18305       ();
18306     
18307     block._tag = '(?!(?:'
18308       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18309       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18310       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18311     
18312     block.html = replace(block.html)
18313       ('comment', /<!--[\s\S]*?-->/)
18314       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18315       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18316       (/tag/g, block._tag)
18317       ();
18318     
18319     block.paragraph = replace(block.paragraph)
18320       ('hr', block.hr)
18321       ('heading', block.heading)
18322       ('lheading', block.lheading)
18323       ('blockquote', block.blockquote)
18324       ('tag', '<' + block._tag)
18325       ('def', block.def)
18326       ();
18327     
18328     /**
18329      * Normal Block Grammar
18330      */
18331     
18332     block.normal = merge({}, block);
18333     
18334     /**
18335      * GFM Block Grammar
18336      */
18337     
18338     block.gfm = merge({}, block.normal, {
18339       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18340       paragraph: /^/,
18341       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18342     });
18343     
18344     block.gfm.paragraph = replace(block.paragraph)
18345       ('(?!', '(?!'
18346         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18347         + block.list.source.replace('\\1', '\\3') + '|')
18348       ();
18349     
18350     /**
18351      * GFM + Tables Block Grammar
18352      */
18353     
18354     block.tables = merge({}, block.gfm, {
18355       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18356       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18357     });
18358     
18359     /**
18360      * Block Lexer
18361      */
18362     
18363     var Lexer = function (options) {
18364       this.tokens = [];
18365       this.tokens.links = {};
18366       this.options = options || marked.defaults;
18367       this.rules = block.normal;
18368     
18369       if (this.options.gfm) {
18370         if (this.options.tables) {
18371           this.rules = block.tables;
18372         } else {
18373           this.rules = block.gfm;
18374         }
18375       }
18376     }
18377     
18378     /**
18379      * Expose Block Rules
18380      */
18381     
18382     Lexer.rules = block;
18383     
18384     /**
18385      * Static Lex Method
18386      */
18387     
18388     Lexer.lex = function(src, options) {
18389       var lexer = new Lexer(options);
18390       return lexer.lex(src);
18391     };
18392     
18393     /**
18394      * Preprocessing
18395      */
18396     
18397     Lexer.prototype.lex = function(src) {
18398       src = src
18399         .replace(/\r\n|\r/g, '\n')
18400         .replace(/\t/g, '    ')
18401         .replace(/\u00a0/g, ' ')
18402         .replace(/\u2424/g, '\n');
18403     
18404       return this.token(src, true);
18405     };
18406     
18407     /**
18408      * Lexing
18409      */
18410     
18411     Lexer.prototype.token = function(src, top, bq) {
18412       var src = src.replace(/^ +$/gm, '')
18413         , next
18414         , loose
18415         , cap
18416         , bull
18417         , b
18418         , item
18419         , space
18420         , i
18421         , l;
18422     
18423       while (src) {
18424         // newline
18425         if (cap = this.rules.newline.exec(src)) {
18426           src = src.substring(cap[0].length);
18427           if (cap[0].length > 1) {
18428             this.tokens.push({
18429               type: 'space'
18430             });
18431           }
18432         }
18433     
18434         // code
18435         if (cap = this.rules.code.exec(src)) {
18436           src = src.substring(cap[0].length);
18437           cap = cap[0].replace(/^ {4}/gm, '');
18438           this.tokens.push({
18439             type: 'code',
18440             text: !this.options.pedantic
18441               ? cap.replace(/\n+$/, '')
18442               : cap
18443           });
18444           continue;
18445         }
18446     
18447         // fences (gfm)
18448         if (cap = this.rules.fences.exec(src)) {
18449           src = src.substring(cap[0].length);
18450           this.tokens.push({
18451             type: 'code',
18452             lang: cap[2],
18453             text: cap[3] || ''
18454           });
18455           continue;
18456         }
18457     
18458         // heading
18459         if (cap = this.rules.heading.exec(src)) {
18460           src = src.substring(cap[0].length);
18461           this.tokens.push({
18462             type: 'heading',
18463             depth: cap[1].length,
18464             text: cap[2]
18465           });
18466           continue;
18467         }
18468     
18469         // table no leading pipe (gfm)
18470         if (top && (cap = this.rules.nptable.exec(src))) {
18471           src = src.substring(cap[0].length);
18472     
18473           item = {
18474             type: 'table',
18475             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18476             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18477             cells: cap[3].replace(/\n$/, '').split('\n')
18478           };
18479     
18480           for (i = 0; i < item.align.length; i++) {
18481             if (/^ *-+: *$/.test(item.align[i])) {
18482               item.align[i] = 'right';
18483             } else if (/^ *:-+: *$/.test(item.align[i])) {
18484               item.align[i] = 'center';
18485             } else if (/^ *:-+ *$/.test(item.align[i])) {
18486               item.align[i] = 'left';
18487             } else {
18488               item.align[i] = null;
18489             }
18490           }
18491     
18492           for (i = 0; i < item.cells.length; i++) {
18493             item.cells[i] = item.cells[i].split(/ *\| */);
18494           }
18495     
18496           this.tokens.push(item);
18497     
18498           continue;
18499         }
18500     
18501         // lheading
18502         if (cap = this.rules.lheading.exec(src)) {
18503           src = src.substring(cap[0].length);
18504           this.tokens.push({
18505             type: 'heading',
18506             depth: cap[2] === '=' ? 1 : 2,
18507             text: cap[1]
18508           });
18509           continue;
18510         }
18511     
18512         // hr
18513         if (cap = this.rules.hr.exec(src)) {
18514           src = src.substring(cap[0].length);
18515           this.tokens.push({
18516             type: 'hr'
18517           });
18518           continue;
18519         }
18520     
18521         // blockquote
18522         if (cap = this.rules.blockquote.exec(src)) {
18523           src = src.substring(cap[0].length);
18524     
18525           this.tokens.push({
18526             type: 'blockquote_start'
18527           });
18528     
18529           cap = cap[0].replace(/^ *> ?/gm, '');
18530     
18531           // Pass `top` to keep the current
18532           // "toplevel" state. This is exactly
18533           // how markdown.pl works.
18534           this.token(cap, top, true);
18535     
18536           this.tokens.push({
18537             type: 'blockquote_end'
18538           });
18539     
18540           continue;
18541         }
18542     
18543         // list
18544         if (cap = this.rules.list.exec(src)) {
18545           src = src.substring(cap[0].length);
18546           bull = cap[2];
18547     
18548           this.tokens.push({
18549             type: 'list_start',
18550             ordered: bull.length > 1
18551           });
18552     
18553           // Get each top-level item.
18554           cap = cap[0].match(this.rules.item);
18555     
18556           next = false;
18557           l = cap.length;
18558           i = 0;
18559     
18560           for (; i < l; i++) {
18561             item = cap[i];
18562     
18563             // Remove the list item's bullet
18564             // so it is seen as the next token.
18565             space = item.length;
18566             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18567     
18568             // Outdent whatever the
18569             // list item contains. Hacky.
18570             if (~item.indexOf('\n ')) {
18571               space -= item.length;
18572               item = !this.options.pedantic
18573                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18574                 : item.replace(/^ {1,4}/gm, '');
18575             }
18576     
18577             // Determine whether the next list item belongs here.
18578             // Backpedal if it does not belong in this list.
18579             if (this.options.smartLists && i !== l - 1) {
18580               b = block.bullet.exec(cap[i + 1])[0];
18581               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18582                 src = cap.slice(i + 1).join('\n') + src;
18583                 i = l - 1;
18584               }
18585             }
18586     
18587             // Determine whether item is loose or not.
18588             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18589             // for discount behavior.
18590             loose = next || /\n\n(?!\s*$)/.test(item);
18591             if (i !== l - 1) {
18592               next = item.charAt(item.length - 1) === '\n';
18593               if (!loose) { loose = next; }
18594             }
18595     
18596             this.tokens.push({
18597               type: loose
18598                 ? 'loose_item_start'
18599                 : 'list_item_start'
18600             });
18601     
18602             // Recurse.
18603             this.token(item, false, bq);
18604     
18605             this.tokens.push({
18606               type: 'list_item_end'
18607             });
18608           }
18609     
18610           this.tokens.push({
18611             type: 'list_end'
18612           });
18613     
18614           continue;
18615         }
18616     
18617         // html
18618         if (cap = this.rules.html.exec(src)) {
18619           src = src.substring(cap[0].length);
18620           this.tokens.push({
18621             type: this.options.sanitize
18622               ? 'paragraph'
18623               : 'html',
18624             pre: !this.options.sanitizer
18625               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18626             text: cap[0]
18627           });
18628           continue;
18629         }
18630     
18631         // def
18632         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18633           src = src.substring(cap[0].length);
18634           this.tokens.links[cap[1].toLowerCase()] = {
18635             href: cap[2],
18636             title: cap[3]
18637           };
18638           continue;
18639         }
18640     
18641         // table (gfm)
18642         if (top && (cap = this.rules.table.exec(src))) {
18643           src = src.substring(cap[0].length);
18644     
18645           item = {
18646             type: 'table',
18647             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18648             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18649             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18650           };
18651     
18652           for (i = 0; i < item.align.length; i++) {
18653             if (/^ *-+: *$/.test(item.align[i])) {
18654               item.align[i] = 'right';
18655             } else if (/^ *:-+: *$/.test(item.align[i])) {
18656               item.align[i] = 'center';
18657             } else if (/^ *:-+ *$/.test(item.align[i])) {
18658               item.align[i] = 'left';
18659             } else {
18660               item.align[i] = null;
18661             }
18662           }
18663     
18664           for (i = 0; i < item.cells.length; i++) {
18665             item.cells[i] = item.cells[i]
18666               .replace(/^ *\| *| *\| *$/g, '')
18667               .split(/ *\| */);
18668           }
18669     
18670           this.tokens.push(item);
18671     
18672           continue;
18673         }
18674     
18675         // top-level paragraph
18676         if (top && (cap = this.rules.paragraph.exec(src))) {
18677           src = src.substring(cap[0].length);
18678           this.tokens.push({
18679             type: 'paragraph',
18680             text: cap[1].charAt(cap[1].length - 1) === '\n'
18681               ? cap[1].slice(0, -1)
18682               : cap[1]
18683           });
18684           continue;
18685         }
18686     
18687         // text
18688         if (cap = this.rules.text.exec(src)) {
18689           // Top-level should never reach here.
18690           src = src.substring(cap[0].length);
18691           this.tokens.push({
18692             type: 'text',
18693             text: cap[0]
18694           });
18695           continue;
18696         }
18697     
18698         if (src) {
18699           throw new
18700             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18701         }
18702       }
18703     
18704       return this.tokens;
18705     };
18706     
18707     /**
18708      * Inline-Level Grammar
18709      */
18710     
18711     var inline = {
18712       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18713       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18714       url: noop,
18715       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18716       link: /^!?\[(inside)\]\(href\)/,
18717       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18718       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18719       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18720       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18721       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18722       br: /^ {2,}\n(?!\s*$)/,
18723       del: noop,
18724       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18725     };
18726     
18727     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18728     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18729     
18730     inline.link = replace(inline.link)
18731       ('inside', inline._inside)
18732       ('href', inline._href)
18733       ();
18734     
18735     inline.reflink = replace(inline.reflink)
18736       ('inside', inline._inside)
18737       ();
18738     
18739     /**
18740      * Normal Inline Grammar
18741      */
18742     
18743     inline.normal = merge({}, inline);
18744     
18745     /**
18746      * Pedantic Inline Grammar
18747      */
18748     
18749     inline.pedantic = merge({}, inline.normal, {
18750       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18751       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18752     });
18753     
18754     /**
18755      * GFM Inline Grammar
18756      */
18757     
18758     inline.gfm = merge({}, inline.normal, {
18759       escape: replace(inline.escape)('])', '~|])')(),
18760       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18761       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18762       text: replace(inline.text)
18763         (']|', '~]|')
18764         ('|', '|https?://|')
18765         ()
18766     });
18767     
18768     /**
18769      * GFM + Line Breaks Inline Grammar
18770      */
18771     
18772     inline.breaks = merge({}, inline.gfm, {
18773       br: replace(inline.br)('{2,}', '*')(),
18774       text: replace(inline.gfm.text)('{2,}', '*')()
18775     });
18776     
18777     /**
18778      * Inline Lexer & Compiler
18779      */
18780     
18781     var InlineLexer  = function (links, options) {
18782       this.options = options || marked.defaults;
18783       this.links = links;
18784       this.rules = inline.normal;
18785       this.renderer = this.options.renderer || new Renderer;
18786       this.renderer.options = this.options;
18787     
18788       if (!this.links) {
18789         throw new
18790           Error('Tokens array requires a `links` property.');
18791       }
18792     
18793       if (this.options.gfm) {
18794         if (this.options.breaks) {
18795           this.rules = inline.breaks;
18796         } else {
18797           this.rules = inline.gfm;
18798         }
18799       } else if (this.options.pedantic) {
18800         this.rules = inline.pedantic;
18801       }
18802     }
18803     
18804     /**
18805      * Expose Inline Rules
18806      */
18807     
18808     InlineLexer.rules = inline;
18809     
18810     /**
18811      * Static Lexing/Compiling Method
18812      */
18813     
18814     InlineLexer.output = function(src, links, options) {
18815       var inline = new InlineLexer(links, options);
18816       return inline.output(src);
18817     };
18818     
18819     /**
18820      * Lexing/Compiling
18821      */
18822     
18823     InlineLexer.prototype.output = function(src) {
18824       var out = ''
18825         , link
18826         , text
18827         , href
18828         , cap;
18829     
18830       while (src) {
18831         // escape
18832         if (cap = this.rules.escape.exec(src)) {
18833           src = src.substring(cap[0].length);
18834           out += cap[1];
18835           continue;
18836         }
18837     
18838         // autolink
18839         if (cap = this.rules.autolink.exec(src)) {
18840           src = src.substring(cap[0].length);
18841           if (cap[2] === '@') {
18842             text = cap[1].charAt(6) === ':'
18843               ? this.mangle(cap[1].substring(7))
18844               : this.mangle(cap[1]);
18845             href = this.mangle('mailto:') + text;
18846           } else {
18847             text = escape(cap[1]);
18848             href = text;
18849           }
18850           out += this.renderer.link(href, null, text);
18851           continue;
18852         }
18853     
18854         // url (gfm)
18855         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18856           src = src.substring(cap[0].length);
18857           text = escape(cap[1]);
18858           href = text;
18859           out += this.renderer.link(href, null, text);
18860           continue;
18861         }
18862     
18863         // tag
18864         if (cap = this.rules.tag.exec(src)) {
18865           if (!this.inLink && /^<a /i.test(cap[0])) {
18866             this.inLink = true;
18867           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18868             this.inLink = false;
18869           }
18870           src = src.substring(cap[0].length);
18871           out += this.options.sanitize
18872             ? this.options.sanitizer
18873               ? this.options.sanitizer(cap[0])
18874               : escape(cap[0])
18875             : cap[0];
18876           continue;
18877         }
18878     
18879         // link
18880         if (cap = this.rules.link.exec(src)) {
18881           src = src.substring(cap[0].length);
18882           this.inLink = true;
18883           out += this.outputLink(cap, {
18884             href: cap[2],
18885             title: cap[3]
18886           });
18887           this.inLink = false;
18888           continue;
18889         }
18890     
18891         // reflink, nolink
18892         if ((cap = this.rules.reflink.exec(src))
18893             || (cap = this.rules.nolink.exec(src))) {
18894           src = src.substring(cap[0].length);
18895           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18896           link = this.links[link.toLowerCase()];
18897           if (!link || !link.href) {
18898             out += cap[0].charAt(0);
18899             src = cap[0].substring(1) + src;
18900             continue;
18901           }
18902           this.inLink = true;
18903           out += this.outputLink(cap, link);
18904           this.inLink = false;
18905           continue;
18906         }
18907     
18908         // strong
18909         if (cap = this.rules.strong.exec(src)) {
18910           src = src.substring(cap[0].length);
18911           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18912           continue;
18913         }
18914     
18915         // em
18916         if (cap = this.rules.em.exec(src)) {
18917           src = src.substring(cap[0].length);
18918           out += this.renderer.em(this.output(cap[2] || cap[1]));
18919           continue;
18920         }
18921     
18922         // code
18923         if (cap = this.rules.code.exec(src)) {
18924           src = src.substring(cap[0].length);
18925           out += this.renderer.codespan(escape(cap[2], true));
18926           continue;
18927         }
18928     
18929         // br
18930         if (cap = this.rules.br.exec(src)) {
18931           src = src.substring(cap[0].length);
18932           out += this.renderer.br();
18933           continue;
18934         }
18935     
18936         // del (gfm)
18937         if (cap = this.rules.del.exec(src)) {
18938           src = src.substring(cap[0].length);
18939           out += this.renderer.del(this.output(cap[1]));
18940           continue;
18941         }
18942     
18943         // text
18944         if (cap = this.rules.text.exec(src)) {
18945           src = src.substring(cap[0].length);
18946           out += this.renderer.text(escape(this.smartypants(cap[0])));
18947           continue;
18948         }
18949     
18950         if (src) {
18951           throw new
18952             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18953         }
18954       }
18955     
18956       return out;
18957     };
18958     
18959     /**
18960      * Compile Link
18961      */
18962     
18963     InlineLexer.prototype.outputLink = function(cap, link) {
18964       var href = escape(link.href)
18965         , title = link.title ? escape(link.title) : null;
18966     
18967       return cap[0].charAt(0) !== '!'
18968         ? this.renderer.link(href, title, this.output(cap[1]))
18969         : this.renderer.image(href, title, escape(cap[1]));
18970     };
18971     
18972     /**
18973      * Smartypants Transformations
18974      */
18975     
18976     InlineLexer.prototype.smartypants = function(text) {
18977       if (!this.options.smartypants)  { return text; }
18978       return text
18979         // em-dashes
18980         .replace(/---/g, '\u2014')
18981         // en-dashes
18982         .replace(/--/g, '\u2013')
18983         // opening singles
18984         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18985         // closing singles & apostrophes
18986         .replace(/'/g, '\u2019')
18987         // opening doubles
18988         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18989         // closing doubles
18990         .replace(/"/g, '\u201d')
18991         // ellipses
18992         .replace(/\.{3}/g, '\u2026');
18993     };
18994     
18995     /**
18996      * Mangle Links
18997      */
18998     
18999     InlineLexer.prototype.mangle = function(text) {
19000       if (!this.options.mangle) { return text; }
19001       var out = ''
19002         , l = text.length
19003         , i = 0
19004         , ch;
19005     
19006       for (; i < l; i++) {
19007         ch = text.charCodeAt(i);
19008         if (Math.random() > 0.5) {
19009           ch = 'x' + ch.toString(16);
19010         }
19011         out += '&#' + ch + ';';
19012       }
19013     
19014       return out;
19015     };
19016     
19017     /**
19018      * Renderer
19019      */
19020     
19021      /**
19022          * eval:var:Renderer
19023     */
19024     
19025     var Renderer   = function (options) {
19026       this.options = options || {};
19027     }
19028     
19029     Renderer.prototype.code = function(code, lang, escaped) {
19030       if (this.options.highlight) {
19031         var out = this.options.highlight(code, lang);
19032         if (out != null && out !== code) {
19033           escaped = true;
19034           code = out;
19035         }
19036       } else {
19037             // hack!!! - it's already escapeD?
19038             escaped = true;
19039       }
19040     
19041       if (!lang) {
19042         return '<pre><code>'
19043           + (escaped ? code : escape(code, true))
19044           + '\n</code></pre>';
19045       }
19046     
19047       return '<pre><code class="'
19048         + this.options.langPrefix
19049         + escape(lang, true)
19050         + '">'
19051         + (escaped ? code : escape(code, true))
19052         + '\n</code></pre>\n';
19053     };
19054     
19055     Renderer.prototype.blockquote = function(quote) {
19056       return '<blockquote>\n' + quote + '</blockquote>\n';
19057     };
19058     
19059     Renderer.prototype.html = function(html) {
19060       return html;
19061     };
19062     
19063     Renderer.prototype.heading = function(text, level, raw) {
19064       return '<h'
19065         + level
19066         + ' id="'
19067         + this.options.headerPrefix
19068         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19069         + '">'
19070         + text
19071         + '</h'
19072         + level
19073         + '>\n';
19074     };
19075     
19076     Renderer.prototype.hr = function() {
19077       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19078     };
19079     
19080     Renderer.prototype.list = function(body, ordered) {
19081       var type = ordered ? 'ol' : 'ul';
19082       return '<' + type + '>\n' + body + '</' + type + '>\n';
19083     };
19084     
19085     Renderer.prototype.listitem = function(text) {
19086       return '<li>' + text + '</li>\n';
19087     };
19088     
19089     Renderer.prototype.paragraph = function(text) {
19090       return '<p>' + text + '</p>\n';
19091     };
19092     
19093     Renderer.prototype.table = function(header, body) {
19094       return '<table class="table table-striped">\n'
19095         + '<thead>\n'
19096         + header
19097         + '</thead>\n'
19098         + '<tbody>\n'
19099         + body
19100         + '</tbody>\n'
19101         + '</table>\n';
19102     };
19103     
19104     Renderer.prototype.tablerow = function(content) {
19105       return '<tr>\n' + content + '</tr>\n';
19106     };
19107     
19108     Renderer.prototype.tablecell = function(content, flags) {
19109       var type = flags.header ? 'th' : 'td';
19110       var tag = flags.align
19111         ? '<' + type + ' style="text-align:' + flags.align + '">'
19112         : '<' + type + '>';
19113       return tag + content + '</' + type + '>\n';
19114     };
19115     
19116     // span level renderer
19117     Renderer.prototype.strong = function(text) {
19118       return '<strong>' + text + '</strong>';
19119     };
19120     
19121     Renderer.prototype.em = function(text) {
19122       return '<em>' + text + '</em>';
19123     };
19124     
19125     Renderer.prototype.codespan = function(text) {
19126       return '<code>' + text + '</code>';
19127     };
19128     
19129     Renderer.prototype.br = function() {
19130       return this.options.xhtml ? '<br/>' : '<br>';
19131     };
19132     
19133     Renderer.prototype.del = function(text) {
19134       return '<del>' + text + '</del>';
19135     };
19136     
19137     Renderer.prototype.link = function(href, title, text) {
19138       if (this.options.sanitize) {
19139         try {
19140           var prot = decodeURIComponent(unescape(href))
19141             .replace(/[^\w:]/g, '')
19142             .toLowerCase();
19143         } catch (e) {
19144           return '';
19145         }
19146         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19147           return '';
19148         }
19149       }
19150       var out = '<a href="' + href + '"';
19151       if (title) {
19152         out += ' title="' + title + '"';
19153       }
19154       out += '>' + text + '</a>';
19155       return out;
19156     };
19157     
19158     Renderer.prototype.image = function(href, title, text) {
19159       var out = '<img src="' + href + '" alt="' + text + '"';
19160       if (title) {
19161         out += ' title="' + title + '"';
19162       }
19163       out += this.options.xhtml ? '/>' : '>';
19164       return out;
19165     };
19166     
19167     Renderer.prototype.text = function(text) {
19168       return text;
19169     };
19170     
19171     /**
19172      * Parsing & Compiling
19173      */
19174          /**
19175          * eval:var:Parser
19176     */
19177     
19178     var Parser= function (options) {
19179       this.tokens = [];
19180       this.token = null;
19181       this.options = options || marked.defaults;
19182       this.options.renderer = this.options.renderer || new Renderer;
19183       this.renderer = this.options.renderer;
19184       this.renderer.options = this.options;
19185     }
19186     
19187     /**
19188      * Static Parse Method
19189      */
19190     
19191     Parser.parse = function(src, options, renderer) {
19192       var parser = new Parser(options, renderer);
19193       return parser.parse(src);
19194     };
19195     
19196     /**
19197      * Parse Loop
19198      */
19199     
19200     Parser.prototype.parse = function(src) {
19201       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19202       this.tokens = src.reverse();
19203     
19204       var out = '';
19205       while (this.next()) {
19206         out += this.tok();
19207       }
19208     
19209       return out;
19210     };
19211     
19212     /**
19213      * Next Token
19214      */
19215     
19216     Parser.prototype.next = function() {
19217       return this.token = this.tokens.pop();
19218     };
19219     
19220     /**
19221      * Preview Next Token
19222      */
19223     
19224     Parser.prototype.peek = function() {
19225       return this.tokens[this.tokens.length - 1] || 0;
19226     };
19227     
19228     /**
19229      * Parse Text Tokens
19230      */
19231     
19232     Parser.prototype.parseText = function() {
19233       var body = this.token.text;
19234     
19235       while (this.peek().type === 'text') {
19236         body += '\n' + this.next().text;
19237       }
19238     
19239       return this.inline.output(body);
19240     };
19241     
19242     /**
19243      * Parse Current Token
19244      */
19245     
19246     Parser.prototype.tok = function() {
19247       switch (this.token.type) {
19248         case 'space': {
19249           return '';
19250         }
19251         case 'hr': {
19252           return this.renderer.hr();
19253         }
19254         case 'heading': {
19255           return this.renderer.heading(
19256             this.inline.output(this.token.text),
19257             this.token.depth,
19258             this.token.text);
19259         }
19260         case 'code': {
19261           return this.renderer.code(this.token.text,
19262             this.token.lang,
19263             this.token.escaped);
19264         }
19265         case 'table': {
19266           var header = ''
19267             , body = ''
19268             , i
19269             , row
19270             , cell
19271             , flags
19272             , j;
19273     
19274           // header
19275           cell = '';
19276           for (i = 0; i < this.token.header.length; i++) {
19277             flags = { header: true, align: this.token.align[i] };
19278             cell += this.renderer.tablecell(
19279               this.inline.output(this.token.header[i]),
19280               { header: true, align: this.token.align[i] }
19281             );
19282           }
19283           header += this.renderer.tablerow(cell);
19284     
19285           for (i = 0; i < this.token.cells.length; i++) {
19286             row = this.token.cells[i];
19287     
19288             cell = '';
19289             for (j = 0; j < row.length; j++) {
19290               cell += this.renderer.tablecell(
19291                 this.inline.output(row[j]),
19292                 { header: false, align: this.token.align[j] }
19293               );
19294             }
19295     
19296             body += this.renderer.tablerow(cell);
19297           }
19298           return this.renderer.table(header, body);
19299         }
19300         case 'blockquote_start': {
19301           var body = '';
19302     
19303           while (this.next().type !== 'blockquote_end') {
19304             body += this.tok();
19305           }
19306     
19307           return this.renderer.blockquote(body);
19308         }
19309         case 'list_start': {
19310           var body = ''
19311             , ordered = this.token.ordered;
19312     
19313           while (this.next().type !== 'list_end') {
19314             body += this.tok();
19315           }
19316     
19317           return this.renderer.list(body, ordered);
19318         }
19319         case 'list_item_start': {
19320           var body = '';
19321     
19322           while (this.next().type !== 'list_item_end') {
19323             body += this.token.type === 'text'
19324               ? this.parseText()
19325               : this.tok();
19326           }
19327     
19328           return this.renderer.listitem(body);
19329         }
19330         case 'loose_item_start': {
19331           var body = '';
19332     
19333           while (this.next().type !== 'list_item_end') {
19334             body += this.tok();
19335           }
19336     
19337           return this.renderer.listitem(body);
19338         }
19339         case 'html': {
19340           var html = !this.token.pre && !this.options.pedantic
19341             ? this.inline.output(this.token.text)
19342             : this.token.text;
19343           return this.renderer.html(html);
19344         }
19345         case 'paragraph': {
19346           return this.renderer.paragraph(this.inline.output(this.token.text));
19347         }
19348         case 'text': {
19349           return this.renderer.paragraph(this.parseText());
19350         }
19351       }
19352     };
19353   
19354     
19355     /**
19356      * Marked
19357      */
19358          /**
19359          * eval:var:marked
19360     */
19361     var marked = function (src, opt, callback) {
19362       if (callback || typeof opt === 'function') {
19363         if (!callback) {
19364           callback = opt;
19365           opt = null;
19366         }
19367     
19368         opt = merge({}, marked.defaults, opt || {});
19369     
19370         var highlight = opt.highlight
19371           , tokens
19372           , pending
19373           , i = 0;
19374     
19375         try {
19376           tokens = Lexer.lex(src, opt)
19377         } catch (e) {
19378           return callback(e);
19379         }
19380     
19381         pending = tokens.length;
19382          /**
19383          * eval:var:done
19384     */
19385         var done = function(err) {
19386           if (err) {
19387             opt.highlight = highlight;
19388             return callback(err);
19389           }
19390     
19391           var out;
19392     
19393           try {
19394             out = Parser.parse(tokens, opt);
19395           } catch (e) {
19396             err = e;
19397           }
19398     
19399           opt.highlight = highlight;
19400     
19401           return err
19402             ? callback(err)
19403             : callback(null, out);
19404         };
19405     
19406         if (!highlight || highlight.length < 3) {
19407           return done();
19408         }
19409     
19410         delete opt.highlight;
19411     
19412         if (!pending) { return done(); }
19413     
19414         for (; i < tokens.length; i++) {
19415           (function(token) {
19416             if (token.type !== 'code') {
19417               return --pending || done();
19418             }
19419             return highlight(token.text, token.lang, function(err, code) {
19420               if (err) { return done(err); }
19421               if (code == null || code === token.text) {
19422                 return --pending || done();
19423               }
19424               token.text = code;
19425               token.escaped = true;
19426               --pending || done();
19427             });
19428           })(tokens[i]);
19429         }
19430     
19431         return;
19432       }
19433       try {
19434         if (opt) { opt = merge({}, marked.defaults, opt); }
19435         return Parser.parse(Lexer.lex(src, opt), opt);
19436       } catch (e) {
19437         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19438         if ((opt || marked.defaults).silent) {
19439           return '<p>An error occured:</p><pre>'
19440             + escape(e.message + '', true)
19441             + '</pre>';
19442         }
19443         throw e;
19444       }
19445     }
19446     
19447     /**
19448      * Options
19449      */
19450     
19451     marked.options =
19452     marked.setOptions = function(opt) {
19453       merge(marked.defaults, opt);
19454       return marked;
19455     };
19456     
19457     marked.defaults = {
19458       gfm: true,
19459       tables: true,
19460       breaks: false,
19461       pedantic: false,
19462       sanitize: false,
19463       sanitizer: null,
19464       mangle: true,
19465       smartLists: false,
19466       silent: false,
19467       highlight: null,
19468       langPrefix: 'lang-',
19469       smartypants: false,
19470       headerPrefix: '',
19471       renderer: new Renderer,
19472       xhtml: false
19473     };
19474     
19475     /**
19476      * Expose
19477      */
19478     
19479     marked.Parser = Parser;
19480     marked.parser = Parser.parse;
19481     
19482     marked.Renderer = Renderer;
19483     
19484     marked.Lexer = Lexer;
19485     marked.lexer = Lexer.lex;
19486     
19487     marked.InlineLexer = InlineLexer;
19488     marked.inlineLexer = InlineLexer.output;
19489     
19490     marked.parse = marked;
19491     
19492     Roo.Markdown.marked = marked;
19493
19494 })();/*
19495  * Based on:
19496  * Ext JS Library 1.1.1
19497  * Copyright(c) 2006-2007, Ext JS, LLC.
19498  *
19499  * Originally Released Under LGPL - original licence link has changed is not relivant.
19500  *
19501  * Fork - LGPL
19502  * <script type="text/javascript">
19503  */
19504
19505
19506
19507 /*
19508  * These classes are derivatives of the similarly named classes in the YUI Library.
19509  * The original license:
19510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19511  * Code licensed under the BSD License:
19512  * http://developer.yahoo.net/yui/license.txt
19513  */
19514
19515 (function() {
19516
19517 var Event=Roo.EventManager;
19518 var Dom=Roo.lib.Dom;
19519
19520 /**
19521  * @class Roo.dd.DragDrop
19522  * @extends Roo.util.Observable
19523  * Defines the interface and base operation of items that that can be
19524  * dragged or can be drop targets.  It was designed to be extended, overriding
19525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19526  * Up to three html elements can be associated with a DragDrop instance:
19527  * <ul>
19528  * <li>linked element: the element that is passed into the constructor.
19529  * This is the element which defines the boundaries for interaction with
19530  * other DragDrop objects.</li>
19531  * <li>handle element(s): The drag operation only occurs if the element that
19532  * was clicked matches a handle element.  By default this is the linked
19533  * element, but there are times that you will want only a portion of the
19534  * linked element to initiate the drag operation, and the setHandleElId()
19535  * method provides a way to define this.</li>
19536  * <li>drag element: this represents the element that would be moved along
19537  * with the cursor during a drag operation.  By default, this is the linked
19538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19540  * </li>
19541  * </ul>
19542  * This class should not be instantiated until the onload event to ensure that
19543  * the associated elements are available.
19544  * The following would define a DragDrop obj that would interact with any
19545  * other DragDrop obj in the "group1" group:
19546  * <pre>
19547  *  dd = new Roo.dd.DragDrop("div1", "group1");
19548  * </pre>
19549  * Since none of the event handlers have been implemented, nothing would
19550  * actually happen if you were to run the code above.  Normally you would
19551  * override this class or one of the default implementations, but you can
19552  * also override the methods you want on an instance of the class...
19553  * <pre>
19554  *  dd.onDragDrop = function(e, id) {
19555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19556  *  }
19557  * </pre>
19558  * @constructor
19559  * @param {String} id of the element that is linked to this instance
19560  * @param {String} sGroup the group of related DragDrop objects
19561  * @param {object} config an object containing configurable attributes
19562  *                Valid properties for DragDrop:
19563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19564  */
19565 Roo.dd.DragDrop = function(id, sGroup, config) {
19566     if (id) {
19567         this.init(id, sGroup, config);
19568     }
19569     
19570 };
19571
19572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19573
19574     /**
19575      * The id of the element associated with this object.  This is what we
19576      * refer to as the "linked element" because the size and position of
19577      * this element is used to determine when the drag and drop objects have
19578      * interacted.
19579      * @property id
19580      * @type String
19581      */
19582     id: null,
19583
19584     /**
19585      * Configuration attributes passed into the constructor
19586      * @property config
19587      * @type object
19588      */
19589     config: null,
19590
19591     /**
19592      * The id of the element that will be dragged.  By default this is same
19593      * as the linked element , but could be changed to another element. Ex:
19594      * Roo.dd.DDProxy
19595      * @property dragElId
19596      * @type String
19597      * @private
19598      */
19599     dragElId: null,
19600
19601     /**
19602      * the id of the element that initiates the drag operation.  By default
19603      * this is the linked element, but could be changed to be a child of this
19604      * element.  This lets us do things like only starting the drag when the
19605      * header element within the linked html element is clicked.
19606      * @property handleElId
19607      * @type String
19608      * @private
19609      */
19610     handleElId: null,
19611
19612     /**
19613      * An associative array of HTML tags that will be ignored if clicked.
19614      * @property invalidHandleTypes
19615      * @type {string: string}
19616      */
19617     invalidHandleTypes: null,
19618
19619     /**
19620      * An associative array of ids for elements that will be ignored if clicked
19621      * @property invalidHandleIds
19622      * @type {string: string}
19623      */
19624     invalidHandleIds: null,
19625
19626     /**
19627      * An indexted array of css class names for elements that will be ignored
19628      * if clicked.
19629      * @property invalidHandleClasses
19630      * @type string[]
19631      */
19632     invalidHandleClasses: null,
19633
19634     /**
19635      * The linked element's absolute X position at the time the drag was
19636      * started
19637      * @property startPageX
19638      * @type int
19639      * @private
19640      */
19641     startPageX: 0,
19642
19643     /**
19644      * The linked element's absolute X position at the time the drag was
19645      * started
19646      * @property startPageY
19647      * @type int
19648      * @private
19649      */
19650     startPageY: 0,
19651
19652     /**
19653      * The group defines a logical collection of DragDrop objects that are
19654      * related.  Instances only get events when interacting with other
19655      * DragDrop object in the same group.  This lets us define multiple
19656      * groups using a single DragDrop subclass if we want.
19657      * @property groups
19658      * @type {string: string}
19659      */
19660     groups: null,
19661
19662     /**
19663      * Individual drag/drop instances can be locked.  This will prevent
19664      * onmousedown start drag.
19665      * @property locked
19666      * @type boolean
19667      * @private
19668      */
19669     locked: false,
19670
19671     /**
19672      * Lock this instance
19673      * @method lock
19674      */
19675     lock: function() { this.locked = true; },
19676
19677     /**
19678      * Unlock this instace
19679      * @method unlock
19680      */
19681     unlock: function() { this.locked = false; },
19682
19683     /**
19684      * By default, all insances can be a drop target.  This can be disabled by
19685      * setting isTarget to false.
19686      * @method isTarget
19687      * @type boolean
19688      */
19689     isTarget: true,
19690
19691     /**
19692      * The padding configured for this drag and drop object for calculating
19693      * the drop zone intersection with this object.
19694      * @method padding
19695      * @type int[]
19696      */
19697     padding: null,
19698
19699     /**
19700      * Cached reference to the linked element
19701      * @property _domRef
19702      * @private
19703      */
19704     _domRef: null,
19705
19706     /**
19707      * Internal typeof flag
19708      * @property __ygDragDrop
19709      * @private
19710      */
19711     __ygDragDrop: true,
19712
19713     /**
19714      * Set to true when horizontal contraints are applied
19715      * @property constrainX
19716      * @type boolean
19717      * @private
19718      */
19719     constrainX: false,
19720
19721     /**
19722      * Set to true when vertical contraints are applied
19723      * @property constrainY
19724      * @type boolean
19725      * @private
19726      */
19727     constrainY: false,
19728
19729     /**
19730      * The left constraint
19731      * @property minX
19732      * @type int
19733      * @private
19734      */
19735     minX: 0,
19736
19737     /**
19738      * The right constraint
19739      * @property maxX
19740      * @type int
19741      * @private
19742      */
19743     maxX: 0,
19744
19745     /**
19746      * The up constraint
19747      * @property minY
19748      * @type int
19749      * @type int
19750      * @private
19751      */
19752     minY: 0,
19753
19754     /**
19755      * The down constraint
19756      * @property maxY
19757      * @type int
19758      * @private
19759      */
19760     maxY: 0,
19761
19762     /**
19763      * Maintain offsets when we resetconstraints.  Set to true when you want
19764      * the position of the element relative to its parent to stay the same
19765      * when the page changes
19766      *
19767      * @property maintainOffset
19768      * @type boolean
19769      */
19770     maintainOffset: false,
19771
19772     /**
19773      * Array of pixel locations the element will snap to if we specified a
19774      * horizontal graduation/interval.  This array is generated automatically
19775      * when you define a tick interval.
19776      * @property xTicks
19777      * @type int[]
19778      */
19779     xTicks: null,
19780
19781     /**
19782      * Array of pixel locations the element will snap to if we specified a
19783      * vertical graduation/interval.  This array is generated automatically
19784      * when you define a tick interval.
19785      * @property yTicks
19786      * @type int[]
19787      */
19788     yTicks: null,
19789
19790     /**
19791      * By default the drag and drop instance will only respond to the primary
19792      * button click (left button for a right-handed mouse).  Set to true to
19793      * allow drag and drop to start with any mouse click that is propogated
19794      * by the browser
19795      * @property primaryButtonOnly
19796      * @type boolean
19797      */
19798     primaryButtonOnly: true,
19799
19800     /**
19801      * The availabe property is false until the linked dom element is accessible.
19802      * @property available
19803      * @type boolean
19804      */
19805     available: false,
19806
19807     /**
19808      * By default, drags can only be initiated if the mousedown occurs in the
19809      * region the linked element is.  This is done in part to work around a
19810      * bug in some browsers that mis-report the mousedown if the previous
19811      * mouseup happened outside of the window.  This property is set to true
19812      * if outer handles are defined.
19813      *
19814      * @property hasOuterHandles
19815      * @type boolean
19816      * @default false
19817      */
19818     hasOuterHandles: false,
19819
19820     /**
19821      * Code that executes immediately before the startDrag event
19822      * @method b4StartDrag
19823      * @private
19824      */
19825     b4StartDrag: function(x, y) { },
19826
19827     /**
19828      * Abstract method called after a drag/drop object is clicked
19829      * and the drag or mousedown time thresholds have beeen met.
19830      * @method startDrag
19831      * @param {int} X click location
19832      * @param {int} Y click location
19833      */
19834     startDrag: function(x, y) { /* override this */ },
19835
19836     /**
19837      * Code that executes immediately before the onDrag event
19838      * @method b4Drag
19839      * @private
19840      */
19841     b4Drag: function(e) { },
19842
19843     /**
19844      * Abstract method called during the onMouseMove event while dragging an
19845      * object.
19846      * @method onDrag
19847      * @param {Event} e the mousemove event
19848      */
19849     onDrag: function(e) { /* override this */ },
19850
19851     /**
19852      * Abstract method called when this element fist begins hovering over
19853      * another DragDrop obj
19854      * @method onDragEnter
19855      * @param {Event} e the mousemove event
19856      * @param {String|DragDrop[]} id In POINT mode, the element
19857      * id this is hovering over.  In INTERSECT mode, an array of one or more
19858      * dragdrop items being hovered over.
19859      */
19860     onDragEnter: function(e, id) { /* override this */ },
19861
19862     /**
19863      * Code that executes immediately before the onDragOver event
19864      * @method b4DragOver
19865      * @private
19866      */
19867     b4DragOver: function(e) { },
19868
19869     /**
19870      * Abstract method called when this element is hovering over another
19871      * DragDrop obj
19872      * @method onDragOver
19873      * @param {Event} e the mousemove event
19874      * @param {String|DragDrop[]} id In POINT mode, the element
19875      * id this is hovering over.  In INTERSECT mode, an array of dd items
19876      * being hovered over.
19877      */
19878     onDragOver: function(e, id) { /* override this */ },
19879
19880     /**
19881      * Code that executes immediately before the onDragOut event
19882      * @method b4DragOut
19883      * @private
19884      */
19885     b4DragOut: function(e) { },
19886
19887     /**
19888      * Abstract method called when we are no longer hovering over an element
19889      * @method onDragOut
19890      * @param {Event} e the mousemove event
19891      * @param {String|DragDrop[]} id In POINT mode, the element
19892      * id this was hovering over.  In INTERSECT mode, an array of dd items
19893      * that the mouse is no longer over.
19894      */
19895     onDragOut: function(e, id) { /* override this */ },
19896
19897     /**
19898      * Code that executes immediately before the onDragDrop event
19899      * @method b4DragDrop
19900      * @private
19901      */
19902     b4DragDrop: function(e) { },
19903
19904     /**
19905      * Abstract method called when this item is dropped on another DragDrop
19906      * obj
19907      * @method onDragDrop
19908      * @param {Event} e the mouseup event
19909      * @param {String|DragDrop[]} id In POINT mode, the element
19910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19911      * was dropped on.
19912      */
19913     onDragDrop: function(e, id) { /* override this */ },
19914
19915     /**
19916      * Abstract method called when this item is dropped on an area with no
19917      * drop target
19918      * @method onInvalidDrop
19919      * @param {Event} e the mouseup event
19920      */
19921     onInvalidDrop: function(e) { /* override this */ },
19922
19923     /**
19924      * Code that executes immediately before the endDrag event
19925      * @method b4EndDrag
19926      * @private
19927      */
19928     b4EndDrag: function(e) { },
19929
19930     /**
19931      * Fired when we are done dragging the object
19932      * @method endDrag
19933      * @param {Event} e the mouseup event
19934      */
19935     endDrag: function(e) { /* override this */ },
19936
19937     /**
19938      * Code executed immediately before the onMouseDown event
19939      * @method b4MouseDown
19940      * @param {Event} e the mousedown event
19941      * @private
19942      */
19943     b4MouseDown: function(e) {  },
19944
19945     /**
19946      * Event handler that fires when a drag/drop obj gets a mousedown
19947      * @method onMouseDown
19948      * @param {Event} e the mousedown event
19949      */
19950     onMouseDown: function(e) { /* override this */ },
19951
19952     /**
19953      * Event handler that fires when a drag/drop obj gets a mouseup
19954      * @method onMouseUp
19955      * @param {Event} e the mouseup event
19956      */
19957     onMouseUp: function(e) { /* override this */ },
19958
19959     /**
19960      * Override the onAvailable method to do what is needed after the initial
19961      * position was determined.
19962      * @method onAvailable
19963      */
19964     onAvailable: function () {
19965     },
19966
19967     /*
19968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19969      * @type Object
19970      */
19971     defaultPadding : {left:0, right:0, top:0, bottom:0},
19972
19973     /*
19974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19975  *
19976  * Usage:
19977  <pre><code>
19978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19979                 { dragElId: "existingProxyDiv" });
19980  dd.startDrag = function(){
19981      this.constrainTo("parent-id");
19982  };
19983  </code></pre>
19984  * Or you can initalize it using the {@link Roo.Element} object:
19985  <pre><code>
19986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19987      startDrag : function(){
19988          this.constrainTo("parent-id");
19989      }
19990  });
19991  </code></pre>
19992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19995      * an object containing the sides to pad. For example: {right:10, bottom:10}
19996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19997      */
19998     constrainTo : function(constrainTo, pad, inContent){
19999         if(typeof pad == "number"){
20000             pad = {left: pad, right:pad, top:pad, bottom:pad};
20001         }
20002         pad = pad || this.defaultPadding;
20003         var b = Roo.get(this.getEl()).getBox();
20004         var ce = Roo.get(constrainTo);
20005         var s = ce.getScroll();
20006         var c, cd = ce.dom;
20007         if(cd == document.body){
20008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20009         }else{
20010             xy = ce.getXY();
20011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20012         }
20013
20014
20015         var topSpace = b.y - c.y;
20016         var leftSpace = b.x - c.x;
20017
20018         this.resetConstraints();
20019         this.setXConstraint(leftSpace - (pad.left||0), // left
20020                 c.width - leftSpace - b.width - (pad.right||0) //right
20021         );
20022         this.setYConstraint(topSpace - (pad.top||0), //top
20023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20024         );
20025     },
20026
20027     /**
20028      * Returns a reference to the linked element
20029      * @method getEl
20030      * @return {HTMLElement} the html element
20031      */
20032     getEl: function() {
20033         if (!this._domRef) {
20034             this._domRef = Roo.getDom(this.id);
20035         }
20036
20037         return this._domRef;
20038     },
20039
20040     /**
20041      * Returns a reference to the actual element to drag.  By default this is
20042      * the same as the html element, but it can be assigned to another
20043      * element. An example of this can be found in Roo.dd.DDProxy
20044      * @method getDragEl
20045      * @return {HTMLElement} the html element
20046      */
20047     getDragEl: function() {
20048         return Roo.getDom(this.dragElId);
20049     },
20050
20051     /**
20052      * Sets up the DragDrop object.  Must be called in the constructor of any
20053      * Roo.dd.DragDrop subclass
20054      * @method init
20055      * @param id the id of the linked element
20056      * @param {String} sGroup the group of related items
20057      * @param {object} config configuration attributes
20058      */
20059     init: function(id, sGroup, config) {
20060         this.initTarget(id, sGroup, config);
20061         if (!Roo.isTouch) {
20062             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20063         }
20064         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20065         // Event.on(this.id, "selectstart", Event.preventDefault);
20066     },
20067
20068     /**
20069      * Initializes Targeting functionality only... the object does not
20070      * get a mousedown handler.
20071      * @method initTarget
20072      * @param id the id of the linked element
20073      * @param {String} sGroup the group of related items
20074      * @param {object} config configuration attributes
20075      */
20076     initTarget: function(id, sGroup, config) {
20077
20078         // configuration attributes
20079         this.config = config || {};
20080
20081         // create a local reference to the drag and drop manager
20082         this.DDM = Roo.dd.DDM;
20083         // initialize the groups array
20084         this.groups = {};
20085
20086         // assume that we have an element reference instead of an id if the
20087         // parameter is not a string
20088         if (typeof id !== "string") {
20089             id = Roo.id(id);
20090         }
20091
20092         // set the id
20093         this.id = id;
20094
20095         // add to an interaction group
20096         this.addToGroup((sGroup) ? sGroup : "default");
20097
20098         // We don't want to register this as the handle with the manager
20099         // so we just set the id rather than calling the setter.
20100         this.handleElId = id;
20101
20102         // the linked element is the element that gets dragged by default
20103         this.setDragElId(id);
20104
20105         // by default, clicked anchors will not start drag operations.
20106         this.invalidHandleTypes = { A: "A" };
20107         this.invalidHandleIds = {};
20108         this.invalidHandleClasses = [];
20109
20110         this.applyConfig();
20111
20112         this.handleOnAvailable();
20113     },
20114
20115     /**
20116      * Applies the configuration parameters that were passed into the constructor.
20117      * This is supposed to happen at each level through the inheritance chain.  So
20118      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20119      * DragDrop in order to get all of the parameters that are available in
20120      * each object.
20121      * @method applyConfig
20122      */
20123     applyConfig: function() {
20124
20125         // configurable properties:
20126         //    padding, isTarget, maintainOffset, primaryButtonOnly
20127         this.padding           = this.config.padding || [0, 0, 0, 0];
20128         this.isTarget          = (this.config.isTarget !== false);
20129         this.maintainOffset    = (this.config.maintainOffset);
20130         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20131
20132     },
20133
20134     /**
20135      * Executed when the linked element is available
20136      * @method handleOnAvailable
20137      * @private
20138      */
20139     handleOnAvailable: function() {
20140         this.available = true;
20141         this.resetConstraints();
20142         this.onAvailable();
20143     },
20144
20145      /**
20146      * Configures the padding for the target zone in px.  Effectively expands
20147      * (or reduces) the virtual object size for targeting calculations.
20148      * Supports css-style shorthand; if only one parameter is passed, all sides
20149      * will have that padding, and if only two are passed, the top and bottom
20150      * will have the first param, the left and right the second.
20151      * @method setPadding
20152      * @param {int} iTop    Top pad
20153      * @param {int} iRight  Right pad
20154      * @param {int} iBot    Bot pad
20155      * @param {int} iLeft   Left pad
20156      */
20157     setPadding: function(iTop, iRight, iBot, iLeft) {
20158         // this.padding = [iLeft, iRight, iTop, iBot];
20159         if (!iRight && 0 !== iRight) {
20160             this.padding = [iTop, iTop, iTop, iTop];
20161         } else if (!iBot && 0 !== iBot) {
20162             this.padding = [iTop, iRight, iTop, iRight];
20163         } else {
20164             this.padding = [iTop, iRight, iBot, iLeft];
20165         }
20166     },
20167
20168     /**
20169      * Stores the initial placement of the linked element.
20170      * @method setInitialPosition
20171      * @param {int} diffX   the X offset, default 0
20172      * @param {int} diffY   the Y offset, default 0
20173      */
20174     setInitPosition: function(diffX, diffY) {
20175         var el = this.getEl();
20176
20177         if (!this.DDM.verifyEl(el)) {
20178             return;
20179         }
20180
20181         var dx = diffX || 0;
20182         var dy = diffY || 0;
20183
20184         var p = Dom.getXY( el );
20185
20186         this.initPageX = p[0] - dx;
20187         this.initPageY = p[1] - dy;
20188
20189         this.lastPageX = p[0];
20190         this.lastPageY = p[1];
20191
20192
20193         this.setStartPosition(p);
20194     },
20195
20196     /**
20197      * Sets the start position of the element.  This is set when the obj
20198      * is initialized, the reset when a drag is started.
20199      * @method setStartPosition
20200      * @param pos current position (from previous lookup)
20201      * @private
20202      */
20203     setStartPosition: function(pos) {
20204         var p = pos || Dom.getXY( this.getEl() );
20205         this.deltaSetXY = null;
20206
20207         this.startPageX = p[0];
20208         this.startPageY = p[1];
20209     },
20210
20211     /**
20212      * Add this instance to a group of related drag/drop objects.  All
20213      * instances belong to at least one group, and can belong to as many
20214      * groups as needed.
20215      * @method addToGroup
20216      * @param sGroup {string} the name of the group
20217      */
20218     addToGroup: function(sGroup) {
20219         this.groups[sGroup] = true;
20220         this.DDM.regDragDrop(this, sGroup);
20221     },
20222
20223     /**
20224      * Remove's this instance from the supplied interaction group
20225      * @method removeFromGroup
20226      * @param {string}  sGroup  The group to drop
20227      */
20228     removeFromGroup: function(sGroup) {
20229         if (this.groups[sGroup]) {
20230             delete this.groups[sGroup];
20231         }
20232
20233         this.DDM.removeDDFromGroup(this, sGroup);
20234     },
20235
20236     /**
20237      * Allows you to specify that an element other than the linked element
20238      * will be moved with the cursor during a drag
20239      * @method setDragElId
20240      * @param id {string} the id of the element that will be used to initiate the drag
20241      */
20242     setDragElId: function(id) {
20243         this.dragElId = id;
20244     },
20245
20246     /**
20247      * Allows you to specify a child of the linked element that should be
20248      * used to initiate the drag operation.  An example of this would be if
20249      * you have a content div with text and links.  Clicking anywhere in the
20250      * content area would normally start the drag operation.  Use this method
20251      * to specify that an element inside of the content div is the element
20252      * that starts the drag operation.
20253      * @method setHandleElId
20254      * @param id {string} the id of the element that will be used to
20255      * initiate the drag.
20256      */
20257     setHandleElId: function(id) {
20258         if (typeof id !== "string") {
20259             id = Roo.id(id);
20260         }
20261         this.handleElId = id;
20262         this.DDM.regHandle(this.id, id);
20263     },
20264
20265     /**
20266      * Allows you to set an element outside of the linked element as a drag
20267      * handle
20268      * @method setOuterHandleElId
20269      * @param id the id of the element that will be used to initiate the drag
20270      */
20271     setOuterHandleElId: function(id) {
20272         if (typeof id !== "string") {
20273             id = Roo.id(id);
20274         }
20275         Event.on(id, "mousedown",
20276                 this.handleMouseDown, this);
20277         this.setHandleElId(id);
20278
20279         this.hasOuterHandles = true;
20280     },
20281
20282     /**
20283      * Remove all drag and drop hooks for this element
20284      * @method unreg
20285      */
20286     unreg: function() {
20287         Event.un(this.id, "mousedown",
20288                 this.handleMouseDown);
20289         Event.un(this.id, "touchstart",
20290                 this.handleMouseDown);
20291         this._domRef = null;
20292         this.DDM._remove(this);
20293     },
20294
20295     destroy : function(){
20296         this.unreg();
20297     },
20298
20299     /**
20300      * Returns true if this instance is locked, or the drag drop mgr is locked
20301      * (meaning that all drag/drop is disabled on the page.)
20302      * @method isLocked
20303      * @return {boolean} true if this obj or all drag/drop is locked, else
20304      * false
20305      */
20306     isLocked: function() {
20307         return (this.DDM.isLocked() || this.locked);
20308     },
20309
20310     /**
20311      * Fired when this object is clicked
20312      * @method handleMouseDown
20313      * @param {Event} e
20314      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20315      * @private
20316      */
20317     handleMouseDown: function(e, oDD){
20318      
20319         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20320             //Roo.log('not touch/ button !=0');
20321             return;
20322         }
20323         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20324             return; // double touch..
20325         }
20326         
20327
20328         if (this.isLocked()) {
20329             //Roo.log('locked');
20330             return;
20331         }
20332
20333         this.DDM.refreshCache(this.groups);
20334 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20335         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20336         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20337             //Roo.log('no outer handes or not over target');
20338                 // do nothing.
20339         } else {
20340 //            Roo.log('check validator');
20341             if (this.clickValidator(e)) {
20342 //                Roo.log('validate success');
20343                 // set the initial element position
20344                 this.setStartPosition();
20345
20346
20347                 this.b4MouseDown(e);
20348                 this.onMouseDown(e);
20349
20350                 this.DDM.handleMouseDown(e, this);
20351
20352                 this.DDM.stopEvent(e);
20353             } else {
20354
20355
20356             }
20357         }
20358     },
20359
20360     clickValidator: function(e) {
20361         var target = e.getTarget();
20362         return ( this.isValidHandleChild(target) &&
20363                     (this.id == this.handleElId ||
20364                         this.DDM.handleWasClicked(target, this.id)) );
20365     },
20366
20367     /**
20368      * Allows you to specify a tag name that should not start a drag operation
20369      * when clicked.  This is designed to facilitate embedding links within a
20370      * drag handle that do something other than start the drag.
20371      * @method addInvalidHandleType
20372      * @param {string} tagName the type of element to exclude
20373      */
20374     addInvalidHandleType: function(tagName) {
20375         var type = tagName.toUpperCase();
20376         this.invalidHandleTypes[type] = type;
20377     },
20378
20379     /**
20380      * Lets you to specify an element id for a child of a drag handle
20381      * that should not initiate a drag
20382      * @method addInvalidHandleId
20383      * @param {string} id the element id of the element you wish to ignore
20384      */
20385     addInvalidHandleId: function(id) {
20386         if (typeof id !== "string") {
20387             id = Roo.id(id);
20388         }
20389         this.invalidHandleIds[id] = id;
20390     },
20391
20392     /**
20393      * Lets you specify a css class of elements that will not initiate a drag
20394      * @method addInvalidHandleClass
20395      * @param {string} cssClass the class of the elements you wish to ignore
20396      */
20397     addInvalidHandleClass: function(cssClass) {
20398         this.invalidHandleClasses.push(cssClass);
20399     },
20400
20401     /**
20402      * Unsets an excluded tag name set by addInvalidHandleType
20403      * @method removeInvalidHandleType
20404      * @param {string} tagName the type of element to unexclude
20405      */
20406     removeInvalidHandleType: function(tagName) {
20407         var type = tagName.toUpperCase();
20408         // this.invalidHandleTypes[type] = null;
20409         delete this.invalidHandleTypes[type];
20410     },
20411
20412     /**
20413      * Unsets an invalid handle id
20414      * @method removeInvalidHandleId
20415      * @param {string} id the id of the element to re-enable
20416      */
20417     removeInvalidHandleId: function(id) {
20418         if (typeof id !== "string") {
20419             id = Roo.id(id);
20420         }
20421         delete this.invalidHandleIds[id];
20422     },
20423
20424     /**
20425      * Unsets an invalid css class
20426      * @method removeInvalidHandleClass
20427      * @param {string} cssClass the class of the element(s) you wish to
20428      * re-enable
20429      */
20430     removeInvalidHandleClass: function(cssClass) {
20431         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20432             if (this.invalidHandleClasses[i] == cssClass) {
20433                 delete this.invalidHandleClasses[i];
20434             }
20435         }
20436     },
20437
20438     /**
20439      * Checks the tag exclusion list to see if this click should be ignored
20440      * @method isValidHandleChild
20441      * @param {HTMLElement} node the HTMLElement to evaluate
20442      * @return {boolean} true if this is a valid tag type, false if not
20443      */
20444     isValidHandleChild: function(node) {
20445
20446         var valid = true;
20447         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20448         var nodeName;
20449         try {
20450             nodeName = node.nodeName.toUpperCase();
20451         } catch(e) {
20452             nodeName = node.nodeName;
20453         }
20454         valid = valid && !this.invalidHandleTypes[nodeName];
20455         valid = valid && !this.invalidHandleIds[node.id];
20456
20457         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20458             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20459         }
20460
20461
20462         return valid;
20463
20464     },
20465
20466     /**
20467      * Create the array of horizontal tick marks if an interval was specified
20468      * in setXConstraint().
20469      * @method setXTicks
20470      * @private
20471      */
20472     setXTicks: function(iStartX, iTickSize) {
20473         this.xTicks = [];
20474         this.xTickSize = iTickSize;
20475
20476         var tickMap = {};
20477
20478         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20479             if (!tickMap[i]) {
20480                 this.xTicks[this.xTicks.length] = i;
20481                 tickMap[i] = true;
20482             }
20483         }
20484
20485         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20486             if (!tickMap[i]) {
20487                 this.xTicks[this.xTicks.length] = i;
20488                 tickMap[i] = true;
20489             }
20490         }
20491
20492         this.xTicks.sort(this.DDM.numericSort) ;
20493     },
20494
20495     /**
20496      * Create the array of vertical tick marks if an interval was specified in
20497      * setYConstraint().
20498      * @method setYTicks
20499      * @private
20500      */
20501     setYTicks: function(iStartY, iTickSize) {
20502         this.yTicks = [];
20503         this.yTickSize = iTickSize;
20504
20505         var tickMap = {};
20506
20507         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20508             if (!tickMap[i]) {
20509                 this.yTicks[this.yTicks.length] = i;
20510                 tickMap[i] = true;
20511             }
20512         }
20513
20514         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20515             if (!tickMap[i]) {
20516                 this.yTicks[this.yTicks.length] = i;
20517                 tickMap[i] = true;
20518             }
20519         }
20520
20521         this.yTicks.sort(this.DDM.numericSort) ;
20522     },
20523
20524     /**
20525      * By default, the element can be dragged any place on the screen.  Use
20526      * this method to limit the horizontal travel of the element.  Pass in
20527      * 0,0 for the parameters if you want to lock the drag to the y axis.
20528      * @method setXConstraint
20529      * @param {int} iLeft the number of pixels the element can move to the left
20530      * @param {int} iRight the number of pixels the element can move to the
20531      * right
20532      * @param {int} iTickSize optional parameter for specifying that the
20533      * element
20534      * should move iTickSize pixels at a time.
20535      */
20536     setXConstraint: function(iLeft, iRight, iTickSize) {
20537         this.leftConstraint = iLeft;
20538         this.rightConstraint = iRight;
20539
20540         this.minX = this.initPageX - iLeft;
20541         this.maxX = this.initPageX + iRight;
20542         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20543
20544         this.constrainX = true;
20545     },
20546
20547     /**
20548      * Clears any constraints applied to this instance.  Also clears ticks
20549      * since they can't exist independent of a constraint at this time.
20550      * @method clearConstraints
20551      */
20552     clearConstraints: function() {
20553         this.constrainX = false;
20554         this.constrainY = false;
20555         this.clearTicks();
20556     },
20557
20558     /**
20559      * Clears any tick interval defined for this instance
20560      * @method clearTicks
20561      */
20562     clearTicks: function() {
20563         this.xTicks = null;
20564         this.yTicks = null;
20565         this.xTickSize = 0;
20566         this.yTickSize = 0;
20567     },
20568
20569     /**
20570      * By default, the element can be dragged any place on the screen.  Set
20571      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20572      * parameters if you want to lock the drag to the x axis.
20573      * @method setYConstraint
20574      * @param {int} iUp the number of pixels the element can move up
20575      * @param {int} iDown the number of pixels the element can move down
20576      * @param {int} iTickSize optional parameter for specifying that the
20577      * element should move iTickSize pixels at a time.
20578      */
20579     setYConstraint: function(iUp, iDown, iTickSize) {
20580         this.topConstraint = iUp;
20581         this.bottomConstraint = iDown;
20582
20583         this.minY = this.initPageY - iUp;
20584         this.maxY = this.initPageY + iDown;
20585         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20586
20587         this.constrainY = true;
20588
20589     },
20590
20591     /**
20592      * resetConstraints must be called if you manually reposition a dd element.
20593      * @method resetConstraints
20594      * @param {boolean} maintainOffset
20595      */
20596     resetConstraints: function() {
20597
20598
20599         // Maintain offsets if necessary
20600         if (this.initPageX || this.initPageX === 0) {
20601             // figure out how much this thing has moved
20602             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20603             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20604
20605             this.setInitPosition(dx, dy);
20606
20607         // This is the first time we have detected the element's position
20608         } else {
20609             this.setInitPosition();
20610         }
20611
20612         if (this.constrainX) {
20613             this.setXConstraint( this.leftConstraint,
20614                                  this.rightConstraint,
20615                                  this.xTickSize        );
20616         }
20617
20618         if (this.constrainY) {
20619             this.setYConstraint( this.topConstraint,
20620                                  this.bottomConstraint,
20621                                  this.yTickSize         );
20622         }
20623     },
20624
20625     /**
20626      * Normally the drag element is moved pixel by pixel, but we can specify
20627      * that it move a number of pixels at a time.  This method resolves the
20628      * location when we have it set up like this.
20629      * @method getTick
20630      * @param {int} val where we want to place the object
20631      * @param {int[]} tickArray sorted array of valid points
20632      * @return {int} the closest tick
20633      * @private
20634      */
20635     getTick: function(val, tickArray) {
20636
20637         if (!tickArray) {
20638             // If tick interval is not defined, it is effectively 1 pixel,
20639             // so we return the value passed to us.
20640             return val;
20641         } else if (tickArray[0] >= val) {
20642             // The value is lower than the first tick, so we return the first
20643             // tick.
20644             return tickArray[0];
20645         } else {
20646             for (var i=0, len=tickArray.length; i<len; ++i) {
20647                 var next = i + 1;
20648                 if (tickArray[next] && tickArray[next] >= val) {
20649                     var diff1 = val - tickArray[i];
20650                     var diff2 = tickArray[next] - val;
20651                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20652                 }
20653             }
20654
20655             // The value is larger than the last tick, so we return the last
20656             // tick.
20657             return tickArray[tickArray.length - 1];
20658         }
20659     },
20660
20661     /**
20662      * toString method
20663      * @method toString
20664      * @return {string} string representation of the dd obj
20665      */
20666     toString: function() {
20667         return ("DragDrop " + this.id);
20668     }
20669
20670 });
20671
20672 })();
20673 /*
20674  * Based on:
20675  * Ext JS Library 1.1.1
20676  * Copyright(c) 2006-2007, Ext JS, LLC.
20677  *
20678  * Originally Released Under LGPL - original licence link has changed is not relivant.
20679  *
20680  * Fork - LGPL
20681  * <script type="text/javascript">
20682  */
20683
20684
20685 /**
20686  * The drag and drop utility provides a framework for building drag and drop
20687  * applications.  In addition to enabling drag and drop for specific elements,
20688  * the drag and drop elements are tracked by the manager class, and the
20689  * interactions between the various elements are tracked during the drag and
20690  * the implementing code is notified about these important moments.
20691  */
20692
20693 // Only load the library once.  Rewriting the manager class would orphan
20694 // existing drag and drop instances.
20695 if (!Roo.dd.DragDropMgr) {
20696
20697 /**
20698  * @class Roo.dd.DragDropMgr
20699  * DragDropMgr is a singleton that tracks the element interaction for
20700  * all DragDrop items in the window.  Generally, you will not call
20701  * this class directly, but it does have helper methods that could
20702  * be useful in your DragDrop implementations.
20703  * @static
20704  */
20705 Roo.dd.DragDropMgr = function() {
20706
20707     var Event = Roo.EventManager;
20708
20709     return {
20710
20711         /**
20712          * Two dimensional Array of registered DragDrop objects.  The first
20713          * dimension is the DragDrop item group, the second the DragDrop
20714          * object.
20715          * @property ids
20716          * @type {string: string}
20717          * @private
20718          * @static
20719          */
20720         ids: {},
20721
20722         /**
20723          * Array of element ids defined as drag handles.  Used to determine
20724          * if the element that generated the mousedown event is actually the
20725          * handle and not the html element itself.
20726          * @property handleIds
20727          * @type {string: string}
20728          * @private
20729          * @static
20730          */
20731         handleIds: {},
20732
20733         /**
20734          * the DragDrop object that is currently being dragged
20735          * @property dragCurrent
20736          * @type DragDrop
20737          * @private
20738          * @static
20739          **/
20740         dragCurrent: null,
20741
20742         /**
20743          * the DragDrop object(s) that are being hovered over
20744          * @property dragOvers
20745          * @type Array
20746          * @private
20747          * @static
20748          */
20749         dragOvers: {},
20750
20751         /**
20752          * the X distance between the cursor and the object being dragged
20753          * @property deltaX
20754          * @type int
20755          * @private
20756          * @static
20757          */
20758         deltaX: 0,
20759
20760         /**
20761          * the Y distance between the cursor and the object being dragged
20762          * @property deltaY
20763          * @type int
20764          * @private
20765          * @static
20766          */
20767         deltaY: 0,
20768
20769         /**
20770          * Flag to determine if we should prevent the default behavior of the
20771          * events we define. By default this is true, but this can be set to
20772          * false if you need the default behavior (not recommended)
20773          * @property preventDefault
20774          * @type boolean
20775          * @static
20776          */
20777         preventDefault: true,
20778
20779         /**
20780          * Flag to determine if we should stop the propagation of the events
20781          * we generate. This is true by default but you may want to set it to
20782          * false if the html element contains other features that require the
20783          * mouse click.
20784          * @property stopPropagation
20785          * @type boolean
20786          * @static
20787          */
20788         stopPropagation: true,
20789
20790         /**
20791          * Internal flag that is set to true when drag and drop has been
20792          * intialized
20793          * @property initialized
20794          * @private
20795          * @static
20796          */
20797         initalized: false,
20798
20799         /**
20800          * All drag and drop can be disabled.
20801          * @property locked
20802          * @private
20803          * @static
20804          */
20805         locked: false,
20806
20807         /**
20808          * Called the first time an element is registered.
20809          * @method init
20810          * @private
20811          * @static
20812          */
20813         init: function() {
20814             this.initialized = true;
20815         },
20816
20817         /**
20818          * In point mode, drag and drop interaction is defined by the
20819          * location of the cursor during the drag/drop
20820          * @property POINT
20821          * @type int
20822          * @static
20823          */
20824         POINT: 0,
20825
20826         /**
20827          * In intersect mode, drag and drop interactio nis defined by the
20828          * overlap of two or more drag and drop objects.
20829          * @property INTERSECT
20830          * @type int
20831          * @static
20832          */
20833         INTERSECT: 1,
20834
20835         /**
20836          * The current drag and drop mode.  Default: POINT
20837          * @property mode
20838          * @type int
20839          * @static
20840          */
20841         mode: 0,
20842
20843         /**
20844          * Runs method on all drag and drop objects
20845          * @method _execOnAll
20846          * @private
20847          * @static
20848          */
20849         _execOnAll: function(sMethod, args) {
20850             for (var i in this.ids) {
20851                 for (var j in this.ids[i]) {
20852                     var oDD = this.ids[i][j];
20853                     if (! this.isTypeOfDD(oDD)) {
20854                         continue;
20855                     }
20856                     oDD[sMethod].apply(oDD, args);
20857                 }
20858             }
20859         },
20860
20861         /**
20862          * Drag and drop initialization.  Sets up the global event handlers
20863          * @method _onLoad
20864          * @private
20865          * @static
20866          */
20867         _onLoad: function() {
20868
20869             this.init();
20870
20871             if (!Roo.isTouch) {
20872                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20873                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20874             }
20875             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20876             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20877             
20878             Event.on(window,   "unload",    this._onUnload, this, true);
20879             Event.on(window,   "resize",    this._onResize, this, true);
20880             // Event.on(window,   "mouseout",    this._test);
20881
20882         },
20883
20884         /**
20885          * Reset constraints on all drag and drop objs
20886          * @method _onResize
20887          * @private
20888          * @static
20889          */
20890         _onResize: function(e) {
20891             this._execOnAll("resetConstraints", []);
20892         },
20893
20894         /**
20895          * Lock all drag and drop functionality
20896          * @method lock
20897          * @static
20898          */
20899         lock: function() { this.locked = true; },
20900
20901         /**
20902          * Unlock all drag and drop functionality
20903          * @method unlock
20904          * @static
20905          */
20906         unlock: function() { this.locked = false; },
20907
20908         /**
20909          * Is drag and drop locked?
20910          * @method isLocked
20911          * @return {boolean} True if drag and drop is locked, false otherwise.
20912          * @static
20913          */
20914         isLocked: function() { return this.locked; },
20915
20916         /**
20917          * Location cache that is set for all drag drop objects when a drag is
20918          * initiated, cleared when the drag is finished.
20919          * @property locationCache
20920          * @private
20921          * @static
20922          */
20923         locationCache: {},
20924
20925         /**
20926          * Set useCache to false if you want to force object the lookup of each
20927          * drag and drop linked element constantly during a drag.
20928          * @property useCache
20929          * @type boolean
20930          * @static
20931          */
20932         useCache: true,
20933
20934         /**
20935          * The number of pixels that the mouse needs to move after the
20936          * mousedown before the drag is initiated.  Default=3;
20937          * @property clickPixelThresh
20938          * @type int
20939          * @static
20940          */
20941         clickPixelThresh: 3,
20942
20943         /**
20944          * The number of milliseconds after the mousedown event to initiate the
20945          * drag if we don't get a mouseup event. Default=1000
20946          * @property clickTimeThresh
20947          * @type int
20948          * @static
20949          */
20950         clickTimeThresh: 350,
20951
20952         /**
20953          * Flag that indicates that either the drag pixel threshold or the
20954          * mousdown time threshold has been met
20955          * @property dragThreshMet
20956          * @type boolean
20957          * @private
20958          * @static
20959          */
20960         dragThreshMet: false,
20961
20962         /**
20963          * Timeout used for the click time threshold
20964          * @property clickTimeout
20965          * @type Object
20966          * @private
20967          * @static
20968          */
20969         clickTimeout: null,
20970
20971         /**
20972          * The X position of the mousedown event stored for later use when a
20973          * drag threshold is met.
20974          * @property startX
20975          * @type int
20976          * @private
20977          * @static
20978          */
20979         startX: 0,
20980
20981         /**
20982          * The Y position of the mousedown event stored for later use when a
20983          * drag threshold is met.
20984          * @property startY
20985          * @type int
20986          * @private
20987          * @static
20988          */
20989         startY: 0,
20990
20991         /**
20992          * Each DragDrop instance must be registered with the DragDropMgr.
20993          * This is executed in DragDrop.init()
20994          * @method regDragDrop
20995          * @param {DragDrop} oDD the DragDrop object to register
20996          * @param {String} sGroup the name of the group this element belongs to
20997          * @static
20998          */
20999         regDragDrop: function(oDD, sGroup) {
21000             if (!this.initialized) { this.init(); }
21001
21002             if (!this.ids[sGroup]) {
21003                 this.ids[sGroup] = {};
21004             }
21005             this.ids[sGroup][oDD.id] = oDD;
21006         },
21007
21008         /**
21009          * Removes the supplied dd instance from the supplied group. Executed
21010          * by DragDrop.removeFromGroup, so don't call this function directly.
21011          * @method removeDDFromGroup
21012          * @private
21013          * @static
21014          */
21015         removeDDFromGroup: function(oDD, sGroup) {
21016             if (!this.ids[sGroup]) {
21017                 this.ids[sGroup] = {};
21018             }
21019
21020             var obj = this.ids[sGroup];
21021             if (obj && obj[oDD.id]) {
21022                 delete obj[oDD.id];
21023             }
21024         },
21025
21026         /**
21027          * Unregisters a drag and drop item.  This is executed in
21028          * DragDrop.unreg, use that method instead of calling this directly.
21029          * @method _remove
21030          * @private
21031          * @static
21032          */
21033         _remove: function(oDD) {
21034             for (var g in oDD.groups) {
21035                 if (g && this.ids[g][oDD.id]) {
21036                     delete this.ids[g][oDD.id];
21037                 }
21038             }
21039             delete this.handleIds[oDD.id];
21040         },
21041
21042         /**
21043          * Each DragDrop handle element must be registered.  This is done
21044          * automatically when executing DragDrop.setHandleElId()
21045          * @method regHandle
21046          * @param {String} sDDId the DragDrop id this element is a handle for
21047          * @param {String} sHandleId the id of the element that is the drag
21048          * handle
21049          * @static
21050          */
21051         regHandle: function(sDDId, sHandleId) {
21052             if (!this.handleIds[sDDId]) {
21053                 this.handleIds[sDDId] = {};
21054             }
21055             this.handleIds[sDDId][sHandleId] = sHandleId;
21056         },
21057
21058         /**
21059          * Utility function to determine if a given element has been
21060          * registered as a drag drop item.
21061          * @method isDragDrop
21062          * @param {String} id the element id to check
21063          * @return {boolean} true if this element is a DragDrop item,
21064          * false otherwise
21065          * @static
21066          */
21067         isDragDrop: function(id) {
21068             return ( this.getDDById(id) ) ? true : false;
21069         },
21070
21071         /**
21072          * Returns the drag and drop instances that are in all groups the
21073          * passed in instance belongs to.
21074          * @method getRelated
21075          * @param {DragDrop} p_oDD the obj to get related data for
21076          * @param {boolean} bTargetsOnly if true, only return targetable objs
21077          * @return {DragDrop[]} the related instances
21078          * @static
21079          */
21080         getRelated: function(p_oDD, bTargetsOnly) {
21081             var oDDs = [];
21082             for (var i in p_oDD.groups) {
21083                 for (j in this.ids[i]) {
21084                     var dd = this.ids[i][j];
21085                     if (! this.isTypeOfDD(dd)) {
21086                         continue;
21087                     }
21088                     if (!bTargetsOnly || dd.isTarget) {
21089                         oDDs[oDDs.length] = dd;
21090                     }
21091                 }
21092             }
21093
21094             return oDDs;
21095         },
21096
21097         /**
21098          * Returns true if the specified dd target is a legal target for
21099          * the specifice drag obj
21100          * @method isLegalTarget
21101          * @param {DragDrop} the drag obj
21102          * @param {DragDrop} the target
21103          * @return {boolean} true if the target is a legal target for the
21104          * dd obj
21105          * @static
21106          */
21107         isLegalTarget: function (oDD, oTargetDD) {
21108             var targets = this.getRelated(oDD, true);
21109             for (var i=0, len=targets.length;i<len;++i) {
21110                 if (targets[i].id == oTargetDD.id) {
21111                     return true;
21112                 }
21113             }
21114
21115             return false;
21116         },
21117
21118         /**
21119          * My goal is to be able to transparently determine if an object is
21120          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21121          * returns "object", oDD.constructor.toString() always returns
21122          * "DragDrop" and not the name of the subclass.  So for now it just
21123          * evaluates a well-known variable in DragDrop.
21124          * @method isTypeOfDD
21125          * @param {Object} the object to evaluate
21126          * @return {boolean} true if typeof oDD = DragDrop
21127          * @static
21128          */
21129         isTypeOfDD: function (oDD) {
21130             return (oDD && oDD.__ygDragDrop);
21131         },
21132
21133         /**
21134          * Utility function to determine if a given element has been
21135          * registered as a drag drop handle for the given Drag Drop object.
21136          * @method isHandle
21137          * @param {String} id the element id to check
21138          * @return {boolean} true if this element is a DragDrop handle, false
21139          * otherwise
21140          * @static
21141          */
21142         isHandle: function(sDDId, sHandleId) {
21143             return ( this.handleIds[sDDId] &&
21144                             this.handleIds[sDDId][sHandleId] );
21145         },
21146
21147         /**
21148          * Returns the DragDrop instance for a given id
21149          * @method getDDById
21150          * @param {String} id the id of the DragDrop object
21151          * @return {DragDrop} the drag drop object, null if it is not found
21152          * @static
21153          */
21154         getDDById: function(id) {
21155             for (var i in this.ids) {
21156                 if (this.ids[i][id]) {
21157                     return this.ids[i][id];
21158                 }
21159             }
21160             return null;
21161         },
21162
21163         /**
21164          * Fired after a registered DragDrop object gets the mousedown event.
21165          * Sets up the events required to track the object being dragged
21166          * @method handleMouseDown
21167          * @param {Event} e the event
21168          * @param oDD the DragDrop object being dragged
21169          * @private
21170          * @static
21171          */
21172         handleMouseDown: function(e, oDD) {
21173             if(Roo.QuickTips){
21174                 Roo.QuickTips.disable();
21175             }
21176             this.currentTarget = e.getTarget();
21177
21178             this.dragCurrent = oDD;
21179
21180             var el = oDD.getEl();
21181
21182             // track start position
21183             this.startX = e.getPageX();
21184             this.startY = e.getPageY();
21185
21186             this.deltaX = this.startX - el.offsetLeft;
21187             this.deltaY = this.startY - el.offsetTop;
21188
21189             this.dragThreshMet = false;
21190
21191             this.clickTimeout = setTimeout(
21192                     function() {
21193                         var DDM = Roo.dd.DDM;
21194                         DDM.startDrag(DDM.startX, DDM.startY);
21195                     },
21196                     this.clickTimeThresh );
21197         },
21198
21199         /**
21200          * Fired when either the drag pixel threshol or the mousedown hold
21201          * time threshold has been met.
21202          * @method startDrag
21203          * @param x {int} the X position of the original mousedown
21204          * @param y {int} the Y position of the original mousedown
21205          * @static
21206          */
21207         startDrag: function(x, y) {
21208             clearTimeout(this.clickTimeout);
21209             if (this.dragCurrent) {
21210                 this.dragCurrent.b4StartDrag(x, y);
21211                 this.dragCurrent.startDrag(x, y);
21212             }
21213             this.dragThreshMet = true;
21214         },
21215
21216         /**
21217          * Internal function to handle the mouseup event.  Will be invoked
21218          * from the context of the document.
21219          * @method handleMouseUp
21220          * @param {Event} e the event
21221          * @private
21222          * @static
21223          */
21224         handleMouseUp: function(e) {
21225
21226             if(Roo.QuickTips){
21227                 Roo.QuickTips.enable();
21228             }
21229             if (! this.dragCurrent) {
21230                 return;
21231             }
21232
21233             clearTimeout(this.clickTimeout);
21234
21235             if (this.dragThreshMet) {
21236                 this.fireEvents(e, true);
21237             } else {
21238             }
21239
21240             this.stopDrag(e);
21241
21242             this.stopEvent(e);
21243         },
21244
21245         /**
21246          * Utility to stop event propagation and event default, if these
21247          * features are turned on.
21248          * @method stopEvent
21249          * @param {Event} e the event as returned by this.getEvent()
21250          * @static
21251          */
21252         stopEvent: function(e){
21253             if(this.stopPropagation) {
21254                 e.stopPropagation();
21255             }
21256
21257             if (this.preventDefault) {
21258                 e.preventDefault();
21259             }
21260         },
21261
21262         /**
21263          * Internal function to clean up event handlers after the drag
21264          * operation is complete
21265          * @method stopDrag
21266          * @param {Event} e the event
21267          * @private
21268          * @static
21269          */
21270         stopDrag: function(e) {
21271             // Fire the drag end event for the item that was dragged
21272             if (this.dragCurrent) {
21273                 if (this.dragThreshMet) {
21274                     this.dragCurrent.b4EndDrag(e);
21275                     this.dragCurrent.endDrag(e);
21276                 }
21277
21278                 this.dragCurrent.onMouseUp(e);
21279             }
21280
21281             this.dragCurrent = null;
21282             this.dragOvers = {};
21283         },
21284
21285         /**
21286          * Internal function to handle the mousemove event.  Will be invoked
21287          * from the context of the html element.
21288          *
21289          * @TODO figure out what we can do about mouse events lost when the
21290          * user drags objects beyond the window boundary.  Currently we can
21291          * detect this in internet explorer by verifying that the mouse is
21292          * down during the mousemove event.  Firefox doesn't give us the
21293          * button state on the mousemove event.
21294          * @method handleMouseMove
21295          * @param {Event} e the event
21296          * @private
21297          * @static
21298          */
21299         handleMouseMove: function(e) {
21300             if (! this.dragCurrent) {
21301                 return true;
21302             }
21303
21304             // var button = e.which || e.button;
21305
21306             // check for IE mouseup outside of page boundary
21307             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21308                 this.stopEvent(e);
21309                 return this.handleMouseUp(e);
21310             }
21311
21312             if (!this.dragThreshMet) {
21313                 var diffX = Math.abs(this.startX - e.getPageX());
21314                 var diffY = Math.abs(this.startY - e.getPageY());
21315                 if (diffX > this.clickPixelThresh ||
21316                             diffY > this.clickPixelThresh) {
21317                     this.startDrag(this.startX, this.startY);
21318                 }
21319             }
21320
21321             if (this.dragThreshMet) {
21322                 this.dragCurrent.b4Drag(e);
21323                 this.dragCurrent.onDrag(e);
21324                 if(!this.dragCurrent.moveOnly){
21325                     this.fireEvents(e, false);
21326                 }
21327             }
21328
21329             this.stopEvent(e);
21330
21331             return true;
21332         },
21333
21334         /**
21335          * Iterates over all of the DragDrop elements to find ones we are
21336          * hovering over or dropping on
21337          * @method fireEvents
21338          * @param {Event} e the event
21339          * @param {boolean} isDrop is this a drop op or a mouseover op?
21340          * @private
21341          * @static
21342          */
21343         fireEvents: function(e, isDrop) {
21344             var dc = this.dragCurrent;
21345
21346             // If the user did the mouse up outside of the window, we could
21347             // get here even though we have ended the drag.
21348             if (!dc || dc.isLocked()) {
21349                 return;
21350             }
21351
21352             var pt = e.getPoint();
21353
21354             // cache the previous dragOver array
21355             var oldOvers = [];
21356
21357             var outEvts   = [];
21358             var overEvts  = [];
21359             var dropEvts  = [];
21360             var enterEvts = [];
21361
21362             // Check to see if the object(s) we were hovering over is no longer
21363             // being hovered over so we can fire the onDragOut event
21364             for (var i in this.dragOvers) {
21365
21366                 var ddo = this.dragOvers[i];
21367
21368                 if (! this.isTypeOfDD(ddo)) {
21369                     continue;
21370                 }
21371
21372                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21373                     outEvts.push( ddo );
21374                 }
21375
21376                 oldOvers[i] = true;
21377                 delete this.dragOvers[i];
21378             }
21379
21380             for (var sGroup in dc.groups) {
21381
21382                 if ("string" != typeof sGroup) {
21383                     continue;
21384                 }
21385
21386                 for (i in this.ids[sGroup]) {
21387                     var oDD = this.ids[sGroup][i];
21388                     if (! this.isTypeOfDD(oDD)) {
21389                         continue;
21390                     }
21391
21392                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21393                         if (this.isOverTarget(pt, oDD, this.mode)) {
21394                             // look for drop interactions
21395                             if (isDrop) {
21396                                 dropEvts.push( oDD );
21397                             // look for drag enter and drag over interactions
21398                             } else {
21399
21400                                 // initial drag over: dragEnter fires
21401                                 if (!oldOvers[oDD.id]) {
21402                                     enterEvts.push( oDD );
21403                                 // subsequent drag overs: dragOver fires
21404                                 } else {
21405                                     overEvts.push( oDD );
21406                                 }
21407
21408                                 this.dragOvers[oDD.id] = oDD;
21409                             }
21410                         }
21411                     }
21412                 }
21413             }
21414
21415             if (this.mode) {
21416                 if (outEvts.length) {
21417                     dc.b4DragOut(e, outEvts);
21418                     dc.onDragOut(e, outEvts);
21419                 }
21420
21421                 if (enterEvts.length) {
21422                     dc.onDragEnter(e, enterEvts);
21423                 }
21424
21425                 if (overEvts.length) {
21426                     dc.b4DragOver(e, overEvts);
21427                     dc.onDragOver(e, overEvts);
21428                 }
21429
21430                 if (dropEvts.length) {
21431                     dc.b4DragDrop(e, dropEvts);
21432                     dc.onDragDrop(e, dropEvts);
21433                 }
21434
21435             } else {
21436                 // fire dragout events
21437                 var len = 0;
21438                 for (i=0, len=outEvts.length; i<len; ++i) {
21439                     dc.b4DragOut(e, outEvts[i].id);
21440                     dc.onDragOut(e, outEvts[i].id);
21441                 }
21442
21443                 // fire enter events
21444                 for (i=0,len=enterEvts.length; i<len; ++i) {
21445                     // dc.b4DragEnter(e, oDD.id);
21446                     dc.onDragEnter(e, enterEvts[i].id);
21447                 }
21448
21449                 // fire over events
21450                 for (i=0,len=overEvts.length; i<len; ++i) {
21451                     dc.b4DragOver(e, overEvts[i].id);
21452                     dc.onDragOver(e, overEvts[i].id);
21453                 }
21454
21455                 // fire drop events
21456                 for (i=0, len=dropEvts.length; i<len; ++i) {
21457                     dc.b4DragDrop(e, dropEvts[i].id);
21458                     dc.onDragDrop(e, dropEvts[i].id);
21459                 }
21460
21461             }
21462
21463             // notify about a drop that did not find a target
21464             if (isDrop && !dropEvts.length) {
21465                 dc.onInvalidDrop(e);
21466             }
21467
21468         },
21469
21470         /**
21471          * Helper function for getting the best match from the list of drag
21472          * and drop objects returned by the drag and drop events when we are
21473          * in INTERSECT mode.  It returns either the first object that the
21474          * cursor is over, or the object that has the greatest overlap with
21475          * the dragged element.
21476          * @method getBestMatch
21477          * @param  {DragDrop[]} dds The array of drag and drop objects
21478          * targeted
21479          * @return {DragDrop}       The best single match
21480          * @static
21481          */
21482         getBestMatch: function(dds) {
21483             var winner = null;
21484             // Return null if the input is not what we expect
21485             //if (!dds || !dds.length || dds.length == 0) {
21486                // winner = null;
21487             // If there is only one item, it wins
21488             //} else if (dds.length == 1) {
21489
21490             var len = dds.length;
21491
21492             if (len == 1) {
21493                 winner = dds[0];
21494             } else {
21495                 // Loop through the targeted items
21496                 for (var i=0; i<len; ++i) {
21497                     var dd = dds[i];
21498                     // If the cursor is over the object, it wins.  If the
21499                     // cursor is over multiple matches, the first one we come
21500                     // to wins.
21501                     if (dd.cursorIsOver) {
21502                         winner = dd;
21503                         break;
21504                     // Otherwise the object with the most overlap wins
21505                     } else {
21506                         if (!winner ||
21507                             winner.overlap.getArea() < dd.overlap.getArea()) {
21508                             winner = dd;
21509                         }
21510                     }
21511                 }
21512             }
21513
21514             return winner;
21515         },
21516
21517         /**
21518          * Refreshes the cache of the top-left and bottom-right points of the
21519          * drag and drop objects in the specified group(s).  This is in the
21520          * format that is stored in the drag and drop instance, so typical
21521          * usage is:
21522          * <code>
21523          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21524          * </code>
21525          * Alternatively:
21526          * <code>
21527          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21528          * </code>
21529          * @TODO this really should be an indexed array.  Alternatively this
21530          * method could accept both.
21531          * @method refreshCache
21532          * @param {Object} groups an associative array of groups to refresh
21533          * @static
21534          */
21535         refreshCache: function(groups) {
21536             for (var sGroup in groups) {
21537                 if ("string" != typeof sGroup) {
21538                     continue;
21539                 }
21540                 for (var i in this.ids[sGroup]) {
21541                     var oDD = this.ids[sGroup][i];
21542
21543                     if (this.isTypeOfDD(oDD)) {
21544                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21545                         var loc = this.getLocation(oDD);
21546                         if (loc) {
21547                             this.locationCache[oDD.id] = loc;
21548                         } else {
21549                             delete this.locationCache[oDD.id];
21550                             // this will unregister the drag and drop object if
21551                             // the element is not in a usable state
21552                             // oDD.unreg();
21553                         }
21554                     }
21555                 }
21556             }
21557         },
21558
21559         /**
21560          * This checks to make sure an element exists and is in the DOM.  The
21561          * main purpose is to handle cases where innerHTML is used to remove
21562          * drag and drop objects from the DOM.  IE provides an 'unspecified
21563          * error' when trying to access the offsetParent of such an element
21564          * @method verifyEl
21565          * @param {HTMLElement} el the element to check
21566          * @return {boolean} true if the element looks usable
21567          * @static
21568          */
21569         verifyEl: function(el) {
21570             if (el) {
21571                 var parent;
21572                 if(Roo.isIE){
21573                     try{
21574                         parent = el.offsetParent;
21575                     }catch(e){}
21576                 }else{
21577                     parent = el.offsetParent;
21578                 }
21579                 if (parent) {
21580                     return true;
21581                 }
21582             }
21583
21584             return false;
21585         },
21586
21587         /**
21588          * Returns a Region object containing the drag and drop element's position
21589          * and size, including the padding configured for it
21590          * @method getLocation
21591          * @param {DragDrop} oDD the drag and drop object to get the
21592          *                       location for
21593          * @return {Roo.lib.Region} a Region object representing the total area
21594          *                             the element occupies, including any padding
21595          *                             the instance is configured for.
21596          * @static
21597          */
21598         getLocation: function(oDD) {
21599             if (! this.isTypeOfDD(oDD)) {
21600                 return null;
21601             }
21602
21603             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21604
21605             try {
21606                 pos= Roo.lib.Dom.getXY(el);
21607             } catch (e) { }
21608
21609             if (!pos) {
21610                 return null;
21611             }
21612
21613             x1 = pos[0];
21614             x2 = x1 + el.offsetWidth;
21615             y1 = pos[1];
21616             y2 = y1 + el.offsetHeight;
21617
21618             t = y1 - oDD.padding[0];
21619             r = x2 + oDD.padding[1];
21620             b = y2 + oDD.padding[2];
21621             l = x1 - oDD.padding[3];
21622
21623             return new Roo.lib.Region( t, r, b, l );
21624         },
21625
21626         /**
21627          * Checks the cursor location to see if it over the target
21628          * @method isOverTarget
21629          * @param {Roo.lib.Point} pt The point to evaluate
21630          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21631          * @return {boolean} true if the mouse is over the target
21632          * @private
21633          * @static
21634          */
21635         isOverTarget: function(pt, oTarget, intersect) {
21636             // use cache if available
21637             var loc = this.locationCache[oTarget.id];
21638             if (!loc || !this.useCache) {
21639                 loc = this.getLocation(oTarget);
21640                 this.locationCache[oTarget.id] = loc;
21641
21642             }
21643
21644             if (!loc) {
21645                 return false;
21646             }
21647
21648             oTarget.cursorIsOver = loc.contains( pt );
21649
21650             // DragDrop is using this as a sanity check for the initial mousedown
21651             // in this case we are done.  In POINT mode, if the drag obj has no
21652             // contraints, we are also done. Otherwise we need to evaluate the
21653             // location of the target as related to the actual location of the
21654             // dragged element.
21655             var dc = this.dragCurrent;
21656             if (!dc || !dc.getTargetCoord ||
21657                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21658                 return oTarget.cursorIsOver;
21659             }
21660
21661             oTarget.overlap = null;
21662
21663             // Get the current location of the drag element, this is the
21664             // location of the mouse event less the delta that represents
21665             // where the original mousedown happened on the element.  We
21666             // need to consider constraints and ticks as well.
21667             var pos = dc.getTargetCoord(pt.x, pt.y);
21668
21669             var el = dc.getDragEl();
21670             var curRegion = new Roo.lib.Region( pos.y,
21671                                                    pos.x + el.offsetWidth,
21672                                                    pos.y + el.offsetHeight,
21673                                                    pos.x );
21674
21675             var overlap = curRegion.intersect(loc);
21676
21677             if (overlap) {
21678                 oTarget.overlap = overlap;
21679                 return (intersect) ? true : oTarget.cursorIsOver;
21680             } else {
21681                 return false;
21682             }
21683         },
21684
21685         /**
21686          * unload event handler
21687          * @method _onUnload
21688          * @private
21689          * @static
21690          */
21691         _onUnload: function(e, me) {
21692             Roo.dd.DragDropMgr.unregAll();
21693         },
21694
21695         /**
21696          * Cleans up the drag and drop events and objects.
21697          * @method unregAll
21698          * @private
21699          * @static
21700          */
21701         unregAll: function() {
21702
21703             if (this.dragCurrent) {
21704                 this.stopDrag();
21705                 this.dragCurrent = null;
21706             }
21707
21708             this._execOnAll("unreg", []);
21709
21710             for (i in this.elementCache) {
21711                 delete this.elementCache[i];
21712             }
21713
21714             this.elementCache = {};
21715             this.ids = {};
21716         },
21717
21718         /**
21719          * A cache of DOM elements
21720          * @property elementCache
21721          * @private
21722          * @static
21723          */
21724         elementCache: {},
21725
21726         /**
21727          * Get the wrapper for the DOM element specified
21728          * @method getElWrapper
21729          * @param {String} id the id of the element to get
21730          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21731          * @private
21732          * @deprecated This wrapper isn't that useful
21733          * @static
21734          */
21735         getElWrapper: function(id) {
21736             var oWrapper = this.elementCache[id];
21737             if (!oWrapper || !oWrapper.el) {
21738                 oWrapper = this.elementCache[id] =
21739                     new this.ElementWrapper(Roo.getDom(id));
21740             }
21741             return oWrapper;
21742         },
21743
21744         /**
21745          * Returns the actual DOM element
21746          * @method getElement
21747          * @param {String} id the id of the elment to get
21748          * @return {Object} The element
21749          * @deprecated use Roo.getDom instead
21750          * @static
21751          */
21752         getElement: function(id) {
21753             return Roo.getDom(id);
21754         },
21755
21756         /**
21757          * Returns the style property for the DOM element (i.e.,
21758          * document.getElById(id).style)
21759          * @method getCss
21760          * @param {String} id the id of the elment to get
21761          * @return {Object} The style property of the element
21762          * @deprecated use Roo.getDom instead
21763          * @static
21764          */
21765         getCss: function(id) {
21766             var el = Roo.getDom(id);
21767             return (el) ? el.style : null;
21768         },
21769
21770         /**
21771          * Inner class for cached elements
21772          * @class DragDropMgr.ElementWrapper
21773          * @for DragDropMgr
21774          * @private
21775          * @deprecated
21776          */
21777         ElementWrapper: function(el) {
21778                 /**
21779                  * The element
21780                  * @property el
21781                  */
21782                 this.el = el || null;
21783                 /**
21784                  * The element id
21785                  * @property id
21786                  */
21787                 this.id = this.el && el.id;
21788                 /**
21789                  * A reference to the style property
21790                  * @property css
21791                  */
21792                 this.css = this.el && el.style;
21793             },
21794
21795         /**
21796          * Returns the X position of an html element
21797          * @method getPosX
21798          * @param el the element for which to get the position
21799          * @return {int} the X coordinate
21800          * @for DragDropMgr
21801          * @deprecated use Roo.lib.Dom.getX instead
21802          * @static
21803          */
21804         getPosX: function(el) {
21805             return Roo.lib.Dom.getX(el);
21806         },
21807
21808         /**
21809          * Returns the Y position of an html element
21810          * @method getPosY
21811          * @param el the element for which to get the position
21812          * @return {int} the Y coordinate
21813          * @deprecated use Roo.lib.Dom.getY instead
21814          * @static
21815          */
21816         getPosY: function(el) {
21817             return Roo.lib.Dom.getY(el);
21818         },
21819
21820         /**
21821          * Swap two nodes.  In IE, we use the native method, for others we
21822          * emulate the IE behavior
21823          * @method swapNode
21824          * @param n1 the first node to swap
21825          * @param n2 the other node to swap
21826          * @static
21827          */
21828         swapNode: function(n1, n2) {
21829             if (n1.swapNode) {
21830                 n1.swapNode(n2);
21831             } else {
21832                 var p = n2.parentNode;
21833                 var s = n2.nextSibling;
21834
21835                 if (s == n1) {
21836                     p.insertBefore(n1, n2);
21837                 } else if (n2 == n1.nextSibling) {
21838                     p.insertBefore(n2, n1);
21839                 } else {
21840                     n1.parentNode.replaceChild(n2, n1);
21841                     p.insertBefore(n1, s);
21842                 }
21843             }
21844         },
21845
21846         /**
21847          * Returns the current scroll position
21848          * @method getScroll
21849          * @private
21850          * @static
21851          */
21852         getScroll: function () {
21853             var t, l, dde=document.documentElement, db=document.body;
21854             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21855                 t = dde.scrollTop;
21856                 l = dde.scrollLeft;
21857             } else if (db) {
21858                 t = db.scrollTop;
21859                 l = db.scrollLeft;
21860             } else {
21861
21862             }
21863             return { top: t, left: l };
21864         },
21865
21866         /**
21867          * Returns the specified element style property
21868          * @method getStyle
21869          * @param {HTMLElement} el          the element
21870          * @param {string}      styleProp   the style property
21871          * @return {string} The value of the style property
21872          * @deprecated use Roo.lib.Dom.getStyle
21873          * @static
21874          */
21875         getStyle: function(el, styleProp) {
21876             return Roo.fly(el).getStyle(styleProp);
21877         },
21878
21879         /**
21880          * Gets the scrollTop
21881          * @method getScrollTop
21882          * @return {int} the document's scrollTop
21883          * @static
21884          */
21885         getScrollTop: function () { return this.getScroll().top; },
21886
21887         /**
21888          * Gets the scrollLeft
21889          * @method getScrollLeft
21890          * @return {int} the document's scrollTop
21891          * @static
21892          */
21893         getScrollLeft: function () { return this.getScroll().left; },
21894
21895         /**
21896          * Sets the x/y position of an element to the location of the
21897          * target element.
21898          * @method moveToEl
21899          * @param {HTMLElement} moveEl      The element to move
21900          * @param {HTMLElement} targetEl    The position reference element
21901          * @static
21902          */
21903         moveToEl: function (moveEl, targetEl) {
21904             var aCoord = Roo.lib.Dom.getXY(targetEl);
21905             Roo.lib.Dom.setXY(moveEl, aCoord);
21906         },
21907
21908         /**
21909          * Numeric array sort function
21910          * @method numericSort
21911          * @static
21912          */
21913         numericSort: function(a, b) { return (a - b); },
21914
21915         /**
21916          * Internal counter
21917          * @property _timeoutCount
21918          * @private
21919          * @static
21920          */
21921         _timeoutCount: 0,
21922
21923         /**
21924          * Trying to make the load order less important.  Without this we get
21925          * an error if this file is loaded before the Event Utility.
21926          * @method _addListeners
21927          * @private
21928          * @static
21929          */
21930         _addListeners: function() {
21931             var DDM = Roo.dd.DDM;
21932             if ( Roo.lib.Event && document ) {
21933                 DDM._onLoad();
21934             } else {
21935                 if (DDM._timeoutCount > 2000) {
21936                 } else {
21937                     setTimeout(DDM._addListeners, 10);
21938                     if (document && document.body) {
21939                         DDM._timeoutCount += 1;
21940                     }
21941                 }
21942             }
21943         },
21944
21945         /**
21946          * Recursively searches the immediate parent and all child nodes for
21947          * the handle element in order to determine wheter or not it was
21948          * clicked.
21949          * @method handleWasClicked
21950          * @param node the html element to inspect
21951          * @static
21952          */
21953         handleWasClicked: function(node, id) {
21954             if (this.isHandle(id, node.id)) {
21955                 return true;
21956             } else {
21957                 // check to see if this is a text node child of the one we want
21958                 var p = node.parentNode;
21959
21960                 while (p) {
21961                     if (this.isHandle(id, p.id)) {
21962                         return true;
21963                     } else {
21964                         p = p.parentNode;
21965                     }
21966                 }
21967             }
21968
21969             return false;
21970         }
21971
21972     };
21973
21974 }();
21975
21976 // shorter alias, save a few bytes
21977 Roo.dd.DDM = Roo.dd.DragDropMgr;
21978 Roo.dd.DDM._addListeners();
21979
21980 }/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990
21991 /**
21992  * @class Roo.dd.DD
21993  * A DragDrop implementation where the linked element follows the
21994  * mouse cursor during a drag.
21995  * @extends Roo.dd.DragDrop
21996  * @constructor
21997  * @param {String} id the id of the linked element
21998  * @param {String} sGroup the group of related DragDrop items
21999  * @param {object} config an object containing configurable attributes
22000  *                Valid properties for DD:
22001  *                    scroll
22002  */
22003 Roo.dd.DD = function(id, sGroup, config) {
22004     if (id) {
22005         this.init(id, sGroup, config);
22006     }
22007 };
22008
22009 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22010
22011     /**
22012      * When set to true, the utility automatically tries to scroll the browser
22013      * window wehn a drag and drop element is dragged near the viewport boundary.
22014      * Defaults to true.
22015      * @property scroll
22016      * @type boolean
22017      */
22018     scroll: true,
22019
22020     /**
22021      * Sets the pointer offset to the distance between the linked element's top
22022      * left corner and the location the element was clicked
22023      * @method autoOffset
22024      * @param {int} iPageX the X coordinate of the click
22025      * @param {int} iPageY the Y coordinate of the click
22026      */
22027     autoOffset: function(iPageX, iPageY) {
22028         var x = iPageX - this.startPageX;
22029         var y = iPageY - this.startPageY;
22030         this.setDelta(x, y);
22031     },
22032
22033     /**
22034      * Sets the pointer offset.  You can call this directly to force the
22035      * offset to be in a particular location (e.g., pass in 0,0 to set it
22036      * to the center of the object)
22037      * @method setDelta
22038      * @param {int} iDeltaX the distance from the left
22039      * @param {int} iDeltaY the distance from the top
22040      */
22041     setDelta: function(iDeltaX, iDeltaY) {
22042         this.deltaX = iDeltaX;
22043         this.deltaY = iDeltaY;
22044     },
22045
22046     /**
22047      * Sets the drag element to the location of the mousedown or click event,
22048      * maintaining the cursor location relative to the location on the element
22049      * that was clicked.  Override this if you want to place the element in a
22050      * location other than where the cursor is.
22051      * @method setDragElPos
22052      * @param {int} iPageX the X coordinate of the mousedown or drag event
22053      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22054      */
22055     setDragElPos: function(iPageX, iPageY) {
22056         // the first time we do this, we are going to check to make sure
22057         // the element has css positioning
22058
22059         var el = this.getDragEl();
22060         this.alignElWithMouse(el, iPageX, iPageY);
22061     },
22062
22063     /**
22064      * Sets the element to the location of the mousedown or click event,
22065      * maintaining the cursor location relative to the location on the element
22066      * that was clicked.  Override this if you want to place the element in a
22067      * location other than where the cursor is.
22068      * @method alignElWithMouse
22069      * @param {HTMLElement} el the element to move
22070      * @param {int} iPageX the X coordinate of the mousedown or drag event
22071      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22072      */
22073     alignElWithMouse: function(el, iPageX, iPageY) {
22074         var oCoord = this.getTargetCoord(iPageX, iPageY);
22075         var fly = el.dom ? el : Roo.fly(el);
22076         if (!this.deltaSetXY) {
22077             var aCoord = [oCoord.x, oCoord.y];
22078             fly.setXY(aCoord);
22079             var newLeft = fly.getLeft(true);
22080             var newTop  = fly.getTop(true);
22081             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22082         } else {
22083             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22084         }
22085
22086         this.cachePosition(oCoord.x, oCoord.y);
22087         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22088         return oCoord;
22089     },
22090
22091     /**
22092      * Saves the most recent position so that we can reset the constraints and
22093      * tick marks on-demand.  We need to know this so that we can calculate the
22094      * number of pixels the element is offset from its original position.
22095      * @method cachePosition
22096      * @param iPageX the current x position (optional, this just makes it so we
22097      * don't have to look it up again)
22098      * @param iPageY the current y position (optional, this just makes it so we
22099      * don't have to look it up again)
22100      */
22101     cachePosition: function(iPageX, iPageY) {
22102         if (iPageX) {
22103             this.lastPageX = iPageX;
22104             this.lastPageY = iPageY;
22105         } else {
22106             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22107             this.lastPageX = aCoord[0];
22108             this.lastPageY = aCoord[1];
22109         }
22110     },
22111
22112     /**
22113      * Auto-scroll the window if the dragged object has been moved beyond the
22114      * visible window boundary.
22115      * @method autoScroll
22116      * @param {int} x the drag element's x position
22117      * @param {int} y the drag element's y position
22118      * @param {int} h the height of the drag element
22119      * @param {int} w the width of the drag element
22120      * @private
22121      */
22122     autoScroll: function(x, y, h, w) {
22123
22124         if (this.scroll) {
22125             // The client height
22126             var clientH = Roo.lib.Dom.getViewWidth();
22127
22128             // The client width
22129             var clientW = Roo.lib.Dom.getViewHeight();
22130
22131             // The amt scrolled down
22132             var st = this.DDM.getScrollTop();
22133
22134             // The amt scrolled right
22135             var sl = this.DDM.getScrollLeft();
22136
22137             // Location of the bottom of the element
22138             var bot = h + y;
22139
22140             // Location of the right of the element
22141             var right = w + x;
22142
22143             // The distance from the cursor to the bottom of the visible area,
22144             // adjusted so that we don't scroll if the cursor is beyond the
22145             // element drag constraints
22146             var toBot = (clientH + st - y - this.deltaY);
22147
22148             // The distance from the cursor to the right of the visible area
22149             var toRight = (clientW + sl - x - this.deltaX);
22150
22151
22152             // How close to the edge the cursor must be before we scroll
22153             // var thresh = (document.all) ? 100 : 40;
22154             var thresh = 40;
22155
22156             // How many pixels to scroll per autoscroll op.  This helps to reduce
22157             // clunky scrolling. IE is more sensitive about this ... it needs this
22158             // value to be higher.
22159             var scrAmt = (document.all) ? 80 : 30;
22160
22161             // Scroll down if we are near the bottom of the visible page and the
22162             // obj extends below the crease
22163             if ( bot > clientH && toBot < thresh ) {
22164                 window.scrollTo(sl, st + scrAmt);
22165             }
22166
22167             // Scroll up if the window is scrolled down and the top of the object
22168             // goes above the top border
22169             if ( y < st && st > 0 && y - st < thresh ) {
22170                 window.scrollTo(sl, st - scrAmt);
22171             }
22172
22173             // Scroll right if the obj is beyond the right border and the cursor is
22174             // near the border.
22175             if ( right > clientW && toRight < thresh ) {
22176                 window.scrollTo(sl + scrAmt, st);
22177             }
22178
22179             // Scroll left if the window has been scrolled to the right and the obj
22180             // extends past the left border
22181             if ( x < sl && sl > 0 && x - sl < thresh ) {
22182                 window.scrollTo(sl - scrAmt, st);
22183             }
22184         }
22185     },
22186
22187     /**
22188      * Finds the location the element should be placed if we want to move
22189      * it to where the mouse location less the click offset would place us.
22190      * @method getTargetCoord
22191      * @param {int} iPageX the X coordinate of the click
22192      * @param {int} iPageY the Y coordinate of the click
22193      * @return an object that contains the coordinates (Object.x and Object.y)
22194      * @private
22195      */
22196     getTargetCoord: function(iPageX, iPageY) {
22197
22198
22199         var x = iPageX - this.deltaX;
22200         var y = iPageY - this.deltaY;
22201
22202         if (this.constrainX) {
22203             if (x < this.minX) { x = this.minX; }
22204             if (x > this.maxX) { x = this.maxX; }
22205         }
22206
22207         if (this.constrainY) {
22208             if (y < this.minY) { y = this.minY; }
22209             if (y > this.maxY) { y = this.maxY; }
22210         }
22211
22212         x = this.getTick(x, this.xTicks);
22213         y = this.getTick(y, this.yTicks);
22214
22215
22216         return {x:x, y:y};
22217     },
22218
22219     /*
22220      * Sets up config options specific to this class. Overrides
22221      * Roo.dd.DragDrop, but all versions of this method through the
22222      * inheritance chain are called
22223      */
22224     applyConfig: function() {
22225         Roo.dd.DD.superclass.applyConfig.call(this);
22226         this.scroll = (this.config.scroll !== false);
22227     },
22228
22229     /*
22230      * Event that fires prior to the onMouseDown event.  Overrides
22231      * Roo.dd.DragDrop.
22232      */
22233     b4MouseDown: function(e) {
22234         // this.resetConstraints();
22235         this.autoOffset(e.getPageX(),
22236                             e.getPageY());
22237     },
22238
22239     /*
22240      * Event that fires prior to the onDrag event.  Overrides
22241      * Roo.dd.DragDrop.
22242      */
22243     b4Drag: function(e) {
22244         this.setDragElPos(e.getPageX(),
22245                             e.getPageY());
22246     },
22247
22248     toString: function() {
22249         return ("DD " + this.id);
22250     }
22251
22252     //////////////////////////////////////////////////////////////////////////
22253     // Debugging ygDragDrop events that can be overridden
22254     //////////////////////////////////////////////////////////////////////////
22255     /*
22256     startDrag: function(x, y) {
22257     },
22258
22259     onDrag: function(e) {
22260     },
22261
22262     onDragEnter: function(e, id) {
22263     },
22264
22265     onDragOver: function(e, id) {
22266     },
22267
22268     onDragOut: function(e, id) {
22269     },
22270
22271     onDragDrop: function(e, id) {
22272     },
22273
22274     endDrag: function(e) {
22275     }
22276
22277     */
22278
22279 });/*
22280  * Based on:
22281  * Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  *
22284  * Originally Released Under LGPL - original licence link has changed is not relivant.
22285  *
22286  * Fork - LGPL
22287  * <script type="text/javascript">
22288  */
22289
22290 /**
22291  * @class Roo.dd.DDProxy
22292  * A DragDrop implementation that inserts an empty, bordered div into
22293  * the document that follows the cursor during drag operations.  At the time of
22294  * the click, the frame div is resized to the dimensions of the linked html
22295  * element, and moved to the exact location of the linked element.
22296  *
22297  * References to the "frame" element refer to the single proxy element that
22298  * was created to be dragged in place of all DDProxy elements on the
22299  * page.
22300  *
22301  * @extends Roo.dd.DD
22302  * @constructor
22303  * @param {String} id the id of the linked html element
22304  * @param {String} sGroup the group of related DragDrop objects
22305  * @param {object} config an object containing configurable attributes
22306  *                Valid properties for DDProxy in addition to those in DragDrop:
22307  *                   resizeFrame, centerFrame, dragElId
22308  */
22309 Roo.dd.DDProxy = function(id, sGroup, config) {
22310     if (id) {
22311         this.init(id, sGroup, config);
22312         this.initFrame();
22313     }
22314 };
22315
22316 /**
22317  * The default drag frame div id
22318  * @property Roo.dd.DDProxy.dragElId
22319  * @type String
22320  * @static
22321  */
22322 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22323
22324 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22325
22326     /**
22327      * By default we resize the drag frame to be the same size as the element
22328      * we want to drag (this is to get the frame effect).  We can turn it off
22329      * if we want a different behavior.
22330      * @property resizeFrame
22331      * @type boolean
22332      */
22333     resizeFrame: true,
22334
22335     /**
22336      * By default the frame is positioned exactly where the drag element is, so
22337      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22338      * you do not have constraints on the obj is to have the drag frame centered
22339      * around the cursor.  Set centerFrame to true for this effect.
22340      * @property centerFrame
22341      * @type boolean
22342      */
22343     centerFrame: false,
22344
22345     /**
22346      * Creates the proxy element if it does not yet exist
22347      * @method createFrame
22348      */
22349     createFrame: function() {
22350         var self = this;
22351         var body = document.body;
22352
22353         if (!body || !body.firstChild) {
22354             setTimeout( function() { self.createFrame(); }, 50 );
22355             return;
22356         }
22357
22358         var div = this.getDragEl();
22359
22360         if (!div) {
22361             div    = document.createElement("div");
22362             div.id = this.dragElId;
22363             var s  = div.style;
22364
22365             s.position   = "absolute";
22366             s.visibility = "hidden";
22367             s.cursor     = "move";
22368             s.border     = "2px solid #aaa";
22369             s.zIndex     = 999;
22370
22371             // appendChild can blow up IE if invoked prior to the window load event
22372             // while rendering a table.  It is possible there are other scenarios
22373             // that would cause this to happen as well.
22374             body.insertBefore(div, body.firstChild);
22375         }
22376     },
22377
22378     /**
22379      * Initialization for the drag frame element.  Must be called in the
22380      * constructor of all subclasses
22381      * @method initFrame
22382      */
22383     initFrame: function() {
22384         this.createFrame();
22385     },
22386
22387     applyConfig: function() {
22388         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22389
22390         this.resizeFrame = (this.config.resizeFrame !== false);
22391         this.centerFrame = (this.config.centerFrame);
22392         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22393     },
22394
22395     /**
22396      * Resizes the drag frame to the dimensions of the clicked object, positions
22397      * it over the object, and finally displays it
22398      * @method showFrame
22399      * @param {int} iPageX X click position
22400      * @param {int} iPageY Y click position
22401      * @private
22402      */
22403     showFrame: function(iPageX, iPageY) {
22404         var el = this.getEl();
22405         var dragEl = this.getDragEl();
22406         var s = dragEl.style;
22407
22408         this._resizeProxy();
22409
22410         if (this.centerFrame) {
22411             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22412                            Math.round(parseInt(s.height, 10)/2) );
22413         }
22414
22415         this.setDragElPos(iPageX, iPageY);
22416
22417         Roo.fly(dragEl).show();
22418     },
22419
22420     /**
22421      * The proxy is automatically resized to the dimensions of the linked
22422      * element when a drag is initiated, unless resizeFrame is set to false
22423      * @method _resizeProxy
22424      * @private
22425      */
22426     _resizeProxy: function() {
22427         if (this.resizeFrame) {
22428             var el = this.getEl();
22429             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22430         }
22431     },
22432
22433     // overrides Roo.dd.DragDrop
22434     b4MouseDown: function(e) {
22435         var x = e.getPageX();
22436         var y = e.getPageY();
22437         this.autoOffset(x, y);
22438         this.setDragElPos(x, y);
22439     },
22440
22441     // overrides Roo.dd.DragDrop
22442     b4StartDrag: function(x, y) {
22443         // show the drag frame
22444         this.showFrame(x, y);
22445     },
22446
22447     // overrides Roo.dd.DragDrop
22448     b4EndDrag: function(e) {
22449         Roo.fly(this.getDragEl()).hide();
22450     },
22451
22452     // overrides Roo.dd.DragDrop
22453     // By default we try to move the element to the last location of the frame.
22454     // This is so that the default behavior mirrors that of Roo.dd.DD.
22455     endDrag: function(e) {
22456
22457         var lel = this.getEl();
22458         var del = this.getDragEl();
22459
22460         // Show the drag frame briefly so we can get its position
22461         del.style.visibility = "";
22462
22463         this.beforeMove();
22464         // Hide the linked element before the move to get around a Safari
22465         // rendering bug.
22466         lel.style.visibility = "hidden";
22467         Roo.dd.DDM.moveToEl(lel, del);
22468         del.style.visibility = "hidden";
22469         lel.style.visibility = "";
22470
22471         this.afterDrag();
22472     },
22473
22474     beforeMove : function(){
22475
22476     },
22477
22478     afterDrag : function(){
22479
22480     },
22481
22482     toString: function() {
22483         return ("DDProxy " + this.id);
22484     }
22485
22486 });
22487 /*
22488  * Based on:
22489  * Ext JS Library 1.1.1
22490  * Copyright(c) 2006-2007, Ext JS, LLC.
22491  *
22492  * Originally Released Under LGPL - original licence link has changed is not relivant.
22493  *
22494  * Fork - LGPL
22495  * <script type="text/javascript">
22496  */
22497
22498  /**
22499  * @class Roo.dd.DDTarget
22500  * A DragDrop implementation that does not move, but can be a drop
22501  * target.  You would get the same result by simply omitting implementation
22502  * for the event callbacks, but this way we reduce the processing cost of the
22503  * event listener and the callbacks.
22504  * @extends Roo.dd.DragDrop
22505  * @constructor
22506  * @param {String} id the id of the element that is a drop target
22507  * @param {String} sGroup the group of related DragDrop objects
22508  * @param {object} config an object containing configurable attributes
22509  *                 Valid properties for DDTarget in addition to those in
22510  *                 DragDrop:
22511  *                    none
22512  */
22513 Roo.dd.DDTarget = function(id, sGroup, config) {
22514     if (id) {
22515         this.initTarget(id, sGroup, config);
22516     }
22517     if (config && (config.listeners || config.events)) { 
22518         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22519             listeners : config.listeners || {}, 
22520             events : config.events || {} 
22521         });    
22522     }
22523 };
22524
22525 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22526 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22527     toString: function() {
22528         return ("DDTarget " + this.id);
22529     }
22530 });
22531 /*
22532  * Based on:
22533  * Ext JS Library 1.1.1
22534  * Copyright(c) 2006-2007, Ext JS, LLC.
22535  *
22536  * Originally Released Under LGPL - original licence link has changed is not relivant.
22537  *
22538  * Fork - LGPL
22539  * <script type="text/javascript">
22540  */
22541  
22542
22543 /**
22544  * @class Roo.dd.ScrollManager
22545  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22546  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22547  * @static
22548  */
22549 Roo.dd.ScrollManager = function(){
22550     var ddm = Roo.dd.DragDropMgr;
22551     var els = {};
22552     var dragEl = null;
22553     var proc = {};
22554     
22555     
22556     
22557     var onStop = function(e){
22558         dragEl = null;
22559         clearProc();
22560     };
22561     
22562     var triggerRefresh = function(){
22563         if(ddm.dragCurrent){
22564              ddm.refreshCache(ddm.dragCurrent.groups);
22565         }
22566     };
22567     
22568     var doScroll = function(){
22569         if(ddm.dragCurrent){
22570             var dds = Roo.dd.ScrollManager;
22571             if(!dds.animate){
22572                 if(proc.el.scroll(proc.dir, dds.increment)){
22573                     triggerRefresh();
22574                 }
22575             }else{
22576                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22577             }
22578         }
22579     };
22580     
22581     var clearProc = function(){
22582         if(proc.id){
22583             clearInterval(proc.id);
22584         }
22585         proc.id = 0;
22586         proc.el = null;
22587         proc.dir = "";
22588     };
22589     
22590     var startProc = function(el, dir){
22591          Roo.log('scroll startproc');
22592         clearProc();
22593         proc.el = el;
22594         proc.dir = dir;
22595         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22596     };
22597     
22598     var onFire = function(e, isDrop){
22599        
22600         if(isDrop || !ddm.dragCurrent){ return; }
22601         var dds = Roo.dd.ScrollManager;
22602         if(!dragEl || dragEl != ddm.dragCurrent){
22603             dragEl = ddm.dragCurrent;
22604             // refresh regions on drag start
22605             dds.refreshCache();
22606         }
22607         
22608         var xy = Roo.lib.Event.getXY(e);
22609         var pt = new Roo.lib.Point(xy[0], xy[1]);
22610         for(var id in els){
22611             var el = els[id], r = el._region;
22612             if(r && r.contains(pt) && el.isScrollable()){
22613                 if(r.bottom - pt.y <= dds.thresh){
22614                     if(proc.el != el){
22615                         startProc(el, "down");
22616                     }
22617                     return;
22618                 }else if(r.right - pt.x <= dds.thresh){
22619                     if(proc.el != el){
22620                         startProc(el, "left");
22621                     }
22622                     return;
22623                 }else if(pt.y - r.top <= dds.thresh){
22624                     if(proc.el != el){
22625                         startProc(el, "up");
22626                     }
22627                     return;
22628                 }else if(pt.x - r.left <= dds.thresh){
22629                     if(proc.el != el){
22630                         startProc(el, "right");
22631                     }
22632                     return;
22633                 }
22634             }
22635         }
22636         clearProc();
22637     };
22638     
22639     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22640     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22641     
22642     return {
22643         /**
22644          * Registers new overflow element(s) to auto scroll
22645          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22646          */
22647         register : function(el){
22648             if(el instanceof Array){
22649                 for(var i = 0, len = el.length; i < len; i++) {
22650                         this.register(el[i]);
22651                 }
22652             }else{
22653                 el = Roo.get(el);
22654                 els[el.id] = el;
22655             }
22656             Roo.dd.ScrollManager.els = els;
22657         },
22658         
22659         /**
22660          * Unregisters overflow element(s) so they are no longer scrolled
22661          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22662          */
22663         unregister : function(el){
22664             if(el instanceof Array){
22665                 for(var i = 0, len = el.length; i < len; i++) {
22666                         this.unregister(el[i]);
22667                 }
22668             }else{
22669                 el = Roo.get(el);
22670                 delete els[el.id];
22671             }
22672         },
22673         
22674         /**
22675          * The number of pixels from the edge of a container the pointer needs to be to 
22676          * trigger scrolling (defaults to 25)
22677          * @type Number
22678          */
22679         thresh : 25,
22680         
22681         /**
22682          * The number of pixels to scroll in each scroll increment (defaults to 50)
22683          * @type Number
22684          */
22685         increment : 100,
22686         
22687         /**
22688          * The frequency of scrolls in milliseconds (defaults to 500)
22689          * @type Number
22690          */
22691         frequency : 500,
22692         
22693         /**
22694          * True to animate the scroll (defaults to true)
22695          * @type Boolean
22696          */
22697         animate: true,
22698         
22699         /**
22700          * The animation duration in seconds - 
22701          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22702          * @type Number
22703          */
22704         animDuration: .4,
22705         
22706         /**
22707          * Manually trigger a cache refresh.
22708          */
22709         refreshCache : function(){
22710             for(var id in els){
22711                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22712                     els[id]._region = els[id].getRegion();
22713                 }
22714             }
22715         }
22716     };
22717 }();/*
22718  * Based on:
22719  * Ext JS Library 1.1.1
22720  * Copyright(c) 2006-2007, Ext JS, LLC.
22721  *
22722  * Originally Released Under LGPL - original licence link has changed is not relivant.
22723  *
22724  * Fork - LGPL
22725  * <script type="text/javascript">
22726  */
22727  
22728
22729 /**
22730  * @class Roo.dd.Registry
22731  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22732  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22733  * @static
22734  */
22735 Roo.dd.Registry = function(){
22736     var elements = {}; 
22737     var handles = {}; 
22738     var autoIdSeed = 0;
22739
22740     var getId = function(el, autogen){
22741         if(typeof el == "string"){
22742             return el;
22743         }
22744         var id = el.id;
22745         if(!id && autogen !== false){
22746             id = "roodd-" + (++autoIdSeed);
22747             el.id = id;
22748         }
22749         return id;
22750     };
22751     
22752     return {
22753     /**
22754      * Register a drag drop element
22755      * @param {String|HTMLElement} element The id or DOM node to register
22756      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22757      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22758      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22759      * populated in the data object (if applicable):
22760      * <pre>
22761 Value      Description<br />
22762 ---------  ------------------------------------------<br />
22763 handles    Array of DOM nodes that trigger dragging<br />
22764            for the element being registered<br />
22765 isHandle   True if the element passed in triggers<br />
22766            dragging itself, else false
22767 </pre>
22768      */
22769         register : function(el, data){
22770             data = data || {};
22771             if(typeof el == "string"){
22772                 el = document.getElementById(el);
22773             }
22774             data.ddel = el;
22775             elements[getId(el)] = data;
22776             if(data.isHandle !== false){
22777                 handles[data.ddel.id] = data;
22778             }
22779             if(data.handles){
22780                 var hs = data.handles;
22781                 for(var i = 0, len = hs.length; i < len; i++){
22782                         handles[getId(hs[i])] = data;
22783                 }
22784             }
22785         },
22786
22787     /**
22788      * Unregister a drag drop element
22789      * @param {String|HTMLElement}  element The id or DOM node to unregister
22790      */
22791         unregister : function(el){
22792             var id = getId(el, false);
22793             var data = elements[id];
22794             if(data){
22795                 delete elements[id];
22796                 if(data.handles){
22797                     var hs = data.handles;
22798                     for(var i = 0, len = hs.length; i < len; i++){
22799                         delete handles[getId(hs[i], false)];
22800                     }
22801                 }
22802             }
22803         },
22804
22805     /**
22806      * Returns the handle registered for a DOM Node by id
22807      * @param {String|HTMLElement} id The DOM node or id to look up
22808      * @return {Object} handle The custom handle data
22809      */
22810         getHandle : function(id){
22811             if(typeof id != "string"){ // must be element?
22812                 id = id.id;
22813             }
22814             return handles[id];
22815         },
22816
22817     /**
22818      * Returns the handle that is registered for the DOM node that is the target of the event
22819      * @param {Event} e The event
22820      * @return {Object} handle The custom handle data
22821      */
22822         getHandleFromEvent : function(e){
22823             var t = Roo.lib.Event.getTarget(e);
22824             return t ? handles[t.id] : null;
22825         },
22826
22827     /**
22828      * Returns a custom data object that is registered for a DOM node by id
22829      * @param {String|HTMLElement} id The DOM node or id to look up
22830      * @return {Object} data The custom data
22831      */
22832         getTarget : function(id){
22833             if(typeof id != "string"){ // must be element?
22834                 id = id.id;
22835             }
22836             return elements[id];
22837         },
22838
22839     /**
22840      * Returns a custom data object that is registered for the DOM node that is the target of the event
22841      * @param {Event} e The event
22842      * @return {Object} data The custom data
22843      */
22844         getTargetFromEvent : function(e){
22845             var t = Roo.lib.Event.getTarget(e);
22846             return t ? elements[t.id] || handles[t.id] : null;
22847         }
22848     };
22849 }();/*
22850  * Based on:
22851  * Ext JS Library 1.1.1
22852  * Copyright(c) 2006-2007, Ext JS, LLC.
22853  *
22854  * Originally Released Under LGPL - original licence link has changed is not relivant.
22855  *
22856  * Fork - LGPL
22857  * <script type="text/javascript">
22858  */
22859  
22860
22861 /**
22862  * @class Roo.dd.StatusProxy
22863  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22864  * default drag proxy used by all Roo.dd components.
22865  * @constructor
22866  * @param {Object} config
22867  */
22868 Roo.dd.StatusProxy = function(config){
22869     Roo.apply(this, config);
22870     this.id = this.id || Roo.id();
22871     this.el = new Roo.Layer({
22872         dh: {
22873             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22874                 {tag: "div", cls: "x-dd-drop-icon"},
22875                 {tag: "div", cls: "x-dd-drag-ghost"}
22876             ]
22877         }, 
22878         shadow: !config || config.shadow !== false
22879     });
22880     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22881     this.dropStatus = this.dropNotAllowed;
22882 };
22883
22884 Roo.dd.StatusProxy.prototype = {
22885     /**
22886      * @cfg {String} dropAllowed
22887      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22888      */
22889     dropAllowed : "x-dd-drop-ok",
22890     /**
22891      * @cfg {String} dropNotAllowed
22892      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22893      */
22894     dropNotAllowed : "x-dd-drop-nodrop",
22895
22896     /**
22897      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22898      * over the current target element.
22899      * @param {String} cssClass The css class for the new drop status indicator image
22900      */
22901     setStatus : function(cssClass){
22902         cssClass = cssClass || this.dropNotAllowed;
22903         if(this.dropStatus != cssClass){
22904             this.el.replaceClass(this.dropStatus, cssClass);
22905             this.dropStatus = cssClass;
22906         }
22907     },
22908
22909     /**
22910      * Resets the status indicator to the default dropNotAllowed value
22911      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22912      */
22913     reset : function(clearGhost){
22914         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22915         this.dropStatus = this.dropNotAllowed;
22916         if(clearGhost){
22917             this.ghost.update("");
22918         }
22919     },
22920
22921     /**
22922      * Updates the contents of the ghost element
22923      * @param {String} html The html that will replace the current innerHTML of the ghost element
22924      */
22925     update : function(html){
22926         if(typeof html == "string"){
22927             this.ghost.update(html);
22928         }else{
22929             this.ghost.update("");
22930             html.style.margin = "0";
22931             this.ghost.dom.appendChild(html);
22932         }
22933         // ensure float = none set?? cant remember why though.
22934         var el = this.ghost.dom.firstChild;
22935                 if(el){
22936                         Roo.fly(el).setStyle('float', 'none');
22937                 }
22938     },
22939     
22940     /**
22941      * Returns the underlying proxy {@link Roo.Layer}
22942      * @return {Roo.Layer} el
22943     */
22944     getEl : function(){
22945         return this.el;
22946     },
22947
22948     /**
22949      * Returns the ghost element
22950      * @return {Roo.Element} el
22951      */
22952     getGhost : function(){
22953         return this.ghost;
22954     },
22955
22956     /**
22957      * Hides the proxy
22958      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22959      */
22960     hide : function(clear){
22961         this.el.hide();
22962         if(clear){
22963             this.reset(true);
22964         }
22965     },
22966
22967     /**
22968      * Stops the repair animation if it's currently running
22969      */
22970     stop : function(){
22971         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22972             this.anim.stop();
22973         }
22974     },
22975
22976     /**
22977      * Displays this proxy
22978      */
22979     show : function(){
22980         this.el.show();
22981     },
22982
22983     /**
22984      * Force the Layer to sync its shadow and shim positions to the element
22985      */
22986     sync : function(){
22987         this.el.sync();
22988     },
22989
22990     /**
22991      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22992      * invalid drop operation by the item being dragged.
22993      * @param {Array} xy The XY position of the element ([x, y])
22994      * @param {Function} callback The function to call after the repair is complete
22995      * @param {Object} scope The scope in which to execute the callback
22996      */
22997     repair : function(xy, callback, scope){
22998         this.callback = callback;
22999         this.scope = scope;
23000         if(xy && this.animRepair !== false){
23001             this.el.addClass("x-dd-drag-repair");
23002             this.el.hideUnders(true);
23003             this.anim = this.el.shift({
23004                 duration: this.repairDuration || .5,
23005                 easing: 'easeOut',
23006                 xy: xy,
23007                 stopFx: true,
23008                 callback: this.afterRepair,
23009                 scope: this
23010             });
23011         }else{
23012             this.afterRepair();
23013         }
23014     },
23015
23016     // private
23017     afterRepair : function(){
23018         this.hide(true);
23019         if(typeof this.callback == "function"){
23020             this.callback.call(this.scope || this);
23021         }
23022         this.callback = null;
23023         this.scope = null;
23024     }
23025 };/*
23026  * Based on:
23027  * Ext JS Library 1.1.1
23028  * Copyright(c) 2006-2007, Ext JS, LLC.
23029  *
23030  * Originally Released Under LGPL - original licence link has changed is not relivant.
23031  *
23032  * Fork - LGPL
23033  * <script type="text/javascript">
23034  */
23035
23036 /**
23037  * @class Roo.dd.DragSource
23038  * @extends Roo.dd.DDProxy
23039  * A simple class that provides the basic implementation needed to make any element draggable.
23040  * @constructor
23041  * @param {String/HTMLElement/Element} el The container element
23042  * @param {Object} config
23043  */
23044 Roo.dd.DragSource = function(el, config){
23045     this.el = Roo.get(el);
23046     this.dragData = {};
23047     
23048     Roo.apply(this, config);
23049     
23050     if(!this.proxy){
23051         this.proxy = new Roo.dd.StatusProxy();
23052     }
23053
23054     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23055           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23056     
23057     this.dragging = false;
23058 };
23059
23060 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23061     /**
23062      * @cfg {String} dropAllowed
23063      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23064      */
23065     dropAllowed : "x-dd-drop-ok",
23066     /**
23067      * @cfg {String} dropNotAllowed
23068      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23069      */
23070     dropNotAllowed : "x-dd-drop-nodrop",
23071
23072     /**
23073      * Returns the data object associated with this drag source
23074      * @return {Object} data An object containing arbitrary data
23075      */
23076     getDragData : function(e){
23077         return this.dragData;
23078     },
23079
23080     // private
23081     onDragEnter : function(e, id){
23082         var target = Roo.dd.DragDropMgr.getDDById(id);
23083         this.cachedTarget = target;
23084         if(this.beforeDragEnter(target, e, id) !== false){
23085             if(target.isNotifyTarget){
23086                 var status = target.notifyEnter(this, e, this.dragData);
23087                 this.proxy.setStatus(status);
23088             }else{
23089                 this.proxy.setStatus(this.dropAllowed);
23090             }
23091             
23092             if(this.afterDragEnter){
23093                 /**
23094                  * An empty function by default, but provided so that you can perform a custom action
23095                  * when the dragged item enters the drop target by providing an implementation.
23096                  * @param {Roo.dd.DragDrop} target The drop target
23097                  * @param {Event} e The event object
23098                  * @param {String} id The id of the dragged element
23099                  * @method afterDragEnter
23100                  */
23101                 this.afterDragEnter(target, e, id);
23102             }
23103         }
23104     },
23105
23106     /**
23107      * An empty function by default, but provided so that you can perform a custom action
23108      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23109      * @param {Roo.dd.DragDrop} target The drop target
23110      * @param {Event} e The event object
23111      * @param {String} id The id of the dragged element
23112      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23113      */
23114     beforeDragEnter : function(target, e, id){
23115         return true;
23116     },
23117
23118     // private
23119     alignElWithMouse: function() {
23120         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23121         this.proxy.sync();
23122     },
23123
23124     // private
23125     onDragOver : function(e, id){
23126         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23127         if(this.beforeDragOver(target, e, id) !== false){
23128             if(target.isNotifyTarget){
23129                 var status = target.notifyOver(this, e, this.dragData);
23130                 this.proxy.setStatus(status);
23131             }
23132
23133             if(this.afterDragOver){
23134                 /**
23135                  * An empty function by default, but provided so that you can perform a custom action
23136                  * while the dragged item is over the drop target by providing an implementation.
23137                  * @param {Roo.dd.DragDrop} target The drop target
23138                  * @param {Event} e The event object
23139                  * @param {String} id The id of the dragged element
23140                  * @method afterDragOver
23141                  */
23142                 this.afterDragOver(target, e, id);
23143             }
23144         }
23145     },
23146
23147     /**
23148      * An empty function by default, but provided so that you can perform a custom action
23149      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23150      * @param {Roo.dd.DragDrop} target The drop target
23151      * @param {Event} e The event object
23152      * @param {String} id The id of the dragged element
23153      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23154      */
23155     beforeDragOver : function(target, e, id){
23156         return true;
23157     },
23158
23159     // private
23160     onDragOut : function(e, id){
23161         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23162         if(this.beforeDragOut(target, e, id) !== false){
23163             if(target.isNotifyTarget){
23164                 target.notifyOut(this, e, this.dragData);
23165             }
23166             this.proxy.reset();
23167             if(this.afterDragOut){
23168                 /**
23169                  * An empty function by default, but provided so that you can perform a custom action
23170                  * after the dragged item is dragged out of the target without dropping.
23171                  * @param {Roo.dd.DragDrop} target The drop target
23172                  * @param {Event} e The event object
23173                  * @param {String} id The id of the dragged element
23174                  * @method afterDragOut
23175                  */
23176                 this.afterDragOut(target, e, id);
23177             }
23178         }
23179         this.cachedTarget = null;
23180     },
23181
23182     /**
23183      * An empty function by default, but provided so that you can perform a custom action before the dragged
23184      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23185      * @param {Roo.dd.DragDrop} target The drop target
23186      * @param {Event} e The event object
23187      * @param {String} id The id of the dragged element
23188      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23189      */
23190     beforeDragOut : function(target, e, id){
23191         return true;
23192     },
23193     
23194     // private
23195     onDragDrop : function(e, id){
23196         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23197         if(this.beforeDragDrop(target, e, id) !== false){
23198             if(target.isNotifyTarget){
23199                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23200                     this.onValidDrop(target, e, id);
23201                 }else{
23202                     this.onInvalidDrop(target, e, id);
23203                 }
23204             }else{
23205                 this.onValidDrop(target, e, id);
23206             }
23207             
23208             if(this.afterDragDrop){
23209                 /**
23210                  * An empty function by default, but provided so that you can perform a custom action
23211                  * after a valid drag drop has occurred by providing an implementation.
23212                  * @param {Roo.dd.DragDrop} target The drop target
23213                  * @param {Event} e The event object
23214                  * @param {String} id The id of the dropped element
23215                  * @method afterDragDrop
23216                  */
23217                 this.afterDragDrop(target, e, id);
23218             }
23219         }
23220         delete this.cachedTarget;
23221     },
23222
23223     /**
23224      * An empty function by default, but provided so that you can perform a custom action before the dragged
23225      * item is dropped onto the target and optionally cancel the onDragDrop.
23226      * @param {Roo.dd.DragDrop} target The drop target
23227      * @param {Event} e The event object
23228      * @param {String} id The id of the dragged element
23229      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23230      */
23231     beforeDragDrop : function(target, e, id){
23232         return true;
23233     },
23234
23235     // private
23236     onValidDrop : function(target, e, id){
23237         this.hideProxy();
23238         if(this.afterValidDrop){
23239             /**
23240              * An empty function by default, but provided so that you can perform a custom action
23241              * after a valid drop has occurred by providing an implementation.
23242              * @param {Object} target The target DD 
23243              * @param {Event} e The event object
23244              * @param {String} id The id of the dropped element
23245              * @method afterInvalidDrop
23246              */
23247             this.afterValidDrop(target, e, id);
23248         }
23249     },
23250
23251     // private
23252     getRepairXY : function(e, data){
23253         return this.el.getXY();  
23254     },
23255
23256     // private
23257     onInvalidDrop : function(target, e, id){
23258         this.beforeInvalidDrop(target, e, id);
23259         if(this.cachedTarget){
23260             if(this.cachedTarget.isNotifyTarget){
23261                 this.cachedTarget.notifyOut(this, e, this.dragData);
23262             }
23263             this.cacheTarget = null;
23264         }
23265         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23266
23267         if(this.afterInvalidDrop){
23268             /**
23269              * An empty function by default, but provided so that you can perform a custom action
23270              * after an invalid drop has occurred by providing an implementation.
23271              * @param {Event} e The event object
23272              * @param {String} id The id of the dropped element
23273              * @method afterInvalidDrop
23274              */
23275             this.afterInvalidDrop(e, id);
23276         }
23277     },
23278
23279     // private
23280     afterRepair : function(){
23281         if(Roo.enableFx){
23282             this.el.highlight(this.hlColor || "c3daf9");
23283         }
23284         this.dragging = false;
23285     },
23286
23287     /**
23288      * An empty function by default, but provided so that you can perform a custom action after an invalid
23289      * drop has occurred.
23290      * @param {Roo.dd.DragDrop} target The drop target
23291      * @param {Event} e The event object
23292      * @param {String} id The id of the dragged element
23293      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23294      */
23295     beforeInvalidDrop : function(target, e, id){
23296         return true;
23297     },
23298
23299     // private
23300     handleMouseDown : function(e){
23301         if(this.dragging) {
23302             return;
23303         }
23304         var data = this.getDragData(e);
23305         if(data && this.onBeforeDrag(data, e) !== false){
23306             this.dragData = data;
23307             this.proxy.stop();
23308             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23309         } 
23310     },
23311
23312     /**
23313      * An empty function by default, but provided so that you can perform a custom action before the initial
23314      * drag event begins and optionally cancel it.
23315      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23316      * @param {Event} e The event object
23317      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23318      */
23319     onBeforeDrag : function(data, e){
23320         return true;
23321     },
23322
23323     /**
23324      * An empty function by default, but provided so that you can perform a custom action once the initial
23325      * drag event has begun.  The drag cannot be canceled from this function.
23326      * @param {Number} x The x position of the click on the dragged object
23327      * @param {Number} y The y position of the click on the dragged object
23328      */
23329     onStartDrag : Roo.emptyFn,
23330
23331     // private - YUI override
23332     startDrag : function(x, y){
23333         this.proxy.reset();
23334         this.dragging = true;
23335         this.proxy.update("");
23336         this.onInitDrag(x, y);
23337         this.proxy.show();
23338     },
23339
23340     // private
23341     onInitDrag : function(x, y){
23342         var clone = this.el.dom.cloneNode(true);
23343         clone.id = Roo.id(); // prevent duplicate ids
23344         this.proxy.update(clone);
23345         this.onStartDrag(x, y);
23346         return true;
23347     },
23348
23349     /**
23350      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23351      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23352      */
23353     getProxy : function(){
23354         return this.proxy;  
23355     },
23356
23357     /**
23358      * Hides the drag source's {@link Roo.dd.StatusProxy}
23359      */
23360     hideProxy : function(){
23361         this.proxy.hide();  
23362         this.proxy.reset(true);
23363         this.dragging = false;
23364     },
23365
23366     // private
23367     triggerCacheRefresh : function(){
23368         Roo.dd.DDM.refreshCache(this.groups);
23369     },
23370
23371     // private - override to prevent hiding
23372     b4EndDrag: function(e) {
23373     },
23374
23375     // private - override to prevent moving
23376     endDrag : function(e){
23377         this.onEndDrag(this.dragData, e);
23378     },
23379
23380     // private
23381     onEndDrag : function(data, e){
23382     },
23383     
23384     // private - pin to cursor
23385     autoOffset : function(x, y) {
23386         this.setDelta(-12, -20);
23387     }    
23388 });/*
23389  * Based on:
23390  * Ext JS Library 1.1.1
23391  * Copyright(c) 2006-2007, Ext JS, LLC.
23392  *
23393  * Originally Released Under LGPL - original licence link has changed is not relivant.
23394  *
23395  * Fork - LGPL
23396  * <script type="text/javascript">
23397  */
23398
23399
23400 /**
23401  * @class Roo.dd.DropTarget
23402  * @extends Roo.dd.DDTarget
23403  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23404  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23405  * @constructor
23406  * @param {String/HTMLElement/Element} el The container element
23407  * @param {Object} config
23408  */
23409 Roo.dd.DropTarget = function(el, config){
23410     this.el = Roo.get(el);
23411     
23412     var listeners = false; ;
23413     if (config && config.listeners) {
23414         listeners= config.listeners;
23415         delete config.listeners;
23416     }
23417     Roo.apply(this, config);
23418     
23419     if(this.containerScroll){
23420         Roo.dd.ScrollManager.register(this.el);
23421     }
23422     this.addEvents( {
23423          /**
23424          * @scope Roo.dd.DropTarget
23425          */
23426          
23427          /**
23428          * @event enter
23429          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23430          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23431          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23432          * 
23433          * IMPORTANT : it should set  this.valid to true|false
23434          * 
23435          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23436          * @param {Event} e The event
23437          * @param {Object} data An object containing arbitrary data supplied by the drag source
23438          */
23439         "enter" : true,
23440         
23441          /**
23442          * @event over
23443          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23444          * This method will be called on every mouse movement while the drag source is over the drop target.
23445          * This default implementation simply returns the dropAllowed config value.
23446          * 
23447          * IMPORTANT : it should set  this.valid to true|false
23448          * 
23449          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23450          * @param {Event} e The event
23451          * @param {Object} data An object containing arbitrary data supplied by the drag source
23452          
23453          */
23454         "over" : true,
23455         /**
23456          * @event out
23457          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23458          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23459          * overClass (if any) from the drop element.
23460          * 
23461          * 
23462          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23463          * @param {Event} e The event
23464          * @param {Object} data An object containing arbitrary data supplied by the drag source
23465          */
23466          "out" : true,
23467          
23468         /**
23469          * @event drop
23470          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23471          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23472          * implementation that does something to process the drop event and returns true so that the drag source's
23473          * repair action does not run.
23474          * 
23475          * IMPORTANT : it should set this.success
23476          * 
23477          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23478          * @param {Event} e The event
23479          * @param {Object} data An object containing arbitrary data supplied by the drag source
23480         */
23481          "drop" : true
23482     });
23483             
23484      
23485     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23486         this.el.dom, 
23487         this.ddGroup || this.group,
23488         {
23489             isTarget: true,
23490             listeners : listeners || {} 
23491            
23492         
23493         }
23494     );
23495
23496 };
23497
23498 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23499     /**
23500      * @cfg {String} overClass
23501      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23502      */
23503      /**
23504      * @cfg {String} ddGroup
23505      * The drag drop group to handle drop events for
23506      */
23507      
23508     /**
23509      * @cfg {String} dropAllowed
23510      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23511      */
23512     dropAllowed : "x-dd-drop-ok",
23513     /**
23514      * @cfg {String} dropNotAllowed
23515      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23516      */
23517     dropNotAllowed : "x-dd-drop-nodrop",
23518     /**
23519      * @cfg {boolean} success
23520      * set this after drop listener.. 
23521      */
23522     success : false,
23523     /**
23524      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23525      * if the drop point is valid for over/enter..
23526      */
23527     valid : false,
23528     // private
23529     isTarget : true,
23530
23531     // private
23532     isNotifyTarget : true,
23533     
23534     /**
23535      * @hide
23536      */
23537     notifyEnter : function(dd, e, data)
23538     {
23539         this.valid = true;
23540         this.fireEvent('enter', dd, e, data);
23541         if(this.overClass){
23542             this.el.addClass(this.overClass);
23543         }
23544         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23545             this.valid ? this.dropAllowed : this.dropNotAllowed
23546         );
23547     },
23548
23549     /**
23550      * @hide
23551      */
23552     notifyOver : function(dd, e, data)
23553     {
23554         this.valid = true;
23555         this.fireEvent('over', dd, e, data);
23556         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23557             this.valid ? this.dropAllowed : this.dropNotAllowed
23558         );
23559     },
23560
23561     /**
23562      * @hide
23563      */
23564     notifyOut : function(dd, e, data)
23565     {
23566         this.fireEvent('out', dd, e, data);
23567         if(this.overClass){
23568             this.el.removeClass(this.overClass);
23569         }
23570     },
23571
23572     /**
23573      * @hide
23574      */
23575     notifyDrop : function(dd, e, data)
23576     {
23577         this.success = false;
23578         this.fireEvent('drop', dd, e, data);
23579         return this.success;
23580     }
23581 });/*
23582  * Based on:
23583  * Ext JS Library 1.1.1
23584  * Copyright(c) 2006-2007, Ext JS, LLC.
23585  *
23586  * Originally Released Under LGPL - original licence link has changed is not relivant.
23587  *
23588  * Fork - LGPL
23589  * <script type="text/javascript">
23590  */
23591
23592
23593 /**
23594  * @class Roo.dd.DragZone
23595  * @extends Roo.dd.DragSource
23596  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23597  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23598  * @constructor
23599  * @param {String/HTMLElement/Element} el The container element
23600  * @param {Object} config
23601  */
23602 Roo.dd.DragZone = function(el, config){
23603     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23604     if(this.containerScroll){
23605         Roo.dd.ScrollManager.register(this.el);
23606     }
23607 };
23608
23609 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23610     /**
23611      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23612      * for auto scrolling during drag operations.
23613      */
23614     /**
23615      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23616      * method after a failed drop (defaults to "c3daf9" - light blue)
23617      */
23618
23619     /**
23620      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23621      * for a valid target to drag based on the mouse down. Override this method
23622      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23623      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23624      * @param {EventObject} e The mouse down event
23625      * @return {Object} The dragData
23626      */
23627     getDragData : function(e){
23628         return Roo.dd.Registry.getHandleFromEvent(e);
23629     },
23630     
23631     /**
23632      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23633      * this.dragData.ddel
23634      * @param {Number} x The x position of the click on the dragged object
23635      * @param {Number} y The y position of the click on the dragged object
23636      * @return {Boolean} true to continue the drag, false to cancel
23637      */
23638     onInitDrag : function(x, y){
23639         this.proxy.update(this.dragData.ddel.cloneNode(true));
23640         this.onStartDrag(x, y);
23641         return true;
23642     },
23643     
23644     /**
23645      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23646      */
23647     afterRepair : function(){
23648         if(Roo.enableFx){
23649             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23650         }
23651         this.dragging = false;
23652     },
23653
23654     /**
23655      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23656      * the XY of this.dragData.ddel
23657      * @param {EventObject} e The mouse up event
23658      * @return {Array} The xy location (e.g. [100, 200])
23659      */
23660     getRepairXY : function(e){
23661         return Roo.Element.fly(this.dragData.ddel).getXY();  
23662     }
23663 });/*
23664  * Based on:
23665  * Ext JS Library 1.1.1
23666  * Copyright(c) 2006-2007, Ext JS, LLC.
23667  *
23668  * Originally Released Under LGPL - original licence link has changed is not relivant.
23669  *
23670  * Fork - LGPL
23671  * <script type="text/javascript">
23672  */
23673 /**
23674  * @class Roo.dd.DropZone
23675  * @extends Roo.dd.DropTarget
23676  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23677  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23678  * @constructor
23679  * @param {String/HTMLElement/Element} el The container element
23680  * @param {Object} config
23681  */
23682 Roo.dd.DropZone = function(el, config){
23683     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23684 };
23685
23686 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23687     /**
23688      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23689      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23690      * provide your own custom lookup.
23691      * @param {Event} e The event
23692      * @return {Object} data The custom data
23693      */
23694     getTargetFromEvent : function(e){
23695         return Roo.dd.Registry.getTargetFromEvent(e);
23696     },
23697
23698     /**
23699      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23700      * that it has registered.  This method has no default implementation and should be overridden to provide
23701      * node-specific processing if necessary.
23702      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23703      * {@link #getTargetFromEvent} for this node)
23704      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23705      * @param {Event} e The event
23706      * @param {Object} data An object containing arbitrary data supplied by the drag source
23707      */
23708     onNodeEnter : function(n, dd, e, data){
23709         
23710     },
23711
23712     /**
23713      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23714      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23715      * overridden to provide the proper feedback.
23716      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23717      * {@link #getTargetFromEvent} for this node)
23718      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23719      * @param {Event} e The event
23720      * @param {Object} data An object containing arbitrary data supplied by the drag source
23721      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23722      * underlying {@link Roo.dd.StatusProxy} can be updated
23723      */
23724     onNodeOver : function(n, dd, e, data){
23725         return this.dropAllowed;
23726     },
23727
23728     /**
23729      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23730      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23731      * node-specific processing if necessary.
23732      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23733      * {@link #getTargetFromEvent} for this node)
23734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23735      * @param {Event} e The event
23736      * @param {Object} data An object containing arbitrary data supplied by the drag source
23737      */
23738     onNodeOut : function(n, dd, e, data){
23739         
23740     },
23741
23742     /**
23743      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23744      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23745      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23746      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23747      * {@link #getTargetFromEvent} for this node)
23748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23749      * @param {Event} e The event
23750      * @param {Object} data An object containing arbitrary data supplied by the drag source
23751      * @return {Boolean} True if the drop was valid, else false
23752      */
23753     onNodeDrop : function(n, dd, e, data){
23754         return false;
23755     },
23756
23757     /**
23758      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23759      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23760      * it should be overridden to provide the proper feedback if necessary.
23761      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23762      * @param {Event} e The event
23763      * @param {Object} data An object containing arbitrary data supplied by the drag source
23764      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23765      * underlying {@link Roo.dd.StatusProxy} can be updated
23766      */
23767     onContainerOver : function(dd, e, data){
23768         return this.dropNotAllowed;
23769     },
23770
23771     /**
23772      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23773      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23774      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23775      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23776      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23777      * @param {Event} e The event
23778      * @param {Object} data An object containing arbitrary data supplied by the drag source
23779      * @return {Boolean} True if the drop was valid, else false
23780      */
23781     onContainerDrop : function(dd, e, data){
23782         return false;
23783     },
23784
23785     /**
23786      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23787      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23788      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23789      * you should override this method and provide a custom implementation.
23790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23791      * @param {Event} e The event
23792      * @param {Object} data An object containing arbitrary data supplied by the drag source
23793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23794      * underlying {@link Roo.dd.StatusProxy} can be updated
23795      */
23796     notifyEnter : function(dd, e, data){
23797         return this.dropNotAllowed;
23798     },
23799
23800     /**
23801      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23802      * This method will be called on every mouse movement while the drag source is over the drop zone.
23803      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23804      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23805      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23806      * registered node, it will call {@link #onContainerOver}.
23807      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23808      * @param {Event} e The event
23809      * @param {Object} data An object containing arbitrary data supplied by the drag source
23810      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23811      * underlying {@link Roo.dd.StatusProxy} can be updated
23812      */
23813     notifyOver : function(dd, e, data){
23814         var n = this.getTargetFromEvent(e);
23815         if(!n){ // not over valid drop target
23816             if(this.lastOverNode){
23817                 this.onNodeOut(this.lastOverNode, dd, e, data);
23818                 this.lastOverNode = null;
23819             }
23820             return this.onContainerOver(dd, e, data);
23821         }
23822         if(this.lastOverNode != n){
23823             if(this.lastOverNode){
23824                 this.onNodeOut(this.lastOverNode, dd, e, data);
23825             }
23826             this.onNodeEnter(n, dd, e, data);
23827             this.lastOverNode = n;
23828         }
23829         return this.onNodeOver(n, dd, e, data);
23830     },
23831
23832     /**
23833      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23834      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23835      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23836      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23837      * @param {Event} e The event
23838      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23839      */
23840     notifyOut : function(dd, e, data){
23841         if(this.lastOverNode){
23842             this.onNodeOut(this.lastOverNode, dd, e, data);
23843             this.lastOverNode = null;
23844         }
23845     },
23846
23847     /**
23848      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23849      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23850      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23851      * otherwise it will call {@link #onContainerDrop}.
23852      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23853      * @param {Event} e The event
23854      * @param {Object} data An object containing arbitrary data supplied by the drag source
23855      * @return {Boolean} True if the drop was valid, else false
23856      */
23857     notifyDrop : function(dd, e, data){
23858         if(this.lastOverNode){
23859             this.onNodeOut(this.lastOverNode, dd, e, data);
23860             this.lastOverNode = null;
23861         }
23862         var n = this.getTargetFromEvent(e);
23863         return n ?
23864             this.onNodeDrop(n, dd, e, data) :
23865             this.onContainerDrop(dd, e, data);
23866     },
23867
23868     // private
23869     triggerCacheRefresh : function(){
23870         Roo.dd.DDM.refreshCache(this.groups);
23871     }  
23872 });/*
23873  * Based on:
23874  * Ext JS Library 1.1.1
23875  * Copyright(c) 2006-2007, Ext JS, LLC.
23876  *
23877  * Originally Released Under LGPL - original licence link has changed is not relivant.
23878  *
23879  * Fork - LGPL
23880  * <script type="text/javascript">
23881  */
23882
23883
23884 /**
23885  * @class Roo.data.SortTypes
23886  * @static
23887  * Defines the default sorting (casting?) comparison functions used when sorting data.
23888  */
23889 Roo.data.SortTypes = {
23890     /**
23891      * Default sort that does nothing
23892      * @param {Mixed} s The value being converted
23893      * @return {Mixed} The comparison value
23894      */
23895     none : function(s){
23896         return s;
23897     },
23898     
23899     /**
23900      * The regular expression used to strip tags
23901      * @type {RegExp}
23902      * @property
23903      */
23904     stripTagsRE : /<\/?[^>]+>/gi,
23905     
23906     /**
23907      * Strips all HTML tags to sort on text only
23908      * @param {Mixed} s The value being converted
23909      * @return {String} The comparison value
23910      */
23911     asText : function(s){
23912         return String(s).replace(this.stripTagsRE, "");
23913     },
23914     
23915     /**
23916      * Strips all HTML tags to sort on text only - Case insensitive
23917      * @param {Mixed} s The value being converted
23918      * @return {String} The comparison value
23919      */
23920     asUCText : function(s){
23921         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23922     },
23923     
23924     /**
23925      * Case insensitive string
23926      * @param {Mixed} s The value being converted
23927      * @return {String} The comparison value
23928      */
23929     asUCString : function(s) {
23930         return String(s).toUpperCase();
23931     },
23932     
23933     /**
23934      * Date sorting
23935      * @param {Mixed} s The value being converted
23936      * @return {Number} The comparison value
23937      */
23938     asDate : function(s) {
23939         if(!s){
23940             return 0;
23941         }
23942         if(s instanceof Date){
23943             return s.getTime();
23944         }
23945         return Date.parse(String(s));
23946     },
23947     
23948     /**
23949      * Float sorting
23950      * @param {Mixed} s The value being converted
23951      * @return {Float} The comparison value
23952      */
23953     asFloat : function(s) {
23954         var val = parseFloat(String(s).replace(/,/g, ""));
23955         if(isNaN(val)) {
23956             val = 0;
23957         }
23958         return val;
23959     },
23960     
23961     /**
23962      * Integer sorting
23963      * @param {Mixed} s The value being converted
23964      * @return {Number} The comparison value
23965      */
23966     asInt : function(s) {
23967         var val = parseInt(String(s).replace(/,/g, ""));
23968         if(isNaN(val)) {
23969             val = 0;
23970         }
23971         return val;
23972     }
23973 };/*
23974  * Based on:
23975  * Ext JS Library 1.1.1
23976  * Copyright(c) 2006-2007, Ext JS, LLC.
23977  *
23978  * Originally Released Under LGPL - original licence link has changed is not relivant.
23979  *
23980  * Fork - LGPL
23981  * <script type="text/javascript">
23982  */
23983
23984 /**
23985 * @class Roo.data.Record
23986  * Instances of this class encapsulate both record <em>definition</em> information, and record
23987  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23988  * to access Records cached in an {@link Roo.data.Store} object.<br>
23989  * <p>
23990  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23991  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23992  * objects.<br>
23993  * <p>
23994  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23995  * @constructor
23996  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23997  * {@link #create}. The parameters are the same.
23998  * @param {Array} data An associative Array of data values keyed by the field name.
23999  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24000  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24001  * not specified an integer id is generated.
24002  */
24003 Roo.data.Record = function(data, id){
24004     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24005     this.data = data;
24006 };
24007
24008 /**
24009  * Generate a constructor for a specific record layout.
24010  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24011  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24012  * Each field definition object may contain the following properties: <ul>
24013  * <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,
24014  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24015  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24016  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24017  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24018  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24019  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24020  * this may be omitted.</p></li>
24021  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24022  * <ul><li>auto (Default, implies no conversion)</li>
24023  * <li>string</li>
24024  * <li>int</li>
24025  * <li>float</li>
24026  * <li>boolean</li>
24027  * <li>date</li></ul></p></li>
24028  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24029  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24030  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24031  * by the Reader into an object that will be stored in the Record. It is passed the
24032  * following parameters:<ul>
24033  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24034  * </ul></p></li>
24035  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24036  * </ul>
24037  * <br>usage:<br><pre><code>
24038 var TopicRecord = Roo.data.Record.create(
24039     {name: 'title', mapping: 'topic_title'},
24040     {name: 'author', mapping: 'username'},
24041     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24042     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24043     {name: 'lastPoster', mapping: 'user2'},
24044     {name: 'excerpt', mapping: 'post_text'}
24045 );
24046
24047 var myNewRecord = new TopicRecord({
24048     title: 'Do my job please',
24049     author: 'noobie',
24050     totalPosts: 1,
24051     lastPost: new Date(),
24052     lastPoster: 'Animal',
24053     excerpt: 'No way dude!'
24054 });
24055 myStore.add(myNewRecord);
24056 </code></pre>
24057  * @method create
24058  * @static
24059  */
24060 Roo.data.Record.create = function(o){
24061     var f = function(){
24062         f.superclass.constructor.apply(this, arguments);
24063     };
24064     Roo.extend(f, Roo.data.Record);
24065     var p = f.prototype;
24066     p.fields = new Roo.util.MixedCollection(false, function(field){
24067         return field.name;
24068     });
24069     for(var i = 0, len = o.length; i < len; i++){
24070         p.fields.add(new Roo.data.Field(o[i]));
24071     }
24072     f.getField = function(name){
24073         return p.fields.get(name);  
24074     };
24075     return f;
24076 };
24077
24078 Roo.data.Record.AUTO_ID = 1000;
24079 Roo.data.Record.EDIT = 'edit';
24080 Roo.data.Record.REJECT = 'reject';
24081 Roo.data.Record.COMMIT = 'commit';
24082
24083 Roo.data.Record.prototype = {
24084     /**
24085      * Readonly flag - true if this record has been modified.
24086      * @type Boolean
24087      */
24088     dirty : false,
24089     editing : false,
24090     error: null,
24091     modified: null,
24092
24093     // private
24094     join : function(store){
24095         this.store = store;
24096     },
24097
24098     /**
24099      * Set the named field to the specified value.
24100      * @param {String} name The name of the field to set.
24101      * @param {Object} value The value to set the field to.
24102      */
24103     set : function(name, value){
24104         if(this.data[name] == value){
24105             return;
24106         }
24107         this.dirty = true;
24108         if(!this.modified){
24109             this.modified = {};
24110         }
24111         if(typeof this.modified[name] == 'undefined'){
24112             this.modified[name] = this.data[name];
24113         }
24114         this.data[name] = value;
24115         if(!this.editing && this.store){
24116             this.store.afterEdit(this);
24117         }       
24118     },
24119
24120     /**
24121      * Get the value of the named field.
24122      * @param {String} name The name of the field to get the value of.
24123      * @return {Object} The value of the field.
24124      */
24125     get : function(name){
24126         return this.data[name]; 
24127     },
24128
24129     // private
24130     beginEdit : function(){
24131         this.editing = true;
24132         this.modified = {}; 
24133     },
24134
24135     // private
24136     cancelEdit : function(){
24137         this.editing = false;
24138         delete this.modified;
24139     },
24140
24141     // private
24142     endEdit : function(){
24143         this.editing = false;
24144         if(this.dirty && this.store){
24145             this.store.afterEdit(this);
24146         }
24147     },
24148
24149     /**
24150      * Usually called by the {@link Roo.data.Store} which owns the Record.
24151      * Rejects all changes made to the Record since either creation, or the last commit operation.
24152      * Modified fields are reverted to their original values.
24153      * <p>
24154      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24155      * of reject operations.
24156      */
24157     reject : function(){
24158         var m = this.modified;
24159         for(var n in m){
24160             if(typeof m[n] != "function"){
24161                 this.data[n] = m[n];
24162             }
24163         }
24164         this.dirty = false;
24165         delete this.modified;
24166         this.editing = false;
24167         if(this.store){
24168             this.store.afterReject(this);
24169         }
24170     },
24171
24172     /**
24173      * Usually called by the {@link Roo.data.Store} which owns the Record.
24174      * Commits all changes made to the Record since either creation, or the last commit operation.
24175      * <p>
24176      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24177      * of commit operations.
24178      */
24179     commit : function(){
24180         this.dirty = false;
24181         delete this.modified;
24182         this.editing = false;
24183         if(this.store){
24184             this.store.afterCommit(this);
24185         }
24186     },
24187
24188     // private
24189     hasError : function(){
24190         return this.error != null;
24191     },
24192
24193     // private
24194     clearError : function(){
24195         this.error = null;
24196     },
24197
24198     /**
24199      * Creates a copy of this record.
24200      * @param {String} id (optional) A new record id if you don't want to use this record's id
24201      * @return {Record}
24202      */
24203     copy : function(newId) {
24204         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24205     }
24206 };/*
24207  * Based on:
24208  * Ext JS Library 1.1.1
24209  * Copyright(c) 2006-2007, Ext JS, LLC.
24210  *
24211  * Originally Released Under LGPL - original licence link has changed is not relivant.
24212  *
24213  * Fork - LGPL
24214  * <script type="text/javascript">
24215  */
24216
24217
24218
24219 /**
24220  * @class Roo.data.Store
24221  * @extends Roo.util.Observable
24222  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24223  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24224  * <p>
24225  * 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
24226  * has no knowledge of the format of the data returned by the Proxy.<br>
24227  * <p>
24228  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24229  * instances from the data object. These records are cached and made available through accessor functions.
24230  * @constructor
24231  * Creates a new Store.
24232  * @param {Object} config A config object containing the objects needed for the Store to access data,
24233  * and read the data into Records.
24234  */
24235 Roo.data.Store = function(config){
24236     this.data = new Roo.util.MixedCollection(false);
24237     this.data.getKey = function(o){
24238         return o.id;
24239     };
24240     this.baseParams = {};
24241     // private
24242     this.paramNames = {
24243         "start" : "start",
24244         "limit" : "limit",
24245         "sort" : "sort",
24246         "dir" : "dir",
24247         "multisort" : "_multisort"
24248     };
24249
24250     if(config && config.data){
24251         this.inlineData = config.data;
24252         delete config.data;
24253     }
24254
24255     Roo.apply(this, config);
24256     
24257     if(this.reader){ // reader passed
24258         this.reader = Roo.factory(this.reader, Roo.data);
24259         this.reader.xmodule = this.xmodule || false;
24260         if(!this.recordType){
24261             this.recordType = this.reader.recordType;
24262         }
24263         if(this.reader.onMetaChange){
24264             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24265         }
24266     }
24267
24268     if(this.recordType){
24269         this.fields = this.recordType.prototype.fields;
24270     }
24271     this.modified = [];
24272
24273     this.addEvents({
24274         /**
24275          * @event datachanged
24276          * Fires when the data cache has changed, and a widget which is using this Store
24277          * as a Record cache should refresh its view.
24278          * @param {Store} this
24279          */
24280         datachanged : true,
24281         /**
24282          * @event metachange
24283          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24284          * @param {Store} this
24285          * @param {Object} meta The JSON metadata
24286          */
24287         metachange : true,
24288         /**
24289          * @event add
24290          * Fires when Records have been added to the Store
24291          * @param {Store} this
24292          * @param {Roo.data.Record[]} records The array of Records added
24293          * @param {Number} index The index at which the record(s) were added
24294          */
24295         add : true,
24296         /**
24297          * @event remove
24298          * Fires when a Record has been removed from the Store
24299          * @param {Store} this
24300          * @param {Roo.data.Record} record The Record that was removed
24301          * @param {Number} index The index at which the record was removed
24302          */
24303         remove : true,
24304         /**
24305          * @event update
24306          * Fires when a Record has been updated
24307          * @param {Store} this
24308          * @param {Roo.data.Record} record The Record that was updated
24309          * @param {String} operation The update operation being performed.  Value may be one of:
24310          * <pre><code>
24311  Roo.data.Record.EDIT
24312  Roo.data.Record.REJECT
24313  Roo.data.Record.COMMIT
24314          * </code></pre>
24315          */
24316         update : true,
24317         /**
24318          * @event clear
24319          * Fires when the data cache has been cleared.
24320          * @param {Store} this
24321          */
24322         clear : true,
24323         /**
24324          * @event beforeload
24325          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24326          * the load action will be canceled.
24327          * @param {Store} this
24328          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24329          */
24330         beforeload : true,
24331         /**
24332          * @event beforeloadadd
24333          * Fires after a new set of Records has been loaded.
24334          * @param {Store} this
24335          * @param {Roo.data.Record[]} records The Records that were loaded
24336          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24337          */
24338         beforeloadadd : true,
24339         /**
24340          * @event load
24341          * Fires after a new set of Records has been loaded, before they are added to the store.
24342          * @param {Store} this
24343          * @param {Roo.data.Record[]} records The Records that were loaded
24344          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24345          * @params {Object} return from reader
24346          */
24347         load : true,
24348         /**
24349          * @event loadexception
24350          * Fires if an exception occurs in the Proxy during loading.
24351          * Called with the signature of the Proxy's "loadexception" event.
24352          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24353          * 
24354          * @param {Proxy} 
24355          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24356          * @param {Object} load options 
24357          * @param {Object} jsonData from your request (normally this contains the Exception)
24358          */
24359         loadexception : true
24360     });
24361     
24362     if(this.proxy){
24363         this.proxy = Roo.factory(this.proxy, Roo.data);
24364         this.proxy.xmodule = this.xmodule || false;
24365         this.relayEvents(this.proxy,  ["loadexception"]);
24366     }
24367     this.sortToggle = {};
24368     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24369
24370     Roo.data.Store.superclass.constructor.call(this);
24371
24372     if(this.inlineData){
24373         this.loadData(this.inlineData);
24374         delete this.inlineData;
24375     }
24376 };
24377
24378 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24379      /**
24380     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24381     * without a remote query - used by combo/forms at present.
24382     */
24383     
24384     /**
24385     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24386     */
24387     /**
24388     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24389     */
24390     /**
24391     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24392     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24393     */
24394     /**
24395     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24396     * on any HTTP request
24397     */
24398     /**
24399     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24400     */
24401     /**
24402     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24403     */
24404     multiSort: false,
24405     /**
24406     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24407     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24408     */
24409     remoteSort : false,
24410
24411     /**
24412     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24413      * loaded or when a record is removed. (defaults to false).
24414     */
24415     pruneModifiedRecords : false,
24416
24417     // private
24418     lastOptions : null,
24419
24420     /**
24421      * Add Records to the Store and fires the add event.
24422      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24423      */
24424     add : function(records){
24425         records = [].concat(records);
24426         for(var i = 0, len = records.length; i < len; i++){
24427             records[i].join(this);
24428         }
24429         var index = this.data.length;
24430         this.data.addAll(records);
24431         this.fireEvent("add", this, records, index);
24432     },
24433
24434     /**
24435      * Remove a Record from the Store and fires the remove event.
24436      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24437      */
24438     remove : function(record){
24439         var index = this.data.indexOf(record);
24440         this.data.removeAt(index);
24441  
24442         if(this.pruneModifiedRecords){
24443             this.modified.remove(record);
24444         }
24445         this.fireEvent("remove", this, record, index);
24446     },
24447
24448     /**
24449      * Remove all Records from the Store and fires the clear event.
24450      */
24451     removeAll : function(){
24452         this.data.clear();
24453         if(this.pruneModifiedRecords){
24454             this.modified = [];
24455         }
24456         this.fireEvent("clear", this);
24457     },
24458
24459     /**
24460      * Inserts Records to the Store at the given index and fires the add event.
24461      * @param {Number} index The start index at which to insert the passed Records.
24462      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24463      */
24464     insert : function(index, records){
24465         records = [].concat(records);
24466         for(var i = 0, len = records.length; i < len; i++){
24467             this.data.insert(index, records[i]);
24468             records[i].join(this);
24469         }
24470         this.fireEvent("add", this, records, index);
24471     },
24472
24473     /**
24474      * Get the index within the cache of the passed Record.
24475      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24476      * @return {Number} The index of the passed Record. Returns -1 if not found.
24477      */
24478     indexOf : function(record){
24479         return this.data.indexOf(record);
24480     },
24481
24482     /**
24483      * Get the index within the cache of the Record with the passed id.
24484      * @param {String} id The id of the Record to find.
24485      * @return {Number} The index of the Record. Returns -1 if not found.
24486      */
24487     indexOfId : function(id){
24488         return this.data.indexOfKey(id);
24489     },
24490
24491     /**
24492      * Get the Record with the specified id.
24493      * @param {String} id The id of the Record to find.
24494      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24495      */
24496     getById : function(id){
24497         return this.data.key(id);
24498     },
24499
24500     /**
24501      * Get the Record at the specified index.
24502      * @param {Number} index The index of the Record to find.
24503      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24504      */
24505     getAt : function(index){
24506         return this.data.itemAt(index);
24507     },
24508
24509     /**
24510      * Returns a range of Records between specified indices.
24511      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24512      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24513      * @return {Roo.data.Record[]} An array of Records
24514      */
24515     getRange : function(start, end){
24516         return this.data.getRange(start, end);
24517     },
24518
24519     // private
24520     storeOptions : function(o){
24521         o = Roo.apply({}, o);
24522         delete o.callback;
24523         delete o.scope;
24524         this.lastOptions = o;
24525     },
24526
24527     /**
24528      * Loads the Record cache from the configured Proxy using the configured Reader.
24529      * <p>
24530      * If using remote paging, then the first load call must specify the <em>start</em>
24531      * and <em>limit</em> properties in the options.params property to establish the initial
24532      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24533      * <p>
24534      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24535      * and this call will return before the new data has been loaded. Perform any post-processing
24536      * in a callback function, or in a "load" event handler.</strong>
24537      * <p>
24538      * @param {Object} options An object containing properties which control loading options:<ul>
24539      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24540      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24541      * passed the following arguments:<ul>
24542      * <li>r : Roo.data.Record[]</li>
24543      * <li>options: Options object from the load call</li>
24544      * <li>success: Boolean success indicator</li></ul></li>
24545      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24546      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24547      * </ul>
24548      */
24549     load : function(options){
24550         options = options || {};
24551         if(this.fireEvent("beforeload", this, options) !== false){
24552             this.storeOptions(options);
24553             var p = Roo.apply(options.params || {}, this.baseParams);
24554             // if meta was not loaded from remote source.. try requesting it.
24555             if (!this.reader.metaFromRemote) {
24556                 p._requestMeta = 1;
24557             }
24558             if(this.sortInfo && this.remoteSort){
24559                 var pn = this.paramNames;
24560                 p[pn["sort"]] = this.sortInfo.field;
24561                 p[pn["dir"]] = this.sortInfo.direction;
24562             }
24563             if (this.multiSort) {
24564                 var pn = this.paramNames;
24565                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24566             }
24567             
24568             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24569         }
24570     },
24571
24572     /**
24573      * Reloads the Record cache from the configured Proxy using the configured Reader and
24574      * the options from the last load operation performed.
24575      * @param {Object} options (optional) An object containing properties which may override the options
24576      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24577      * the most recently used options are reused).
24578      */
24579     reload : function(options){
24580         this.load(Roo.applyIf(options||{}, this.lastOptions));
24581     },
24582
24583     // private
24584     // Called as a callback by the Reader during a load operation.
24585     loadRecords : function(o, options, success){
24586          
24587         if(!o){
24588             if(success !== false){
24589                 this.fireEvent("load", this, [], options, o);
24590             }
24591             if(options.callback){
24592                 options.callback.call(options.scope || this, [], options, false);
24593             }
24594             return;
24595         }
24596         // if data returned failure - throw an exception.
24597         if (o.success === false) {
24598             // show a message if no listener is registered.
24599             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24600                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24601             }
24602             // loadmask wil be hooked into this..
24603             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24604             return;
24605         }
24606         var r = o.records, t = o.totalRecords || r.length;
24607         
24608         this.fireEvent("beforeloadadd", this, r, options, o);
24609         
24610         if(!options || options.add !== true){
24611             if(this.pruneModifiedRecords){
24612                 this.modified = [];
24613             }
24614             for(var i = 0, len = r.length; i < len; i++){
24615                 r[i].join(this);
24616             }
24617             if(this.snapshot){
24618                 this.data = this.snapshot;
24619                 delete this.snapshot;
24620             }
24621             this.data.clear();
24622             this.data.addAll(r);
24623             this.totalLength = t;
24624             this.applySort();
24625             this.fireEvent("datachanged", this);
24626         }else{
24627             this.totalLength = Math.max(t, this.data.length+r.length);
24628             this.add(r);
24629         }
24630         
24631         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24632                 
24633             var e = new Roo.data.Record({});
24634
24635             e.set(this.parent.displayField, this.parent.emptyTitle);
24636             e.set(this.parent.valueField, '');
24637
24638             this.insert(0, e);
24639         }
24640             
24641         this.fireEvent("load", this, r, options, o);
24642         if(options.callback){
24643             options.callback.call(options.scope || this, r, options, true);
24644         }
24645     },
24646
24647
24648     /**
24649      * Loads data from a passed data block. A Reader which understands the format of the data
24650      * must have been configured in the constructor.
24651      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24652      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24653      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24654      */
24655     loadData : function(o, append){
24656         var r = this.reader.readRecords(o);
24657         this.loadRecords(r, {add: append}, true);
24658     },
24659     
24660      /**
24661      * using 'cn' the nested child reader read the child array into it's child stores.
24662      * @param {Object} rec The record with a 'children array
24663      */
24664     loadDataFromChildren : function(rec)
24665     {
24666         this.loadData(this.reader.toLoadData(rec));
24667     },
24668     
24669
24670     /**
24671      * Gets the number of cached records.
24672      * <p>
24673      * <em>If using paging, this may not be the total size of the dataset. If the data object
24674      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24675      * the data set size</em>
24676      */
24677     getCount : function(){
24678         return this.data.length || 0;
24679     },
24680
24681     /**
24682      * Gets the total number of records in the dataset as returned by the server.
24683      * <p>
24684      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24685      * the dataset size</em>
24686      */
24687     getTotalCount : function(){
24688         return this.totalLength || 0;
24689     },
24690
24691     /**
24692      * Returns the sort state of the Store as an object with two properties:
24693      * <pre><code>
24694  field {String} The name of the field by which the Records are sorted
24695  direction {String} The sort order, "ASC" or "DESC"
24696      * </code></pre>
24697      */
24698     getSortState : function(){
24699         return this.sortInfo;
24700     },
24701
24702     // private
24703     applySort : function(){
24704         if(this.sortInfo && !this.remoteSort){
24705             var s = this.sortInfo, f = s.field;
24706             var st = this.fields.get(f).sortType;
24707             var fn = function(r1, r2){
24708                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24709                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24710             };
24711             this.data.sort(s.direction, fn);
24712             if(this.snapshot && this.snapshot != this.data){
24713                 this.snapshot.sort(s.direction, fn);
24714             }
24715         }
24716     },
24717
24718     /**
24719      * Sets the default sort column and order to be used by the next load operation.
24720      * @param {String} fieldName The name of the field to sort by.
24721      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24722      */
24723     setDefaultSort : function(field, dir){
24724         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24725     },
24726
24727     /**
24728      * Sort the Records.
24729      * If remote sorting is used, the sort is performed on the server, and the cache is
24730      * reloaded. If local sorting is used, the cache is sorted internally.
24731      * @param {String} fieldName The name of the field to sort by.
24732      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24733      */
24734     sort : function(fieldName, dir){
24735         var f = this.fields.get(fieldName);
24736         if(!dir){
24737             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24738             
24739             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24740                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24741             }else{
24742                 dir = f.sortDir;
24743             }
24744         }
24745         this.sortToggle[f.name] = dir;
24746         this.sortInfo = {field: f.name, direction: dir};
24747         if(!this.remoteSort){
24748             this.applySort();
24749             this.fireEvent("datachanged", this);
24750         }else{
24751             this.load(this.lastOptions);
24752         }
24753     },
24754
24755     /**
24756      * Calls the specified function for each of the Records in the cache.
24757      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24758      * Returning <em>false</em> aborts and exits the iteration.
24759      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24760      */
24761     each : function(fn, scope){
24762         this.data.each(fn, scope);
24763     },
24764
24765     /**
24766      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24767      * (e.g., during paging).
24768      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24769      */
24770     getModifiedRecords : function(){
24771         return this.modified;
24772     },
24773
24774     // private
24775     createFilterFn : function(property, value, anyMatch){
24776         if(!value.exec){ // not a regex
24777             value = String(value);
24778             if(value.length == 0){
24779                 return false;
24780             }
24781             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24782         }
24783         return function(r){
24784             return value.test(r.data[property]);
24785         };
24786     },
24787
24788     /**
24789      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24790      * @param {String} property A field on your records
24791      * @param {Number} start The record index to start at (defaults to 0)
24792      * @param {Number} end The last record index to include (defaults to length - 1)
24793      * @return {Number} The sum
24794      */
24795     sum : function(property, start, end){
24796         var rs = this.data.items, v = 0;
24797         start = start || 0;
24798         end = (end || end === 0) ? end : rs.length-1;
24799
24800         for(var i = start; i <= end; i++){
24801             v += (rs[i].data[property] || 0);
24802         }
24803         return v;
24804     },
24805
24806     /**
24807      * Filter the records by a specified property.
24808      * @param {String} field A field on your records
24809      * @param {String/RegExp} value Either a string that the field
24810      * should start with or a RegExp to test against the field
24811      * @param {Boolean} anyMatch True to match any part not just the beginning
24812      */
24813     filter : function(property, value, anyMatch){
24814         var fn = this.createFilterFn(property, value, anyMatch);
24815         return fn ? this.filterBy(fn) : this.clearFilter();
24816     },
24817
24818     /**
24819      * Filter by a function. The specified function will be called with each
24820      * record in this data source. If the function returns true the record is included,
24821      * otherwise it is filtered.
24822      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24823      * @param {Object} scope (optional) The scope of the function (defaults to this)
24824      */
24825     filterBy : function(fn, scope){
24826         this.snapshot = this.snapshot || this.data;
24827         this.data = this.queryBy(fn, scope||this);
24828         this.fireEvent("datachanged", this);
24829     },
24830
24831     /**
24832      * Query the records by a specified property.
24833      * @param {String} field A field on your records
24834      * @param {String/RegExp} value Either a string that the field
24835      * should start with or a RegExp to test against the field
24836      * @param {Boolean} anyMatch True to match any part not just the beginning
24837      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24838      */
24839     query : function(property, value, anyMatch){
24840         var fn = this.createFilterFn(property, value, anyMatch);
24841         return fn ? this.queryBy(fn) : this.data.clone();
24842     },
24843
24844     /**
24845      * Query by a function. The specified function will be called with each
24846      * record in this data source. If the function returns true the record is included
24847      * in the results.
24848      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24849      * @param {Object} scope (optional) The scope of the function (defaults to this)
24850       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24851      **/
24852     queryBy : function(fn, scope){
24853         var data = this.snapshot || this.data;
24854         return data.filterBy(fn, scope||this);
24855     },
24856
24857     /**
24858      * Collects unique values for a particular dataIndex from this store.
24859      * @param {String} dataIndex The property to collect
24860      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24861      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24862      * @return {Array} An array of the unique values
24863      **/
24864     collect : function(dataIndex, allowNull, bypassFilter){
24865         var d = (bypassFilter === true && this.snapshot) ?
24866                 this.snapshot.items : this.data.items;
24867         var v, sv, r = [], l = {};
24868         for(var i = 0, len = d.length; i < len; i++){
24869             v = d[i].data[dataIndex];
24870             sv = String(v);
24871             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24872                 l[sv] = true;
24873                 r[r.length] = v;
24874             }
24875         }
24876         return r;
24877     },
24878
24879     /**
24880      * Revert to a view of the Record cache with no filtering applied.
24881      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24882      */
24883     clearFilter : function(suppressEvent){
24884         if(this.snapshot && this.snapshot != this.data){
24885             this.data = this.snapshot;
24886             delete this.snapshot;
24887             if(suppressEvent !== true){
24888                 this.fireEvent("datachanged", this);
24889             }
24890         }
24891     },
24892
24893     // private
24894     afterEdit : function(record){
24895         if(this.modified.indexOf(record) == -1){
24896             this.modified.push(record);
24897         }
24898         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24899     },
24900     
24901     // private
24902     afterReject : function(record){
24903         this.modified.remove(record);
24904         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24905     },
24906
24907     // private
24908     afterCommit : function(record){
24909         this.modified.remove(record);
24910         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24911     },
24912
24913     /**
24914      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24915      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24916      */
24917     commitChanges : function(){
24918         var m = this.modified.slice(0);
24919         this.modified = [];
24920         for(var i = 0, len = m.length; i < len; i++){
24921             m[i].commit();
24922         }
24923     },
24924
24925     /**
24926      * Cancel outstanding changes on all changed records.
24927      */
24928     rejectChanges : function(){
24929         var m = this.modified.slice(0);
24930         this.modified = [];
24931         for(var i = 0, len = m.length; i < len; i++){
24932             m[i].reject();
24933         }
24934     },
24935
24936     onMetaChange : function(meta, rtype, o){
24937         this.recordType = rtype;
24938         this.fields = rtype.prototype.fields;
24939         delete this.snapshot;
24940         this.sortInfo = meta.sortInfo || this.sortInfo;
24941         this.modified = [];
24942         this.fireEvent('metachange', this, this.reader.meta);
24943     },
24944     
24945     moveIndex : function(data, type)
24946     {
24947         var index = this.indexOf(data);
24948         
24949         var newIndex = index + type;
24950         
24951         this.remove(data);
24952         
24953         this.insert(newIndex, data);
24954         
24955     }
24956 });/*
24957  * Based on:
24958  * Ext JS Library 1.1.1
24959  * Copyright(c) 2006-2007, Ext JS, LLC.
24960  *
24961  * Originally Released Under LGPL - original licence link has changed is not relivant.
24962  *
24963  * Fork - LGPL
24964  * <script type="text/javascript">
24965  */
24966
24967 /**
24968  * @class Roo.data.SimpleStore
24969  * @extends Roo.data.Store
24970  * Small helper class to make creating Stores from Array data easier.
24971  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24972  * @cfg {Array} fields An array of field definition objects, or field name strings.
24973  * @cfg {Object} an existing reader (eg. copied from another store)
24974  * @cfg {Array} data The multi-dimensional array of data
24975  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24976  * @cfg {Roo.data.Reader} reader  [not-required] 
24977  * @constructor
24978  * @param {Object} config
24979  */
24980 Roo.data.SimpleStore = function(config)
24981 {
24982     Roo.data.SimpleStore.superclass.constructor.call(this, {
24983         isLocal : true,
24984         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24985                 id: config.id
24986             },
24987             Roo.data.Record.create(config.fields)
24988         ),
24989         proxy : new Roo.data.MemoryProxy(config.data)
24990     });
24991     this.load();
24992 };
24993 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24994  * Based on:
24995  * Ext JS Library 1.1.1
24996  * Copyright(c) 2006-2007, Ext JS, LLC.
24997  *
24998  * Originally Released Under LGPL - original licence link has changed is not relivant.
24999  *
25000  * Fork - LGPL
25001  * <script type="text/javascript">
25002  */
25003
25004 /**
25005 /**
25006  * @extends Roo.data.Store
25007  * @class Roo.data.JsonStore
25008  * Small helper class to make creating Stores for JSON data easier. <br/>
25009 <pre><code>
25010 var store = new Roo.data.JsonStore({
25011     url: 'get-images.php',
25012     root: 'images',
25013     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25014 });
25015 </code></pre>
25016  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25017  * JsonReader and HttpProxy (unless inline data is provided).</b>
25018  * @cfg {Array} fields An array of field definition objects, or field name strings.
25019  * @constructor
25020  * @param {Object} config
25021  */
25022 Roo.data.JsonStore = function(c){
25023     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25024         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25025         reader: new Roo.data.JsonReader(c, c.fields)
25026     }));
25027 };
25028 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25029  * Based on:
25030  * Ext JS Library 1.1.1
25031  * Copyright(c) 2006-2007, Ext JS, LLC.
25032  *
25033  * Originally Released Under LGPL - original licence link has changed is not relivant.
25034  *
25035  * Fork - LGPL
25036  * <script type="text/javascript">
25037  */
25038
25039  
25040 Roo.data.Field = function(config){
25041     if(typeof config == "string"){
25042         config = {name: config};
25043     }
25044     Roo.apply(this, config);
25045     
25046     if(!this.type){
25047         this.type = "auto";
25048     }
25049     
25050     var st = Roo.data.SortTypes;
25051     // named sortTypes are supported, here we look them up
25052     if(typeof this.sortType == "string"){
25053         this.sortType = st[this.sortType];
25054     }
25055     
25056     // set default sortType for strings and dates
25057     if(!this.sortType){
25058         switch(this.type){
25059             case "string":
25060                 this.sortType = st.asUCString;
25061                 break;
25062             case "date":
25063                 this.sortType = st.asDate;
25064                 break;
25065             default:
25066                 this.sortType = st.none;
25067         }
25068     }
25069
25070     // define once
25071     var stripRe = /[\$,%]/g;
25072
25073     // prebuilt conversion function for this field, instead of
25074     // switching every time we're reading a value
25075     if(!this.convert){
25076         var cv, dateFormat = this.dateFormat;
25077         switch(this.type){
25078             case "":
25079             case "auto":
25080             case undefined:
25081                 cv = function(v){ return v; };
25082                 break;
25083             case "string":
25084                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25085                 break;
25086             case "int":
25087                 cv = function(v){
25088                     return v !== undefined && v !== null && v !== '' ?
25089                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25090                     };
25091                 break;
25092             case "float":
25093                 cv = function(v){
25094                     return v !== undefined && v !== null && v !== '' ?
25095                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25096                     };
25097                 break;
25098             case "bool":
25099             case "boolean":
25100                 cv = function(v){ return v === true || v === "true" || v == 1; };
25101                 break;
25102             case "date":
25103                 cv = function(v){
25104                     if(!v){
25105                         return '';
25106                     }
25107                     if(v instanceof Date){
25108                         return v;
25109                     }
25110                     if(dateFormat){
25111                         if(dateFormat == "timestamp"){
25112                             return new Date(v*1000);
25113                         }
25114                         return Date.parseDate(v, dateFormat);
25115                     }
25116                     var parsed = Date.parse(v);
25117                     return parsed ? new Date(parsed) : null;
25118                 };
25119              break;
25120             
25121         }
25122         this.convert = cv;
25123     }
25124 };
25125
25126 Roo.data.Field.prototype = {
25127     dateFormat: null,
25128     defaultValue: "",
25129     mapping: null,
25130     sortType : null,
25131     sortDir : "ASC"
25132 };/*
25133  * Based on:
25134  * Ext JS Library 1.1.1
25135  * Copyright(c) 2006-2007, Ext JS, LLC.
25136  *
25137  * Originally Released Under LGPL - original licence link has changed is not relivant.
25138  *
25139  * Fork - LGPL
25140  * <script type="text/javascript">
25141  */
25142  
25143 // Base class for reading structured data from a data source.  This class is intended to be
25144 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25145
25146 /**
25147  * @class Roo.data.DataReader
25148  * @abstract
25149  * Base class for reading structured data from a data source.  This class is intended to be
25150  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25151  */
25152
25153 Roo.data.DataReader = function(meta, recordType){
25154     
25155     this.meta = meta;
25156     
25157     this.recordType = recordType instanceof Array ? 
25158         Roo.data.Record.create(recordType) : recordType;
25159 };
25160
25161 Roo.data.DataReader.prototype = {
25162     
25163     
25164     readerType : 'Data',
25165      /**
25166      * Create an empty record
25167      * @param {Object} data (optional) - overlay some values
25168      * @return {Roo.data.Record} record created.
25169      */
25170     newRow :  function(d) {
25171         var da =  {};
25172         this.recordType.prototype.fields.each(function(c) {
25173             switch( c.type) {
25174                 case 'int' : da[c.name] = 0; break;
25175                 case 'date' : da[c.name] = new Date(); break;
25176                 case 'float' : da[c.name] = 0.0; break;
25177                 case 'boolean' : da[c.name] = false; break;
25178                 default : da[c.name] = ""; break;
25179             }
25180             
25181         });
25182         return new this.recordType(Roo.apply(da, d));
25183     }
25184     
25185     
25186 };/*
25187  * Based on:
25188  * Ext JS Library 1.1.1
25189  * Copyright(c) 2006-2007, Ext JS, LLC.
25190  *
25191  * Originally Released Under LGPL - original licence link has changed is not relivant.
25192  *
25193  * Fork - LGPL
25194  * <script type="text/javascript">
25195  */
25196
25197 /**
25198  * @class Roo.data.DataProxy
25199  * @extends Roo.util.Observable
25200  * @abstract
25201  * This class is an abstract base class for implementations which provide retrieval of
25202  * unformatted data objects.<br>
25203  * <p>
25204  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25205  * (of the appropriate type which knows how to parse the data object) to provide a block of
25206  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25207  * <p>
25208  * Custom implementations must implement the load method as described in
25209  * {@link Roo.data.HttpProxy#load}.
25210  */
25211 Roo.data.DataProxy = function(){
25212     this.addEvents({
25213         /**
25214          * @event beforeload
25215          * Fires before a network request is made to retrieve a data object.
25216          * @param {Object} This DataProxy object.
25217          * @param {Object} params The params parameter to the load function.
25218          */
25219         beforeload : true,
25220         /**
25221          * @event load
25222          * Fires before the load method's callback is called.
25223          * @param {Object} This DataProxy object.
25224          * @param {Object} o The data object.
25225          * @param {Object} arg The callback argument object passed to the load function.
25226          */
25227         load : true,
25228         /**
25229          * @event loadexception
25230          * Fires if an Exception occurs during data retrieval.
25231          * @param {Object} This DataProxy object.
25232          * @param {Object} o The data object.
25233          * @param {Object} arg The callback argument object passed to the load function.
25234          * @param {Object} e The Exception.
25235          */
25236         loadexception : true
25237     });
25238     Roo.data.DataProxy.superclass.constructor.call(this);
25239 };
25240
25241 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25242
25243     /**
25244      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25245      */
25246 /*
25247  * Based on:
25248  * Ext JS Library 1.1.1
25249  * Copyright(c) 2006-2007, Ext JS, LLC.
25250  *
25251  * Originally Released Under LGPL - original licence link has changed is not relivant.
25252  *
25253  * Fork - LGPL
25254  * <script type="text/javascript">
25255  */
25256 /**
25257  * @class Roo.data.MemoryProxy
25258  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25259  * to the Reader when its load method is called.
25260  * @constructor
25261  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25262  */
25263 Roo.data.MemoryProxy = function(data){
25264     if (data.data) {
25265         data = data.data;
25266     }
25267     Roo.data.MemoryProxy.superclass.constructor.call(this);
25268     this.data = data;
25269 };
25270
25271 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25272     
25273     /**
25274      * Load data from the requested source (in this case an in-memory
25275      * data object passed to the constructor), read the data object into
25276      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25277      * process that block using the passed callback.
25278      * @param {Object} params This parameter is not used by the MemoryProxy class.
25279      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25280      * object into a block of Roo.data.Records.
25281      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25282      * The function must be passed <ul>
25283      * <li>The Record block object</li>
25284      * <li>The "arg" argument from the load function</li>
25285      * <li>A boolean success indicator</li>
25286      * </ul>
25287      * @param {Object} scope The scope in which to call the callback
25288      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25289      */
25290     load : function(params, reader, callback, scope, arg){
25291         params = params || {};
25292         var result;
25293         try {
25294             result = reader.readRecords(params.data ? params.data :this.data);
25295         }catch(e){
25296             this.fireEvent("loadexception", this, arg, null, e);
25297             callback.call(scope, null, arg, false);
25298             return;
25299         }
25300         callback.call(scope, result, arg, true);
25301     },
25302     
25303     // private
25304     update : function(params, records){
25305         
25306     }
25307 });/*
25308  * Based on:
25309  * Ext JS Library 1.1.1
25310  * Copyright(c) 2006-2007, Ext JS, LLC.
25311  *
25312  * Originally Released Under LGPL - original licence link has changed is not relivant.
25313  *
25314  * Fork - LGPL
25315  * <script type="text/javascript">
25316  */
25317 /**
25318  * @class Roo.data.HttpProxy
25319  * @extends Roo.data.DataProxy
25320  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25321  * configured to reference a certain URL.<br><br>
25322  * <p>
25323  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25324  * from which the running page was served.<br><br>
25325  * <p>
25326  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25327  * <p>
25328  * Be aware that to enable the browser to parse an XML document, the server must set
25329  * the Content-Type header in the HTTP response to "text/xml".
25330  * @constructor
25331  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25332  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25333  * will be used to make the request.
25334  */
25335 Roo.data.HttpProxy = function(conn){
25336     Roo.data.HttpProxy.superclass.constructor.call(this);
25337     // is conn a conn config or a real conn?
25338     this.conn = conn;
25339     this.useAjax = !conn || !conn.events;
25340   
25341 };
25342
25343 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25344     // thse are take from connection...
25345     
25346     /**
25347      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25348      */
25349     /**
25350      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25351      * extra parameters to each request made by this object. (defaults to undefined)
25352      */
25353     /**
25354      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25355      *  to each request made by this object. (defaults to undefined)
25356      */
25357     /**
25358      * @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)
25359      */
25360     /**
25361      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25362      */
25363      /**
25364      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25365      * @type Boolean
25366      */
25367   
25368
25369     /**
25370      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25371      * @type Boolean
25372      */
25373     /**
25374      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25375      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25376      * a finer-grained basis than the DataProxy events.
25377      */
25378     getConnection : function(){
25379         return this.useAjax ? Roo.Ajax : this.conn;
25380     },
25381
25382     /**
25383      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25384      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25385      * process that block using the passed callback.
25386      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25387      * for the request to the remote server.
25388      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25389      * object into a block of Roo.data.Records.
25390      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25391      * The function must be passed <ul>
25392      * <li>The Record block object</li>
25393      * <li>The "arg" argument from the load function</li>
25394      * <li>A boolean success indicator</li>
25395      * </ul>
25396      * @param {Object} scope The scope in which to call the callback
25397      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25398      */
25399     load : function(params, reader, callback, scope, arg){
25400         if(this.fireEvent("beforeload", this, params) !== false){
25401             var  o = {
25402                 params : params || {},
25403                 request: {
25404                     callback : callback,
25405                     scope : scope,
25406                     arg : arg
25407                 },
25408                 reader: reader,
25409                 callback : this.loadResponse,
25410                 scope: this
25411             };
25412             if(this.useAjax){
25413                 Roo.applyIf(o, this.conn);
25414                 if(this.activeRequest){
25415                     Roo.Ajax.abort(this.activeRequest);
25416                 }
25417                 this.activeRequest = Roo.Ajax.request(o);
25418             }else{
25419                 this.conn.request(o);
25420             }
25421         }else{
25422             callback.call(scope||this, null, arg, false);
25423         }
25424     },
25425
25426     // private
25427     loadResponse : function(o, success, response){
25428         delete this.activeRequest;
25429         if(!success){
25430             this.fireEvent("loadexception", this, o, response);
25431             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25432             return;
25433         }
25434         var result;
25435         try {
25436             result = o.reader.read(response);
25437         }catch(e){
25438             this.fireEvent("loadexception", this, o, response, e);
25439             o.request.callback.call(o.request.scope, {
25440                     success : false,
25441                     raw : {
25442                         errorMsg : response.responseText
25443                     }
25444                     
25445                 }, o.request.arg, false);
25446             return;
25447         }
25448         
25449         this.fireEvent("load", this, o, o.request.arg);
25450         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25451     },
25452
25453     // private
25454     update : function(dataSet){
25455
25456     },
25457
25458     // private
25459     updateResponse : function(dataSet){
25460
25461     }
25462 });/*
25463  * Based on:
25464  * Ext JS Library 1.1.1
25465  * Copyright(c) 2006-2007, Ext JS, LLC.
25466  *
25467  * Originally Released Under LGPL - original licence link has changed is not relivant.
25468  *
25469  * Fork - LGPL
25470  * <script type="text/javascript">
25471  */
25472
25473 /**
25474  * @class Roo.data.ScriptTagProxy
25475  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25476  * other than the originating domain of the running page.<br><br>
25477  * <p>
25478  * <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
25479  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25480  * <p>
25481  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25482  * source code that is used as the source inside a &lt;script> tag.<br><br>
25483  * <p>
25484  * In order for the browser to process the returned data, the server must wrap the data object
25485  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25486  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25487  * depending on whether the callback name was passed:
25488  * <p>
25489  * <pre><code>
25490 boolean scriptTag = false;
25491 String cb = request.getParameter("callback");
25492 if (cb != null) {
25493     scriptTag = true;
25494     response.setContentType("text/javascript");
25495 } else {
25496     response.setContentType("application/x-json");
25497 }
25498 Writer out = response.getWriter();
25499 if (scriptTag) {
25500     out.write(cb + "(");
25501 }
25502 out.print(dataBlock.toJsonString());
25503 if (scriptTag) {
25504     out.write(");");
25505 }
25506 </pre></code>
25507  *
25508  * @constructor
25509  * @param {Object} config A configuration object.
25510  */
25511 Roo.data.ScriptTagProxy = function(config){
25512     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25513     Roo.apply(this, config);
25514     this.head = document.getElementsByTagName("head")[0];
25515 };
25516
25517 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25518
25519 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25520     /**
25521      * @cfg {String} url The URL from which to request the data object.
25522      */
25523     /**
25524      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25525      */
25526     timeout : 30000,
25527     /**
25528      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25529      * the server the name of the callback function set up by the load call to process the returned data object.
25530      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25531      * javascript output which calls this named function passing the data object as its only parameter.
25532      */
25533     callbackParam : "callback",
25534     /**
25535      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25536      * name to the request.
25537      */
25538     nocache : true,
25539
25540     /**
25541      * Load data from the configured URL, read the data object into
25542      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25543      * process that block using the passed callback.
25544      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25545      * for the request to the remote server.
25546      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25547      * object into a block of Roo.data.Records.
25548      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25549      * The function must be passed <ul>
25550      * <li>The Record block object</li>
25551      * <li>The "arg" argument from the load function</li>
25552      * <li>A boolean success indicator</li>
25553      * </ul>
25554      * @param {Object} scope The scope in which to call the callback
25555      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25556      */
25557     load : function(params, reader, callback, scope, arg){
25558         if(this.fireEvent("beforeload", this, params) !== false){
25559
25560             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25561
25562             var url = this.url;
25563             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25564             if(this.nocache){
25565                 url += "&_dc=" + (new Date().getTime());
25566             }
25567             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25568             var trans = {
25569                 id : transId,
25570                 cb : "stcCallback"+transId,
25571                 scriptId : "stcScript"+transId,
25572                 params : params,
25573                 arg : arg,
25574                 url : url,
25575                 callback : callback,
25576                 scope : scope,
25577                 reader : reader
25578             };
25579             var conn = this;
25580
25581             window[trans.cb] = function(o){
25582                 conn.handleResponse(o, trans);
25583             };
25584
25585             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25586
25587             if(this.autoAbort !== false){
25588                 this.abort();
25589             }
25590
25591             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25592
25593             var script = document.createElement("script");
25594             script.setAttribute("src", url);
25595             script.setAttribute("type", "text/javascript");
25596             script.setAttribute("id", trans.scriptId);
25597             this.head.appendChild(script);
25598
25599             this.trans = trans;
25600         }else{
25601             callback.call(scope||this, null, arg, false);
25602         }
25603     },
25604
25605     // private
25606     isLoading : function(){
25607         return this.trans ? true : false;
25608     },
25609
25610     /**
25611      * Abort the current server request.
25612      */
25613     abort : function(){
25614         if(this.isLoading()){
25615             this.destroyTrans(this.trans);
25616         }
25617     },
25618
25619     // private
25620     destroyTrans : function(trans, isLoaded){
25621         this.head.removeChild(document.getElementById(trans.scriptId));
25622         clearTimeout(trans.timeoutId);
25623         if(isLoaded){
25624             window[trans.cb] = undefined;
25625             try{
25626                 delete window[trans.cb];
25627             }catch(e){}
25628         }else{
25629             // if hasn't been loaded, wait for load to remove it to prevent script error
25630             window[trans.cb] = function(){
25631                 window[trans.cb] = undefined;
25632                 try{
25633                     delete window[trans.cb];
25634                 }catch(e){}
25635             };
25636         }
25637     },
25638
25639     // private
25640     handleResponse : function(o, trans){
25641         this.trans = false;
25642         this.destroyTrans(trans, true);
25643         var result;
25644         try {
25645             result = trans.reader.readRecords(o);
25646         }catch(e){
25647             this.fireEvent("loadexception", this, o, trans.arg, e);
25648             trans.callback.call(trans.scope||window, null, trans.arg, false);
25649             return;
25650         }
25651         this.fireEvent("load", this, o, trans.arg);
25652         trans.callback.call(trans.scope||window, result, trans.arg, true);
25653     },
25654
25655     // private
25656     handleFailure : function(trans){
25657         this.trans = false;
25658         this.destroyTrans(trans, false);
25659         this.fireEvent("loadexception", this, null, trans.arg);
25660         trans.callback.call(trans.scope||window, null, trans.arg, false);
25661     }
25662 });/*
25663  * Based on:
25664  * Ext JS Library 1.1.1
25665  * Copyright(c) 2006-2007, Ext JS, LLC.
25666  *
25667  * Originally Released Under LGPL - original licence link has changed is not relivant.
25668  *
25669  * Fork - LGPL
25670  * <script type="text/javascript">
25671  */
25672
25673 /**
25674  * @class Roo.data.JsonReader
25675  * @extends Roo.data.DataReader
25676  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25677  * based on mappings in a provided Roo.data.Record constructor.
25678  * 
25679  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25680  * in the reply previously. 
25681  * 
25682  * <p>
25683  * Example code:
25684  * <pre><code>
25685 var RecordDef = Roo.data.Record.create([
25686     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25687     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25688 ]);
25689 var myReader = new Roo.data.JsonReader({
25690     totalProperty: "results",    // The property which contains the total dataset size (optional)
25691     root: "rows",                // The property which contains an Array of row objects
25692     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25693 }, RecordDef);
25694 </code></pre>
25695  * <p>
25696  * This would consume a JSON file like this:
25697  * <pre><code>
25698 { 'results': 2, 'rows': [
25699     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25700     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25701 }
25702 </code></pre>
25703  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25704  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25705  * paged from the remote server.
25706  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25707  * @cfg {String} root name of the property which contains the Array of row objects.
25708  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25709  * @cfg {Array} fields Array of field definition objects
25710  * @constructor
25711  * Create a new JsonReader
25712  * @param {Object} meta Metadata configuration options
25713  * @param {Object} recordType Either an Array of field definition objects,
25714  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25715  */
25716 Roo.data.JsonReader = function(meta, recordType){
25717     
25718     meta = meta || {};
25719     // set some defaults:
25720     Roo.applyIf(meta, {
25721         totalProperty: 'total',
25722         successProperty : 'success',
25723         root : 'data',
25724         id : 'id'
25725     });
25726     
25727     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25728 };
25729 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25730     
25731     readerType : 'Json',
25732     
25733     /**
25734      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25735      * Used by Store query builder to append _requestMeta to params.
25736      * 
25737      */
25738     metaFromRemote : false,
25739     /**
25740      * This method is only used by a DataProxy which has retrieved data from a remote server.
25741      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25742      * @return {Object} data A data block which is used by an Roo.data.Store object as
25743      * a cache of Roo.data.Records.
25744      */
25745     read : function(response){
25746         var json = response.responseText;
25747        
25748         var o = /* eval:var:o */ eval("("+json+")");
25749         if(!o) {
25750             throw {message: "JsonReader.read: Json object not found"};
25751         }
25752         
25753         if(o.metaData){
25754             
25755             delete this.ef;
25756             this.metaFromRemote = true;
25757             this.meta = o.metaData;
25758             this.recordType = Roo.data.Record.create(o.metaData.fields);
25759             this.onMetaChange(this.meta, this.recordType, o);
25760         }
25761         return this.readRecords(o);
25762     },
25763
25764     // private function a store will implement
25765     onMetaChange : function(meta, recordType, o){
25766
25767     },
25768
25769     /**
25770          * @ignore
25771          */
25772     simpleAccess: function(obj, subsc) {
25773         return obj[subsc];
25774     },
25775
25776         /**
25777          * @ignore
25778          */
25779     getJsonAccessor: function(){
25780         var re = /[\[\.]/;
25781         return function(expr) {
25782             try {
25783                 return(re.test(expr))
25784                     ? new Function("obj", "return obj." + expr)
25785                     : function(obj){
25786                         return obj[expr];
25787                     };
25788             } catch(e){}
25789             return Roo.emptyFn;
25790         };
25791     }(),
25792
25793     /**
25794      * Create a data block containing Roo.data.Records from an XML document.
25795      * @param {Object} o An object which contains an Array of row objects in the property specified
25796      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25797      * which contains the total size of the dataset.
25798      * @return {Object} data A data block which is used by an Roo.data.Store object as
25799      * a cache of Roo.data.Records.
25800      */
25801     readRecords : function(o){
25802         /**
25803          * After any data loads, the raw JSON data is available for further custom processing.
25804          * @type Object
25805          */
25806         this.o = o;
25807         var s = this.meta, Record = this.recordType,
25808             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25809
25810 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25811         if (!this.ef) {
25812             if(s.totalProperty) {
25813                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25814                 }
25815                 if(s.successProperty) {
25816                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25817                 }
25818                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25819                 if (s.id) {
25820                         var g = this.getJsonAccessor(s.id);
25821                         this.getId = function(rec) {
25822                                 var r = g(rec);  
25823                                 return (r === undefined || r === "") ? null : r;
25824                         };
25825                 } else {
25826                         this.getId = function(){return null;};
25827                 }
25828             this.ef = [];
25829             for(var jj = 0; jj < fl; jj++){
25830                 f = fi[jj];
25831                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25832                 this.ef[jj] = this.getJsonAccessor(map);
25833             }
25834         }
25835
25836         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25837         if(s.totalProperty){
25838             var vt = parseInt(this.getTotal(o), 10);
25839             if(!isNaN(vt)){
25840                 totalRecords = vt;
25841             }
25842         }
25843         if(s.successProperty){
25844             var vs = this.getSuccess(o);
25845             if(vs === false || vs === 'false'){
25846                 success = false;
25847             }
25848         }
25849         var records = [];
25850         for(var i = 0; i < c; i++){
25851                 var n = root[i];
25852             var values = {};
25853             var id = this.getId(n);
25854             for(var j = 0; j < fl; j++){
25855                 f = fi[j];
25856             var v = this.ef[j](n);
25857             if (!f.convert) {
25858                 Roo.log('missing convert for ' + f.name);
25859                 Roo.log(f);
25860                 continue;
25861             }
25862             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25863             }
25864             var record = new Record(values, id);
25865             record.json = n;
25866             records[i] = record;
25867         }
25868         return {
25869             raw : o,
25870             success : success,
25871             records : records,
25872             totalRecords : totalRecords
25873         };
25874     },
25875     // used when loading children.. @see loadDataFromChildren
25876     toLoadData: function(rec)
25877     {
25878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25879         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25880         return { data : data, total : data.length };
25881         
25882     }
25883 });/*
25884  * Based on:
25885  * Ext JS Library 1.1.1
25886  * Copyright(c) 2006-2007, Ext JS, LLC.
25887  *
25888  * Originally Released Under LGPL - original licence link has changed is not relivant.
25889  *
25890  * Fork - LGPL
25891  * <script type="text/javascript">
25892  */
25893
25894 /**
25895  * @class Roo.data.XmlReader
25896  * @extends Roo.data.DataReader
25897  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25898  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25899  * <p>
25900  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25901  * header in the HTTP response must be set to "text/xml".</em>
25902  * <p>
25903  * Example code:
25904  * <pre><code>
25905 var RecordDef = Roo.data.Record.create([
25906    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25907    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25908 ]);
25909 var myReader = new Roo.data.XmlReader({
25910    totalRecords: "results", // The element which contains the total dataset size (optional)
25911    record: "row",           // The repeated element which contains row information
25912    id: "id"                 // The element within the row that provides an ID for the record (optional)
25913 }, RecordDef);
25914 </code></pre>
25915  * <p>
25916  * This would consume an XML file like this:
25917  * <pre><code>
25918 &lt;?xml?>
25919 &lt;dataset>
25920  &lt;results>2&lt;/results>
25921  &lt;row>
25922    &lt;id>1&lt;/id>
25923    &lt;name>Bill&lt;/name>
25924    &lt;occupation>Gardener&lt;/occupation>
25925  &lt;/row>
25926  &lt;row>
25927    &lt;id>2&lt;/id>
25928    &lt;name>Ben&lt;/name>
25929    &lt;occupation>Horticulturalist&lt;/occupation>
25930  &lt;/row>
25931 &lt;/dataset>
25932 </code></pre>
25933  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25934  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25935  * paged from the remote server.
25936  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25937  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25938  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25939  * a record identifier value.
25940  * @constructor
25941  * Create a new XmlReader
25942  * @param {Object} meta Metadata configuration options
25943  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25944  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25945  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25946  */
25947 Roo.data.XmlReader = function(meta, recordType){
25948     meta = meta || {};
25949     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25950 };
25951 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25952     
25953     readerType : 'Xml',
25954     
25955     /**
25956      * This method is only used by a DataProxy which has retrieved data from a remote server.
25957          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25958          * to contain a method called 'responseXML' that returns an XML document object.
25959      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25960      * a cache of Roo.data.Records.
25961      */
25962     read : function(response){
25963         var doc = response.responseXML;
25964         if(!doc) {
25965             throw {message: "XmlReader.read: XML Document not available"};
25966         }
25967         return this.readRecords(doc);
25968     },
25969
25970     /**
25971      * Create a data block containing Roo.data.Records from an XML document.
25972          * @param {Object} doc A parsed XML document.
25973      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25974      * a cache of Roo.data.Records.
25975      */
25976     readRecords : function(doc){
25977         /**
25978          * After any data loads/reads, the raw XML Document is available for further custom processing.
25979          * @type XMLDocument
25980          */
25981         this.xmlData = doc;
25982         var root = doc.documentElement || doc;
25983         var q = Roo.DomQuery;
25984         var recordType = this.recordType, fields = recordType.prototype.fields;
25985         var sid = this.meta.id;
25986         var totalRecords = 0, success = true;
25987         if(this.meta.totalRecords){
25988             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25989         }
25990         
25991         if(this.meta.success){
25992             var sv = q.selectValue(this.meta.success, root, true);
25993             success = sv !== false && sv !== 'false';
25994         }
25995         var records = [];
25996         var ns = q.select(this.meta.record, root);
25997         for(var i = 0, len = ns.length; i < len; i++) {
25998                 var n = ns[i];
25999                 var values = {};
26000                 var id = sid ? q.selectValue(sid, n) : undefined;
26001                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26002                     var f = fields.items[j];
26003                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26004                     v = f.convert(v);
26005                     values[f.name] = v;
26006                 }
26007                 var record = new recordType(values, id);
26008                 record.node = n;
26009                 records[records.length] = record;
26010             }
26011
26012             return {
26013                 success : success,
26014                 records : records,
26015                 totalRecords : totalRecords || records.length
26016             };
26017     }
26018 });/*
26019  * Based on:
26020  * Ext JS Library 1.1.1
26021  * Copyright(c) 2006-2007, Ext JS, LLC.
26022  *
26023  * Originally Released Under LGPL - original licence link has changed is not relivant.
26024  *
26025  * Fork - LGPL
26026  * <script type="text/javascript">
26027  */
26028
26029 /**
26030  * @class Roo.data.ArrayReader
26031  * @extends Roo.data.DataReader
26032  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26033  * Each element of that Array represents a row of data fields. The
26034  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26035  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26036  * <p>
26037  * Example code:.
26038  * <pre><code>
26039 var RecordDef = Roo.data.Record.create([
26040     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26041     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26042 ]);
26043 var myReader = new Roo.data.ArrayReader({
26044     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26045 }, RecordDef);
26046 </code></pre>
26047  * <p>
26048  * This would consume an Array like this:
26049  * <pre><code>
26050 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26051   </code></pre>
26052  
26053  * @constructor
26054  * Create a new JsonReader
26055  * @param {Object} meta Metadata configuration options.
26056  * @param {Object|Array} recordType Either an Array of field definition objects
26057  * 
26058  * @cfg {Array} fields Array of field definition objects
26059  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26060  * as specified to {@link Roo.data.Record#create},
26061  * or an {@link Roo.data.Record} object
26062  *
26063  * 
26064  * created using {@link Roo.data.Record#create}.
26065  */
26066 Roo.data.ArrayReader = function(meta, recordType)
26067 {    
26068     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26069 };
26070
26071 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26072     
26073       /**
26074      * Create a data block containing Roo.data.Records from an XML document.
26075      * @param {Object} o An Array of row objects which represents the dataset.
26076      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26077      * a cache of Roo.data.Records.
26078      */
26079     readRecords : function(o)
26080     {
26081         var sid = this.meta ? this.meta.id : null;
26082         var recordType = this.recordType, fields = recordType.prototype.fields;
26083         var records = [];
26084         var root = o;
26085         for(var i = 0; i < root.length; i++){
26086             var n = root[i];
26087             var values = {};
26088             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26089             for(var j = 0, jlen = fields.length; j < jlen; j++){
26090                 var f = fields.items[j];
26091                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26092                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26093                 v = f.convert(v);
26094                 values[f.name] = v;
26095             }
26096             var record = new recordType(values, id);
26097             record.json = n;
26098             records[records.length] = record;
26099         }
26100         return {
26101             records : records,
26102             totalRecords : records.length
26103         };
26104     },
26105     // used when loading children.. @see loadDataFromChildren
26106     toLoadData: function(rec)
26107     {
26108         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26109         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26110         
26111     }
26112     
26113     
26114 });/*
26115  * Based on:
26116  * Ext JS Library 1.1.1
26117  * Copyright(c) 2006-2007, Ext JS, LLC.
26118  *
26119  * Originally Released Under LGPL - original licence link has changed is not relivant.
26120  *
26121  * Fork - LGPL
26122  * <script type="text/javascript">
26123  */
26124
26125
26126 /**
26127  * @class Roo.data.Tree
26128  * @extends Roo.util.Observable
26129  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26130  * in the tree have most standard DOM functionality.
26131  * @constructor
26132  * @param {Node} root (optional) The root node
26133  */
26134 Roo.data.Tree = function(root){
26135    this.nodeHash = {};
26136    /**
26137     * The root node for this tree
26138     * @type Node
26139     */
26140    this.root = null;
26141    if(root){
26142        this.setRootNode(root);
26143    }
26144    this.addEvents({
26145        /**
26146         * @event append
26147         * Fires when a new child node is appended to a node in this tree.
26148         * @param {Tree} tree The owner tree
26149         * @param {Node} parent The parent node
26150         * @param {Node} node The newly appended node
26151         * @param {Number} index The index of the newly appended node
26152         */
26153        "append" : true,
26154        /**
26155         * @event remove
26156         * Fires when a child node is removed from a node in this tree.
26157         * @param {Tree} tree The owner tree
26158         * @param {Node} parent The parent node
26159         * @param {Node} node The child node removed
26160         */
26161        "remove" : true,
26162        /**
26163         * @event move
26164         * Fires when a node is moved to a new location in the tree
26165         * @param {Tree} tree The owner tree
26166         * @param {Node} node The node moved
26167         * @param {Node} oldParent The old parent of this node
26168         * @param {Node} newParent The new parent of this node
26169         * @param {Number} index The index it was moved to
26170         */
26171        "move" : true,
26172        /**
26173         * @event insert
26174         * Fires when a new child node is inserted in a node in this tree.
26175         * @param {Tree} tree The owner tree
26176         * @param {Node} parent The parent node
26177         * @param {Node} node The child node inserted
26178         * @param {Node} refNode The child node the node was inserted before
26179         */
26180        "insert" : true,
26181        /**
26182         * @event beforeappend
26183         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26184         * @param {Tree} tree The owner tree
26185         * @param {Node} parent The parent node
26186         * @param {Node} node The child node to be appended
26187         */
26188        "beforeappend" : true,
26189        /**
26190         * @event beforeremove
26191         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26192         * @param {Tree} tree The owner tree
26193         * @param {Node} parent The parent node
26194         * @param {Node} node The child node to be removed
26195         */
26196        "beforeremove" : true,
26197        /**
26198         * @event beforemove
26199         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26200         * @param {Tree} tree The owner tree
26201         * @param {Node} node The node being moved
26202         * @param {Node} oldParent The parent of the node
26203         * @param {Node} newParent The new parent the node is moving to
26204         * @param {Number} index The index it is being moved to
26205         */
26206        "beforemove" : true,
26207        /**
26208         * @event beforeinsert
26209         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26210         * @param {Tree} tree The owner tree
26211         * @param {Node} parent The parent node
26212         * @param {Node} node The child node to be inserted
26213         * @param {Node} refNode The child node the node is being inserted before
26214         */
26215        "beforeinsert" : true
26216    });
26217
26218     Roo.data.Tree.superclass.constructor.call(this);
26219 };
26220
26221 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26222     pathSeparator: "/",
26223
26224     proxyNodeEvent : function(){
26225         return this.fireEvent.apply(this, arguments);
26226     },
26227
26228     /**
26229      * Returns the root node for this tree.
26230      * @return {Node}
26231      */
26232     getRootNode : function(){
26233         return this.root;
26234     },
26235
26236     /**
26237      * Sets the root node for this tree.
26238      * @param {Node} node
26239      * @return {Node}
26240      */
26241     setRootNode : function(node){
26242         this.root = node;
26243         node.ownerTree = this;
26244         node.isRoot = true;
26245         this.registerNode(node);
26246         return node;
26247     },
26248
26249     /**
26250      * Gets a node in this tree by its id.
26251      * @param {String} id
26252      * @return {Node}
26253      */
26254     getNodeById : function(id){
26255         return this.nodeHash[id];
26256     },
26257
26258     registerNode : function(node){
26259         this.nodeHash[node.id] = node;
26260     },
26261
26262     unregisterNode : function(node){
26263         delete this.nodeHash[node.id];
26264     },
26265
26266     toString : function(){
26267         return "[Tree"+(this.id?" "+this.id:"")+"]";
26268     }
26269 });
26270
26271 /**
26272  * @class Roo.data.Node
26273  * @extends Roo.util.Observable
26274  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26275  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26276  * @constructor
26277  * @param {Object} attributes The attributes/config for the node
26278  */
26279 Roo.data.Node = function(attributes){
26280     /**
26281      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26282      * @type {Object}
26283      */
26284     this.attributes = attributes || {};
26285     this.leaf = this.attributes.leaf;
26286     /**
26287      * The node id. @type String
26288      */
26289     this.id = this.attributes.id;
26290     if(!this.id){
26291         this.id = Roo.id(null, "ynode-");
26292         this.attributes.id = this.id;
26293     }
26294      
26295     
26296     /**
26297      * All child nodes of this node. @type Array
26298      */
26299     this.childNodes = [];
26300     if(!this.childNodes.indexOf){ // indexOf is a must
26301         this.childNodes.indexOf = function(o){
26302             for(var i = 0, len = this.length; i < len; i++){
26303                 if(this[i] == o) {
26304                     return i;
26305                 }
26306             }
26307             return -1;
26308         };
26309     }
26310     /**
26311      * The parent node for this node. @type Node
26312      */
26313     this.parentNode = null;
26314     /**
26315      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26316      */
26317     this.firstChild = null;
26318     /**
26319      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26320      */
26321     this.lastChild = null;
26322     /**
26323      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26324      */
26325     this.previousSibling = null;
26326     /**
26327      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26328      */
26329     this.nextSibling = null;
26330
26331     this.addEvents({
26332        /**
26333         * @event append
26334         * Fires when a new child node is appended
26335         * @param {Tree} tree The owner tree
26336         * @param {Node} this This node
26337         * @param {Node} node The newly appended node
26338         * @param {Number} index The index of the newly appended node
26339         */
26340        "append" : true,
26341        /**
26342         * @event remove
26343         * Fires when a child node is removed
26344         * @param {Tree} tree The owner tree
26345         * @param {Node} this This node
26346         * @param {Node} node The removed node
26347         */
26348        "remove" : true,
26349        /**
26350         * @event move
26351         * Fires when this node is moved to a new location in the tree
26352         * @param {Tree} tree The owner tree
26353         * @param {Node} this This node
26354         * @param {Node} oldParent The old parent of this node
26355         * @param {Node} newParent The new parent of this node
26356         * @param {Number} index The index it was moved to
26357         */
26358        "move" : true,
26359        /**
26360         * @event insert
26361         * Fires when a new child node is inserted.
26362         * @param {Tree} tree The owner tree
26363         * @param {Node} this This node
26364         * @param {Node} node The child node inserted
26365         * @param {Node} refNode The child node the node was inserted before
26366         */
26367        "insert" : true,
26368        /**
26369         * @event beforeappend
26370         * Fires before a new child is appended, return false to cancel the append.
26371         * @param {Tree} tree The owner tree
26372         * @param {Node} this This node
26373         * @param {Node} node The child node to be appended
26374         */
26375        "beforeappend" : true,
26376        /**
26377         * @event beforeremove
26378         * Fires before a child is removed, return false to cancel the remove.
26379         * @param {Tree} tree The owner tree
26380         * @param {Node} this This node
26381         * @param {Node} node The child node to be removed
26382         */
26383        "beforeremove" : true,
26384        /**
26385         * @event beforemove
26386         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26387         * @param {Tree} tree The owner tree
26388         * @param {Node} this This node
26389         * @param {Node} oldParent The parent of this node
26390         * @param {Node} newParent The new parent this node is moving to
26391         * @param {Number} index The index it is being moved to
26392         */
26393        "beforemove" : true,
26394        /**
26395         * @event beforeinsert
26396         * Fires before a new child is inserted, return false to cancel the insert.
26397         * @param {Tree} tree The owner tree
26398         * @param {Node} this This node
26399         * @param {Node} node The child node to be inserted
26400         * @param {Node} refNode The child node the node is being inserted before
26401         */
26402        "beforeinsert" : true
26403    });
26404     this.listeners = this.attributes.listeners;
26405     Roo.data.Node.superclass.constructor.call(this);
26406 };
26407
26408 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26409     fireEvent : function(evtName){
26410         // first do standard event for this node
26411         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26412             return false;
26413         }
26414         // then bubble it up to the tree if the event wasn't cancelled
26415         var ot = this.getOwnerTree();
26416         if(ot){
26417             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26418                 return false;
26419             }
26420         }
26421         return true;
26422     },
26423
26424     /**
26425      * Returns true if this node is a leaf
26426      * @return {Boolean}
26427      */
26428     isLeaf : function(){
26429         return this.leaf === true;
26430     },
26431
26432     // private
26433     setFirstChild : function(node){
26434         this.firstChild = node;
26435     },
26436
26437     //private
26438     setLastChild : function(node){
26439         this.lastChild = node;
26440     },
26441
26442
26443     /**
26444      * Returns true if this node is the last child of its parent
26445      * @return {Boolean}
26446      */
26447     isLast : function(){
26448        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26449     },
26450
26451     /**
26452      * Returns true if this node is the first child of its parent
26453      * @return {Boolean}
26454      */
26455     isFirst : function(){
26456        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26457     },
26458
26459     hasChildNodes : function(){
26460         return !this.isLeaf() && this.childNodes.length > 0;
26461     },
26462
26463     /**
26464      * Insert node(s) as the last child node of this node.
26465      * @param {Node/Array} node The node or Array of nodes to append
26466      * @return {Node} The appended node if single append, or null if an array was passed
26467      */
26468     appendChild : function(node){
26469         var multi = false;
26470         if(node instanceof Array){
26471             multi = node;
26472         }else if(arguments.length > 1){
26473             multi = arguments;
26474         }
26475         
26476         // if passed an array or multiple args do them one by one
26477         if(multi){
26478             for(var i = 0, len = multi.length; i < len; i++) {
26479                 this.appendChild(multi[i]);
26480             }
26481         }else{
26482             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26483                 return false;
26484             }
26485             var index = this.childNodes.length;
26486             var oldParent = node.parentNode;
26487             // it's a move, make sure we move it cleanly
26488             if(oldParent){
26489                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26490                     return false;
26491                 }
26492                 oldParent.removeChild(node);
26493             }
26494             
26495             index = this.childNodes.length;
26496             if(index == 0){
26497                 this.setFirstChild(node);
26498             }
26499             this.childNodes.push(node);
26500             node.parentNode = this;
26501             var ps = this.childNodes[index-1];
26502             if(ps){
26503                 node.previousSibling = ps;
26504                 ps.nextSibling = node;
26505             }else{
26506                 node.previousSibling = null;
26507             }
26508             node.nextSibling = null;
26509             this.setLastChild(node);
26510             node.setOwnerTree(this.getOwnerTree());
26511             this.fireEvent("append", this.ownerTree, this, node, index);
26512             if(this.ownerTree) {
26513                 this.ownerTree.fireEvent("appendnode", this, node, index);
26514             }
26515             if(oldParent){
26516                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26517             }
26518             return node;
26519         }
26520     },
26521
26522     /**
26523      * Removes a child node from this node.
26524      * @param {Node} node The node to remove
26525      * @return {Node} The removed node
26526      */
26527     removeChild : function(node){
26528         var index = this.childNodes.indexOf(node);
26529         if(index == -1){
26530             return false;
26531         }
26532         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26533             return false;
26534         }
26535
26536         // remove it from childNodes collection
26537         this.childNodes.splice(index, 1);
26538
26539         // update siblings
26540         if(node.previousSibling){
26541             node.previousSibling.nextSibling = node.nextSibling;
26542         }
26543         if(node.nextSibling){
26544             node.nextSibling.previousSibling = node.previousSibling;
26545         }
26546
26547         // update child refs
26548         if(this.firstChild == node){
26549             this.setFirstChild(node.nextSibling);
26550         }
26551         if(this.lastChild == node){
26552             this.setLastChild(node.previousSibling);
26553         }
26554
26555         node.setOwnerTree(null);
26556         // clear any references from the node
26557         node.parentNode = null;
26558         node.previousSibling = null;
26559         node.nextSibling = null;
26560         this.fireEvent("remove", this.ownerTree, this, node);
26561         return node;
26562     },
26563
26564     /**
26565      * Inserts the first node before the second node in this nodes childNodes collection.
26566      * @param {Node} node The node to insert
26567      * @param {Node} refNode The node to insert before (if null the node is appended)
26568      * @return {Node} The inserted node
26569      */
26570     insertBefore : function(node, refNode){
26571         if(!refNode){ // like standard Dom, refNode can be null for append
26572             return this.appendChild(node);
26573         }
26574         // nothing to do
26575         if(node == refNode){
26576             return false;
26577         }
26578
26579         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26580             return false;
26581         }
26582         var index = this.childNodes.indexOf(refNode);
26583         var oldParent = node.parentNode;
26584         var refIndex = index;
26585
26586         // when moving internally, indexes will change after remove
26587         if(oldParent == this && this.childNodes.indexOf(node) < index){
26588             refIndex--;
26589         }
26590
26591         // it's a move, make sure we move it cleanly
26592         if(oldParent){
26593             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26594                 return false;
26595             }
26596             oldParent.removeChild(node);
26597         }
26598         if(refIndex == 0){
26599             this.setFirstChild(node);
26600         }
26601         this.childNodes.splice(refIndex, 0, node);
26602         node.parentNode = this;
26603         var ps = this.childNodes[refIndex-1];
26604         if(ps){
26605             node.previousSibling = ps;
26606             ps.nextSibling = node;
26607         }else{
26608             node.previousSibling = null;
26609         }
26610         node.nextSibling = refNode;
26611         refNode.previousSibling = node;
26612         node.setOwnerTree(this.getOwnerTree());
26613         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26614         if(oldParent){
26615             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26616         }
26617         return node;
26618     },
26619
26620     /**
26621      * Returns the child node at the specified index.
26622      * @param {Number} index
26623      * @return {Node}
26624      */
26625     item : function(index){
26626         return this.childNodes[index];
26627     },
26628
26629     /**
26630      * Replaces one child node in this node with another.
26631      * @param {Node} newChild The replacement node
26632      * @param {Node} oldChild The node to replace
26633      * @return {Node} The replaced node
26634      */
26635     replaceChild : function(newChild, oldChild){
26636         this.insertBefore(newChild, oldChild);
26637         this.removeChild(oldChild);
26638         return oldChild;
26639     },
26640
26641     /**
26642      * Returns the index of a child node
26643      * @param {Node} node
26644      * @return {Number} The index of the node or -1 if it was not found
26645      */
26646     indexOf : function(child){
26647         return this.childNodes.indexOf(child);
26648     },
26649
26650     /**
26651      * Returns the tree this node is in.
26652      * @return {Tree}
26653      */
26654     getOwnerTree : function(){
26655         // if it doesn't have one, look for one
26656         if(!this.ownerTree){
26657             var p = this;
26658             while(p){
26659                 if(p.ownerTree){
26660                     this.ownerTree = p.ownerTree;
26661                     break;
26662                 }
26663                 p = p.parentNode;
26664             }
26665         }
26666         return this.ownerTree;
26667     },
26668
26669     /**
26670      * Returns depth of this node (the root node has a depth of 0)
26671      * @return {Number}
26672      */
26673     getDepth : function(){
26674         var depth = 0;
26675         var p = this;
26676         while(p.parentNode){
26677             ++depth;
26678             p = p.parentNode;
26679         }
26680         return depth;
26681     },
26682
26683     // private
26684     setOwnerTree : function(tree){
26685         // if it's move, we need to update everyone
26686         if(tree != this.ownerTree){
26687             if(this.ownerTree){
26688                 this.ownerTree.unregisterNode(this);
26689             }
26690             this.ownerTree = tree;
26691             var cs = this.childNodes;
26692             for(var i = 0, len = cs.length; i < len; i++) {
26693                 cs[i].setOwnerTree(tree);
26694             }
26695             if(tree){
26696                 tree.registerNode(this);
26697             }
26698         }
26699     },
26700
26701     /**
26702      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26703      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26704      * @return {String} The path
26705      */
26706     getPath : function(attr){
26707         attr = attr || "id";
26708         var p = this.parentNode;
26709         var b = [this.attributes[attr]];
26710         while(p){
26711             b.unshift(p.attributes[attr]);
26712             p = p.parentNode;
26713         }
26714         var sep = this.getOwnerTree().pathSeparator;
26715         return sep + b.join(sep);
26716     },
26717
26718     /**
26719      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26720      * function call will be the scope provided or the current node. The arguments to the function
26721      * will be the args provided or the current node. If the function returns false at any point,
26722      * the bubble is stopped.
26723      * @param {Function} fn The function to call
26724      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26725      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26726      */
26727     bubble : function(fn, scope, args){
26728         var p = this;
26729         while(p){
26730             if(fn.call(scope || p, args || p) === false){
26731                 break;
26732             }
26733             p = p.parentNode;
26734         }
26735     },
26736
26737     /**
26738      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26739      * function call will be the scope provided or the current node. The arguments to the function
26740      * will be the args provided or the current node. If the function returns false at any point,
26741      * the cascade is stopped on that branch.
26742      * @param {Function} fn The function to call
26743      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26744      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26745      */
26746     cascade : function(fn, scope, args){
26747         if(fn.call(scope || this, args || this) !== false){
26748             var cs = this.childNodes;
26749             for(var i = 0, len = cs.length; i < len; i++) {
26750                 cs[i].cascade(fn, scope, args);
26751             }
26752         }
26753     },
26754
26755     /**
26756      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26757      * function call will be the scope provided or the current node. The arguments to the function
26758      * will be the args provided or the current node. If the function returns false at any point,
26759      * the iteration stops.
26760      * @param {Function} fn The function to call
26761      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26762      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26763      */
26764     eachChild : function(fn, scope, args){
26765         var cs = this.childNodes;
26766         for(var i = 0, len = cs.length; i < len; i++) {
26767                 if(fn.call(scope || this, args || cs[i]) === false){
26768                     break;
26769                 }
26770         }
26771     },
26772
26773     /**
26774      * Finds the first child that has the attribute with the specified value.
26775      * @param {String} attribute The attribute name
26776      * @param {Mixed} value The value to search for
26777      * @return {Node} The found child or null if none was found
26778      */
26779     findChild : function(attribute, value){
26780         var cs = this.childNodes;
26781         for(var i = 0, len = cs.length; i < len; i++) {
26782                 if(cs[i].attributes[attribute] == value){
26783                     return cs[i];
26784                 }
26785         }
26786         return null;
26787     },
26788
26789     /**
26790      * Finds the first child by a custom function. The child matches if the function passed
26791      * returns true.
26792      * @param {Function} fn
26793      * @param {Object} scope (optional)
26794      * @return {Node} The found child or null if none was found
26795      */
26796     findChildBy : function(fn, scope){
26797         var cs = this.childNodes;
26798         for(var i = 0, len = cs.length; i < len; i++) {
26799                 if(fn.call(scope||cs[i], cs[i]) === true){
26800                     return cs[i];
26801                 }
26802         }
26803         return null;
26804     },
26805
26806     /**
26807      * Sorts this nodes children using the supplied sort function
26808      * @param {Function} fn
26809      * @param {Object} scope (optional)
26810      */
26811     sort : function(fn, scope){
26812         var cs = this.childNodes;
26813         var len = cs.length;
26814         if(len > 0){
26815             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26816             cs.sort(sortFn);
26817             for(var i = 0; i < len; i++){
26818                 var n = cs[i];
26819                 n.previousSibling = cs[i-1];
26820                 n.nextSibling = cs[i+1];
26821                 if(i == 0){
26822                     this.setFirstChild(n);
26823                 }
26824                 if(i == len-1){
26825                     this.setLastChild(n);
26826                 }
26827             }
26828         }
26829     },
26830
26831     /**
26832      * Returns true if this node is an ancestor (at any point) of the passed node.
26833      * @param {Node} node
26834      * @return {Boolean}
26835      */
26836     contains : function(node){
26837         return node.isAncestor(this);
26838     },
26839
26840     /**
26841      * Returns true if the passed node is an ancestor (at any point) of this node.
26842      * @param {Node} node
26843      * @return {Boolean}
26844      */
26845     isAncestor : function(node){
26846         var p = this.parentNode;
26847         while(p){
26848             if(p == node){
26849                 return true;
26850             }
26851             p = p.parentNode;
26852         }
26853         return false;
26854     },
26855
26856     toString : function(){
26857         return "[Node"+(this.id?" "+this.id:"")+"]";
26858     }
26859 });/*
26860  * Based on:
26861  * Ext JS Library 1.1.1
26862  * Copyright(c) 2006-2007, Ext JS, LLC.
26863  *
26864  * Originally Released Under LGPL - original licence link has changed is not relivant.
26865  *
26866  * Fork - LGPL
26867  * <script type="text/javascript">
26868  */
26869
26870
26871 /**
26872  * @class Roo.Shadow
26873  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26874  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26875  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26876  * @constructor
26877  * Create a new Shadow
26878  * @param {Object} config The config object
26879  */
26880 Roo.Shadow = function(config){
26881     Roo.apply(this, config);
26882     if(typeof this.mode != "string"){
26883         this.mode = this.defaultMode;
26884     }
26885     var o = this.offset, a = {h: 0};
26886     var rad = Math.floor(this.offset/2);
26887     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26888         case "drop":
26889             a.w = 0;
26890             a.l = a.t = o;
26891             a.t -= 1;
26892             if(Roo.isIE){
26893                 a.l -= this.offset + rad;
26894                 a.t -= this.offset + rad;
26895                 a.w -= rad;
26896                 a.h -= rad;
26897                 a.t += 1;
26898             }
26899         break;
26900         case "sides":
26901             a.w = (o*2);
26902             a.l = -o;
26903             a.t = o-1;
26904             if(Roo.isIE){
26905                 a.l -= (this.offset - rad);
26906                 a.t -= this.offset + rad;
26907                 a.l += 1;
26908                 a.w -= (this.offset - rad)*2;
26909                 a.w -= rad + 1;
26910                 a.h -= 1;
26911             }
26912         break;
26913         case "frame":
26914             a.w = a.h = (o*2);
26915             a.l = a.t = -o;
26916             a.t += 1;
26917             a.h -= 2;
26918             if(Roo.isIE){
26919                 a.l -= (this.offset - rad);
26920                 a.t -= (this.offset - rad);
26921                 a.l += 1;
26922                 a.w -= (this.offset + rad + 1);
26923                 a.h -= (this.offset + rad);
26924                 a.h += 1;
26925             }
26926         break;
26927     };
26928
26929     this.adjusts = a;
26930 };
26931
26932 Roo.Shadow.prototype = {
26933     /**
26934      * @cfg {String} mode
26935      * The shadow display mode.  Supports the following options:<br />
26936      * sides: Shadow displays on both sides and bottom only<br />
26937      * frame: Shadow displays equally on all four sides<br />
26938      * drop: Traditional bottom-right drop shadow (default)
26939      */
26940     mode: false,
26941     /**
26942      * @cfg {String} offset
26943      * The number of pixels to offset the shadow from the element (defaults to 4)
26944      */
26945     offset: 4,
26946
26947     // private
26948     defaultMode: "drop",
26949
26950     /**
26951      * Displays the shadow under the target element
26952      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26953      */
26954     show : function(target){
26955         target = Roo.get(target);
26956         if(!this.el){
26957             this.el = Roo.Shadow.Pool.pull();
26958             if(this.el.dom.nextSibling != target.dom){
26959                 this.el.insertBefore(target);
26960             }
26961         }
26962         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26963         if(Roo.isIE){
26964             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26965         }
26966         this.realign(
26967             target.getLeft(true),
26968             target.getTop(true),
26969             target.getWidth(),
26970             target.getHeight()
26971         );
26972         this.el.dom.style.display = "block";
26973     },
26974
26975     /**
26976      * Returns true if the shadow is visible, else false
26977      */
26978     isVisible : function(){
26979         return this.el ? true : false;  
26980     },
26981
26982     /**
26983      * Direct alignment when values are already available. Show must be called at least once before
26984      * calling this method to ensure it is initialized.
26985      * @param {Number} left The target element left position
26986      * @param {Number} top The target element top position
26987      * @param {Number} width The target element width
26988      * @param {Number} height The target element height
26989      */
26990     realign : function(l, t, w, h){
26991         if(!this.el){
26992             return;
26993         }
26994         var a = this.adjusts, d = this.el.dom, s = d.style;
26995         var iea = 0;
26996         s.left = (l+a.l)+"px";
26997         s.top = (t+a.t)+"px";
26998         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26999  
27000         if(s.width != sws || s.height != shs){
27001             s.width = sws;
27002             s.height = shs;
27003             if(!Roo.isIE){
27004                 var cn = d.childNodes;
27005                 var sww = Math.max(0, (sw-12))+"px";
27006                 cn[0].childNodes[1].style.width = sww;
27007                 cn[1].childNodes[1].style.width = sww;
27008                 cn[2].childNodes[1].style.width = sww;
27009                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27010             }
27011         }
27012     },
27013
27014     /**
27015      * Hides this shadow
27016      */
27017     hide : function(){
27018         if(this.el){
27019             this.el.dom.style.display = "none";
27020             Roo.Shadow.Pool.push(this.el);
27021             delete this.el;
27022         }
27023     },
27024
27025     /**
27026      * Adjust the z-index of this shadow
27027      * @param {Number} zindex The new z-index
27028      */
27029     setZIndex : function(z){
27030         this.zIndex = z;
27031         if(this.el){
27032             this.el.setStyle("z-index", z);
27033         }
27034     }
27035 };
27036
27037 // Private utility class that manages the internal Shadow cache
27038 Roo.Shadow.Pool = function(){
27039     var p = [];
27040     var markup = Roo.isIE ?
27041                  '<div class="x-ie-shadow"></div>' :
27042                  '<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>';
27043     return {
27044         pull : function(){
27045             var sh = p.shift();
27046             if(!sh){
27047                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27048                 sh.autoBoxAdjust = false;
27049             }
27050             return sh;
27051         },
27052
27053         push : function(sh){
27054             p.push(sh);
27055         }
27056     };
27057 }();/*
27058  * Based on:
27059  * Ext JS Library 1.1.1
27060  * Copyright(c) 2006-2007, Ext JS, LLC.
27061  *
27062  * Originally Released Under LGPL - original licence link has changed is not relivant.
27063  *
27064  * Fork - LGPL
27065  * <script type="text/javascript">
27066  */
27067
27068
27069 /**
27070  * @class Roo.SplitBar
27071  * @extends Roo.util.Observable
27072  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27073  * <br><br>
27074  * Usage:
27075  * <pre><code>
27076 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27077                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27078 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27079 split.minSize = 100;
27080 split.maxSize = 600;
27081 split.animate = true;
27082 split.on('moved', splitterMoved);
27083 </code></pre>
27084  * @constructor
27085  * Create a new SplitBar
27086  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27087  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27088  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27089  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27090                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27091                         position of the SplitBar).
27092  */
27093 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27094     
27095     /** @private */
27096     this.el = Roo.get(dragElement, true);
27097     this.el.dom.unselectable = "on";
27098     /** @private */
27099     this.resizingEl = Roo.get(resizingElement, true);
27100
27101     /**
27102      * @private
27103      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27104      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27105      * @type Number
27106      */
27107     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27108     
27109     /**
27110      * The minimum size of the resizing element. (Defaults to 0)
27111      * @type Number
27112      */
27113     this.minSize = 0;
27114     
27115     /**
27116      * The maximum size of the resizing element. (Defaults to 2000)
27117      * @type Number
27118      */
27119     this.maxSize = 2000;
27120     
27121     /**
27122      * Whether to animate the transition to the new size
27123      * @type Boolean
27124      */
27125     this.animate = false;
27126     
27127     /**
27128      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27129      * @type Boolean
27130      */
27131     this.useShim = false;
27132     
27133     /** @private */
27134     this.shim = null;
27135     
27136     if(!existingProxy){
27137         /** @private */
27138         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27139     }else{
27140         this.proxy = Roo.get(existingProxy).dom;
27141     }
27142     /** @private */
27143     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27144     
27145     /** @private */
27146     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27147     
27148     /** @private */
27149     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27150     
27151     /** @private */
27152     this.dragSpecs = {};
27153     
27154     /**
27155      * @private The adapter to use to positon and resize elements
27156      */
27157     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27158     this.adapter.init(this);
27159     
27160     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27161         /** @private */
27162         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27163         this.el.addClass("x-splitbar-h");
27164     }else{
27165         /** @private */
27166         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27167         this.el.addClass("x-splitbar-v");
27168     }
27169     
27170     this.addEvents({
27171         /**
27172          * @event resize
27173          * Fires when the splitter is moved (alias for {@link #event-moved})
27174          * @param {Roo.SplitBar} this
27175          * @param {Number} newSize the new width or height
27176          */
27177         "resize" : true,
27178         /**
27179          * @event moved
27180          * Fires when the splitter is moved
27181          * @param {Roo.SplitBar} this
27182          * @param {Number} newSize the new width or height
27183          */
27184         "moved" : true,
27185         /**
27186          * @event beforeresize
27187          * Fires before the splitter is dragged
27188          * @param {Roo.SplitBar} this
27189          */
27190         "beforeresize" : true,
27191
27192         "beforeapply" : true
27193     });
27194
27195     Roo.util.Observable.call(this);
27196 };
27197
27198 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27199     onStartProxyDrag : function(x, y){
27200         this.fireEvent("beforeresize", this);
27201         if(!this.overlay){
27202             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27203             o.unselectable();
27204             o.enableDisplayMode("block");
27205             // all splitbars share the same overlay
27206             Roo.SplitBar.prototype.overlay = o;
27207         }
27208         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27209         this.overlay.show();
27210         Roo.get(this.proxy).setDisplayed("block");
27211         var size = this.adapter.getElementSize(this);
27212         this.activeMinSize = this.getMinimumSize();;
27213         this.activeMaxSize = this.getMaximumSize();;
27214         var c1 = size - this.activeMinSize;
27215         var c2 = Math.max(this.activeMaxSize - size, 0);
27216         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27217             this.dd.resetConstraints();
27218             this.dd.setXConstraint(
27219                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27220                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27221             );
27222             this.dd.setYConstraint(0, 0);
27223         }else{
27224             this.dd.resetConstraints();
27225             this.dd.setXConstraint(0, 0);
27226             this.dd.setYConstraint(
27227                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27228                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27229             );
27230          }
27231         this.dragSpecs.startSize = size;
27232         this.dragSpecs.startPoint = [x, y];
27233         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27234     },
27235     
27236     /** 
27237      * @private Called after the drag operation by the DDProxy
27238      */
27239     onEndProxyDrag : function(e){
27240         Roo.get(this.proxy).setDisplayed(false);
27241         var endPoint = Roo.lib.Event.getXY(e);
27242         if(this.overlay){
27243             this.overlay.hide();
27244         }
27245         var newSize;
27246         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27247             newSize = this.dragSpecs.startSize + 
27248                 (this.placement == Roo.SplitBar.LEFT ?
27249                     endPoint[0] - this.dragSpecs.startPoint[0] :
27250                     this.dragSpecs.startPoint[0] - endPoint[0]
27251                 );
27252         }else{
27253             newSize = this.dragSpecs.startSize + 
27254                 (this.placement == Roo.SplitBar.TOP ?
27255                     endPoint[1] - this.dragSpecs.startPoint[1] :
27256                     this.dragSpecs.startPoint[1] - endPoint[1]
27257                 );
27258         }
27259         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27260         if(newSize != this.dragSpecs.startSize){
27261             if(this.fireEvent('beforeapply', this, newSize) !== false){
27262                 this.adapter.setElementSize(this, newSize);
27263                 this.fireEvent("moved", this, newSize);
27264                 this.fireEvent("resize", this, newSize);
27265             }
27266         }
27267     },
27268     
27269     /**
27270      * Get the adapter this SplitBar uses
27271      * @return The adapter object
27272      */
27273     getAdapter : function(){
27274         return this.adapter;
27275     },
27276     
27277     /**
27278      * Set the adapter this SplitBar uses
27279      * @param {Object} adapter A SplitBar adapter object
27280      */
27281     setAdapter : function(adapter){
27282         this.adapter = adapter;
27283         this.adapter.init(this);
27284     },
27285     
27286     /**
27287      * Gets the minimum size for the resizing element
27288      * @return {Number} The minimum size
27289      */
27290     getMinimumSize : function(){
27291         return this.minSize;
27292     },
27293     
27294     /**
27295      * Sets the minimum size for the resizing element
27296      * @param {Number} minSize The minimum size
27297      */
27298     setMinimumSize : function(minSize){
27299         this.minSize = minSize;
27300     },
27301     
27302     /**
27303      * Gets the maximum size for the resizing element
27304      * @return {Number} The maximum size
27305      */
27306     getMaximumSize : function(){
27307         return this.maxSize;
27308     },
27309     
27310     /**
27311      * Sets the maximum size for the resizing element
27312      * @param {Number} maxSize The maximum size
27313      */
27314     setMaximumSize : function(maxSize){
27315         this.maxSize = maxSize;
27316     },
27317     
27318     /**
27319      * Sets the initialize size for the resizing element
27320      * @param {Number} size The initial size
27321      */
27322     setCurrentSize : function(size){
27323         var oldAnimate = this.animate;
27324         this.animate = false;
27325         this.adapter.setElementSize(this, size);
27326         this.animate = oldAnimate;
27327     },
27328     
27329     /**
27330      * Destroy this splitbar. 
27331      * @param {Boolean} removeEl True to remove the element
27332      */
27333     destroy : function(removeEl){
27334         if(this.shim){
27335             this.shim.remove();
27336         }
27337         this.dd.unreg();
27338         this.proxy.parentNode.removeChild(this.proxy);
27339         if(removeEl){
27340             this.el.remove();
27341         }
27342     }
27343 });
27344
27345 /**
27346  * @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.
27347  */
27348 Roo.SplitBar.createProxy = function(dir){
27349     var proxy = new Roo.Element(document.createElement("div"));
27350     proxy.unselectable();
27351     var cls = 'x-splitbar-proxy';
27352     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27353     document.body.appendChild(proxy.dom);
27354     return proxy.dom;
27355 };
27356
27357 /** 
27358  * @class Roo.SplitBar.BasicLayoutAdapter
27359  * Default Adapter. It assumes the splitter and resizing element are not positioned
27360  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27361  */
27362 Roo.SplitBar.BasicLayoutAdapter = function(){
27363 };
27364
27365 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27366     // do nothing for now
27367     init : function(s){
27368     
27369     },
27370     /**
27371      * Called before drag operations to get the current size of the resizing element. 
27372      * @param {Roo.SplitBar} s The SplitBar using this adapter
27373      */
27374      getElementSize : function(s){
27375         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27376             return s.resizingEl.getWidth();
27377         }else{
27378             return s.resizingEl.getHeight();
27379         }
27380     },
27381     
27382     /**
27383      * Called after drag operations to set the size of the resizing element.
27384      * @param {Roo.SplitBar} s The SplitBar using this adapter
27385      * @param {Number} newSize The new size to set
27386      * @param {Function} onComplete A function to be invoked when resizing is complete
27387      */
27388     setElementSize : function(s, newSize, onComplete){
27389         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27390             if(!s.animate){
27391                 s.resizingEl.setWidth(newSize);
27392                 if(onComplete){
27393                     onComplete(s, newSize);
27394                 }
27395             }else{
27396                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27397             }
27398         }else{
27399             
27400             if(!s.animate){
27401                 s.resizingEl.setHeight(newSize);
27402                 if(onComplete){
27403                     onComplete(s, newSize);
27404                 }
27405             }else{
27406                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27407             }
27408         }
27409     }
27410 };
27411
27412 /** 
27413  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27414  * @extends Roo.SplitBar.BasicLayoutAdapter
27415  * Adapter that  moves the splitter element to align with the resized sizing element. 
27416  * Used with an absolute positioned SplitBar.
27417  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27418  * document.body, make sure you assign an id to the body element.
27419  */
27420 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27421     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27422     this.container = Roo.get(container);
27423 };
27424
27425 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27426     init : function(s){
27427         this.basic.init(s);
27428     },
27429     
27430     getElementSize : function(s){
27431         return this.basic.getElementSize(s);
27432     },
27433     
27434     setElementSize : function(s, newSize, onComplete){
27435         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27436     },
27437     
27438     moveSplitter : function(s){
27439         var yes = Roo.SplitBar;
27440         switch(s.placement){
27441             case yes.LEFT:
27442                 s.el.setX(s.resizingEl.getRight());
27443                 break;
27444             case yes.RIGHT:
27445                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27446                 break;
27447             case yes.TOP:
27448                 s.el.setY(s.resizingEl.getBottom());
27449                 break;
27450             case yes.BOTTOM:
27451                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27452                 break;
27453         }
27454     }
27455 };
27456
27457 /**
27458  * Orientation constant - Create a vertical SplitBar
27459  * @static
27460  * @type Number
27461  */
27462 Roo.SplitBar.VERTICAL = 1;
27463
27464 /**
27465  * Orientation constant - Create a horizontal SplitBar
27466  * @static
27467  * @type Number
27468  */
27469 Roo.SplitBar.HORIZONTAL = 2;
27470
27471 /**
27472  * Placement constant - The resizing element is to the left of the splitter element
27473  * @static
27474  * @type Number
27475  */
27476 Roo.SplitBar.LEFT = 1;
27477
27478 /**
27479  * Placement constant - The resizing element is to the right of the splitter element
27480  * @static
27481  * @type Number
27482  */
27483 Roo.SplitBar.RIGHT = 2;
27484
27485 /**
27486  * Placement constant - The resizing element is positioned above the splitter element
27487  * @static
27488  * @type Number
27489  */
27490 Roo.SplitBar.TOP = 3;
27491
27492 /**
27493  * Placement constant - The resizing element is positioned under splitter element
27494  * @static
27495  * @type Number
27496  */
27497 Roo.SplitBar.BOTTOM = 4;
27498 /*
27499  * Based on:
27500  * Ext JS Library 1.1.1
27501  * Copyright(c) 2006-2007, Ext JS, LLC.
27502  *
27503  * Originally Released Under LGPL - original licence link has changed is not relivant.
27504  *
27505  * Fork - LGPL
27506  * <script type="text/javascript">
27507  */
27508
27509 /**
27510  * @class Roo.View
27511  * @extends Roo.util.Observable
27512  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27513  * This class also supports single and multi selection modes. <br>
27514  * Create a data model bound view:
27515  <pre><code>
27516  var store = new Roo.data.Store(...);
27517
27518  var view = new Roo.View({
27519     el : "my-element",
27520     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27521  
27522     singleSelect: true,
27523     selectedClass: "ydataview-selected",
27524     store: store
27525  });
27526
27527  // listen for node click?
27528  view.on("click", function(vw, index, node, e){
27529  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27530  });
27531
27532  // load XML data
27533  dataModel.load("foobar.xml");
27534  </code></pre>
27535  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27536  * <br><br>
27537  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27538  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27539  * 
27540  * Note: old style constructor is still suported (container, template, config)
27541  * 
27542  * @constructor
27543  * Create a new View
27544  * @param {Object} config The config object
27545  * 
27546  */
27547 Roo.View = function(config, depreciated_tpl, depreciated_config){
27548     
27549     this.parent = false;
27550     
27551     if (typeof(depreciated_tpl) == 'undefined') {
27552         // new way.. - universal constructor.
27553         Roo.apply(this, config);
27554         this.el  = Roo.get(this.el);
27555     } else {
27556         // old format..
27557         this.el  = Roo.get(config);
27558         this.tpl = depreciated_tpl;
27559         Roo.apply(this, depreciated_config);
27560     }
27561     this.wrapEl  = this.el.wrap().wrap();
27562     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27563     
27564     
27565     if(typeof(this.tpl) == "string"){
27566         this.tpl = new Roo.Template(this.tpl);
27567     } else {
27568         // support xtype ctors..
27569         this.tpl = new Roo.factory(this.tpl, Roo);
27570     }
27571     
27572     
27573     this.tpl.compile();
27574     
27575     /** @private */
27576     this.addEvents({
27577         /**
27578          * @event beforeclick
27579          * Fires before a click is processed. Returns false to cancel the default action.
27580          * @param {Roo.View} this
27581          * @param {Number} index The index of the target node
27582          * @param {HTMLElement} node The target node
27583          * @param {Roo.EventObject} e The raw event object
27584          */
27585             "beforeclick" : true,
27586         /**
27587          * @event click
27588          * Fires when a template node is clicked.
27589          * @param {Roo.View} this
27590          * @param {Number} index The index of the target node
27591          * @param {HTMLElement} node The target node
27592          * @param {Roo.EventObject} e The raw event object
27593          */
27594             "click" : true,
27595         /**
27596          * @event dblclick
27597          * Fires when a template node is double clicked.
27598          * @param {Roo.View} this
27599          * @param {Number} index The index of the target node
27600          * @param {HTMLElement} node The target node
27601          * @param {Roo.EventObject} e The raw event object
27602          */
27603             "dblclick" : true,
27604         /**
27605          * @event contextmenu
27606          * Fires when a template node is right clicked.
27607          * @param {Roo.View} this
27608          * @param {Number} index The index of the target node
27609          * @param {HTMLElement} node The target node
27610          * @param {Roo.EventObject} e The raw event object
27611          */
27612             "contextmenu" : true,
27613         /**
27614          * @event selectionchange
27615          * Fires when the selected nodes change.
27616          * @param {Roo.View} this
27617          * @param {Array} selections Array of the selected nodes
27618          */
27619             "selectionchange" : true,
27620     
27621         /**
27622          * @event beforeselect
27623          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27624          * @param {Roo.View} this
27625          * @param {HTMLElement} node The node to be selected
27626          * @param {Array} selections Array of currently selected nodes
27627          */
27628             "beforeselect" : true,
27629         /**
27630          * @event preparedata
27631          * Fires on every row to render, to allow you to change the data.
27632          * @param {Roo.View} this
27633          * @param {Object} data to be rendered (change this)
27634          */
27635           "preparedata" : true
27636           
27637           
27638         });
27639
27640
27641
27642     this.el.on({
27643         "click": this.onClick,
27644         "dblclick": this.onDblClick,
27645         "contextmenu": this.onContextMenu,
27646         scope:this
27647     });
27648
27649     this.selections = [];
27650     this.nodes = [];
27651     this.cmp = new Roo.CompositeElementLite([]);
27652     if(this.store){
27653         this.store = Roo.factory(this.store, Roo.data);
27654         this.setStore(this.store, true);
27655     }
27656     
27657     if ( this.footer && this.footer.xtype) {
27658            
27659          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27660         
27661         this.footer.dataSource = this.store;
27662         this.footer.container = fctr;
27663         this.footer = Roo.factory(this.footer, Roo);
27664         fctr.insertFirst(this.el);
27665         
27666         // this is a bit insane - as the paging toolbar seems to detach the el..
27667 //        dom.parentNode.parentNode.parentNode
27668          // they get detached?
27669     }
27670     
27671     
27672     Roo.View.superclass.constructor.call(this);
27673     
27674     
27675 };
27676
27677 Roo.extend(Roo.View, Roo.util.Observable, {
27678     
27679      /**
27680      * @cfg {Roo.data.Store} store Data store to load data from.
27681      */
27682     store : false,
27683     
27684     /**
27685      * @cfg {String|Roo.Element} el The container element.
27686      */
27687     el : '',
27688     
27689     /**
27690      * @cfg {String|Roo.Template} tpl The template used by this View 
27691      */
27692     tpl : false,
27693     /**
27694      * @cfg {String} dataName the named area of the template to use as the data area
27695      *                          Works with domtemplates roo-name="name"
27696      */
27697     dataName: false,
27698     /**
27699      * @cfg {String} selectedClass The css class to add to selected nodes
27700      */
27701     selectedClass : "x-view-selected",
27702      /**
27703      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27704      */
27705     emptyText : "",
27706     
27707     /**
27708      * @cfg {String} text to display on mask (default Loading)
27709      */
27710     mask : false,
27711     /**
27712      * @cfg {Boolean} multiSelect Allow multiple selection
27713      */
27714     multiSelect : false,
27715     /**
27716      * @cfg {Boolean} singleSelect Allow single selection
27717      */
27718     singleSelect:  false,
27719     
27720     /**
27721      * @cfg {Boolean} toggleSelect - selecting 
27722      */
27723     toggleSelect : false,
27724     
27725     /**
27726      * @cfg {Boolean} tickable - selecting 
27727      */
27728     tickable : false,
27729     
27730     /**
27731      * Returns the element this view is bound to.
27732      * @return {Roo.Element}
27733      */
27734     getEl : function(){
27735         return this.wrapEl;
27736     },
27737     
27738     
27739
27740     /**
27741      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27742      */
27743     refresh : function(){
27744         //Roo.log('refresh');
27745         var t = this.tpl;
27746         
27747         // if we are using something like 'domtemplate', then
27748         // the what gets used is:
27749         // t.applySubtemplate(NAME, data, wrapping data..)
27750         // the outer template then get' applied with
27751         //     the store 'extra data'
27752         // and the body get's added to the
27753         //      roo-name="data" node?
27754         //      <span class='roo-tpl-{name}'></span> ?????
27755         
27756         
27757         
27758         this.clearSelections();
27759         this.el.update("");
27760         var html = [];
27761         var records = this.store.getRange();
27762         if(records.length < 1) {
27763             
27764             // is this valid??  = should it render a template??
27765             
27766             this.el.update(this.emptyText);
27767             return;
27768         }
27769         var el = this.el;
27770         if (this.dataName) {
27771             this.el.update(t.apply(this.store.meta)); //????
27772             el = this.el.child('.roo-tpl-' + this.dataName);
27773         }
27774         
27775         for(var i = 0, len = records.length; i < len; i++){
27776             var data = this.prepareData(records[i].data, i, records[i]);
27777             this.fireEvent("preparedata", this, data, i, records[i]);
27778             
27779             var d = Roo.apply({}, data);
27780             
27781             if(this.tickable){
27782                 Roo.apply(d, {'roo-id' : Roo.id()});
27783                 
27784                 var _this = this;
27785             
27786                 Roo.each(this.parent.item, function(item){
27787                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27788                         return;
27789                     }
27790                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27791                 });
27792             }
27793             
27794             html[html.length] = Roo.util.Format.trim(
27795                 this.dataName ?
27796                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27797                     t.apply(d)
27798             );
27799         }
27800         
27801         
27802         
27803         el.update(html.join(""));
27804         this.nodes = el.dom.childNodes;
27805         this.updateIndexes(0);
27806     },
27807     
27808
27809     /**
27810      * Function to override to reformat the data that is sent to
27811      * the template for each node.
27812      * DEPRICATED - use the preparedata event handler.
27813      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27814      * a JSON object for an UpdateManager bound view).
27815      */
27816     prepareData : function(data, index, record)
27817     {
27818         this.fireEvent("preparedata", this, data, index, record);
27819         return data;
27820     },
27821
27822     onUpdate : function(ds, record){
27823         // Roo.log('on update');   
27824         this.clearSelections();
27825         var index = this.store.indexOf(record);
27826         var n = this.nodes[index];
27827         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27828         n.parentNode.removeChild(n);
27829         this.updateIndexes(index, index);
27830     },
27831
27832     
27833     
27834 // --------- FIXME     
27835     onAdd : function(ds, records, index)
27836     {
27837         //Roo.log(['on Add', ds, records, index] );        
27838         this.clearSelections();
27839         if(this.nodes.length == 0){
27840             this.refresh();
27841             return;
27842         }
27843         var n = this.nodes[index];
27844         for(var i = 0, len = records.length; i < len; i++){
27845             var d = this.prepareData(records[i].data, i, records[i]);
27846             if(n){
27847                 this.tpl.insertBefore(n, d);
27848             }else{
27849                 
27850                 this.tpl.append(this.el, d);
27851             }
27852         }
27853         this.updateIndexes(index);
27854     },
27855
27856     onRemove : function(ds, record, index){
27857        // Roo.log('onRemove');
27858         this.clearSelections();
27859         var el = this.dataName  ?
27860             this.el.child('.roo-tpl-' + this.dataName) :
27861             this.el; 
27862         
27863         el.dom.removeChild(this.nodes[index]);
27864         this.updateIndexes(index);
27865     },
27866
27867     /**
27868      * Refresh an individual node.
27869      * @param {Number} index
27870      */
27871     refreshNode : function(index){
27872         this.onUpdate(this.store, this.store.getAt(index));
27873     },
27874
27875     updateIndexes : function(startIndex, endIndex){
27876         var ns = this.nodes;
27877         startIndex = startIndex || 0;
27878         endIndex = endIndex || ns.length - 1;
27879         for(var i = startIndex; i <= endIndex; i++){
27880             ns[i].nodeIndex = i;
27881         }
27882     },
27883
27884     /**
27885      * Changes the data store this view uses and refresh the view.
27886      * @param {Store} store
27887      */
27888     setStore : function(store, initial){
27889         if(!initial && this.store){
27890             this.store.un("datachanged", this.refresh);
27891             this.store.un("add", this.onAdd);
27892             this.store.un("remove", this.onRemove);
27893             this.store.un("update", this.onUpdate);
27894             this.store.un("clear", this.refresh);
27895             this.store.un("beforeload", this.onBeforeLoad);
27896             this.store.un("load", this.onLoad);
27897             this.store.un("loadexception", this.onLoad);
27898         }
27899         if(store){
27900           
27901             store.on("datachanged", this.refresh, this);
27902             store.on("add", this.onAdd, this);
27903             store.on("remove", this.onRemove, this);
27904             store.on("update", this.onUpdate, this);
27905             store.on("clear", this.refresh, this);
27906             store.on("beforeload", this.onBeforeLoad, this);
27907             store.on("load", this.onLoad, this);
27908             store.on("loadexception", this.onLoad, this);
27909         }
27910         
27911         if(store){
27912             this.refresh();
27913         }
27914     },
27915     /**
27916      * onbeforeLoad - masks the loading area.
27917      *
27918      */
27919     onBeforeLoad : function(store,opts)
27920     {
27921          //Roo.log('onBeforeLoad');   
27922         if (!opts.add) {
27923             this.el.update("");
27924         }
27925         this.el.mask(this.mask ? this.mask : "Loading" ); 
27926     },
27927     onLoad : function ()
27928     {
27929         this.el.unmask();
27930     },
27931     
27932
27933     /**
27934      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27935      * @param {HTMLElement} node
27936      * @return {HTMLElement} The template node
27937      */
27938     findItemFromChild : function(node){
27939         var el = this.dataName  ?
27940             this.el.child('.roo-tpl-' + this.dataName,true) :
27941             this.el.dom; 
27942         
27943         if(!node || node.parentNode == el){
27944                     return node;
27945             }
27946             var p = node.parentNode;
27947             while(p && p != el){
27948             if(p.parentNode == el){
27949                 return p;
27950             }
27951             p = p.parentNode;
27952         }
27953             return null;
27954     },
27955
27956     /** @ignore */
27957     onClick : function(e){
27958         var item = this.findItemFromChild(e.getTarget());
27959         if(item){
27960             var index = this.indexOf(item);
27961             if(this.onItemClick(item, index, e) !== false){
27962                 this.fireEvent("click", this, index, item, e);
27963             }
27964         }else{
27965             this.clearSelections();
27966         }
27967     },
27968
27969     /** @ignore */
27970     onContextMenu : function(e){
27971         var item = this.findItemFromChild(e.getTarget());
27972         if(item){
27973             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27974         }
27975     },
27976
27977     /** @ignore */
27978     onDblClick : function(e){
27979         var item = this.findItemFromChild(e.getTarget());
27980         if(item){
27981             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27982         }
27983     },
27984
27985     onItemClick : function(item, index, e)
27986     {
27987         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27988             return false;
27989         }
27990         if (this.toggleSelect) {
27991             var m = this.isSelected(item) ? 'unselect' : 'select';
27992             //Roo.log(m);
27993             var _t = this;
27994             _t[m](item, true, false);
27995             return true;
27996         }
27997         if(this.multiSelect || this.singleSelect){
27998             if(this.multiSelect && e.shiftKey && this.lastSelection){
27999                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28000             }else{
28001                 this.select(item, this.multiSelect && e.ctrlKey);
28002                 this.lastSelection = item;
28003             }
28004             
28005             if(!this.tickable){
28006                 e.preventDefault();
28007             }
28008             
28009         }
28010         return true;
28011     },
28012
28013     /**
28014      * Get the number of selected nodes.
28015      * @return {Number}
28016      */
28017     getSelectionCount : function(){
28018         return this.selections.length;
28019     },
28020
28021     /**
28022      * Get the currently selected nodes.
28023      * @return {Array} An array of HTMLElements
28024      */
28025     getSelectedNodes : function(){
28026         return this.selections;
28027     },
28028
28029     /**
28030      * Get the indexes of the selected nodes.
28031      * @return {Array}
28032      */
28033     getSelectedIndexes : function(){
28034         var indexes = [], s = this.selections;
28035         for(var i = 0, len = s.length; i < len; i++){
28036             indexes.push(s[i].nodeIndex);
28037         }
28038         return indexes;
28039     },
28040
28041     /**
28042      * Clear all selections
28043      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28044      */
28045     clearSelections : function(suppressEvent){
28046         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28047             this.cmp.elements = this.selections;
28048             this.cmp.removeClass(this.selectedClass);
28049             this.selections = [];
28050             if(!suppressEvent){
28051                 this.fireEvent("selectionchange", this, this.selections);
28052             }
28053         }
28054     },
28055
28056     /**
28057      * Returns true if the passed node is selected
28058      * @param {HTMLElement/Number} node The node or node index
28059      * @return {Boolean}
28060      */
28061     isSelected : function(node){
28062         var s = this.selections;
28063         if(s.length < 1){
28064             return false;
28065         }
28066         node = this.getNode(node);
28067         return s.indexOf(node) !== -1;
28068     },
28069
28070     /**
28071      * Selects nodes.
28072      * @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
28073      * @param {Boolean} keepExisting (optional) true to keep existing selections
28074      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28075      */
28076     select : function(nodeInfo, keepExisting, suppressEvent){
28077         if(nodeInfo instanceof Array){
28078             if(!keepExisting){
28079                 this.clearSelections(true);
28080             }
28081             for(var i = 0, len = nodeInfo.length; i < len; i++){
28082                 this.select(nodeInfo[i], true, true);
28083             }
28084             return;
28085         } 
28086         var node = this.getNode(nodeInfo);
28087         if(!node || this.isSelected(node)){
28088             return; // already selected.
28089         }
28090         if(!keepExisting){
28091             this.clearSelections(true);
28092         }
28093         
28094         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28095             Roo.fly(node).addClass(this.selectedClass);
28096             this.selections.push(node);
28097             if(!suppressEvent){
28098                 this.fireEvent("selectionchange", this, this.selections);
28099             }
28100         }
28101         
28102         
28103     },
28104       /**
28105      * Unselects nodes.
28106      * @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
28107      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28108      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28109      */
28110     unselect : function(nodeInfo, keepExisting, suppressEvent)
28111     {
28112         if(nodeInfo instanceof Array){
28113             Roo.each(this.selections, function(s) {
28114                 this.unselect(s, nodeInfo);
28115             }, this);
28116             return;
28117         }
28118         var node = this.getNode(nodeInfo);
28119         if(!node || !this.isSelected(node)){
28120             //Roo.log("not selected");
28121             return; // not selected.
28122         }
28123         // fireevent???
28124         var ns = [];
28125         Roo.each(this.selections, function(s) {
28126             if (s == node ) {
28127                 Roo.fly(node).removeClass(this.selectedClass);
28128
28129                 return;
28130             }
28131             ns.push(s);
28132         },this);
28133         
28134         this.selections= ns;
28135         this.fireEvent("selectionchange", this, this.selections);
28136     },
28137
28138     /**
28139      * Gets a template node.
28140      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28141      * @return {HTMLElement} The node or null if it wasn't found
28142      */
28143     getNode : function(nodeInfo){
28144         if(typeof nodeInfo == "string"){
28145             return document.getElementById(nodeInfo);
28146         }else if(typeof nodeInfo == "number"){
28147             return this.nodes[nodeInfo];
28148         }
28149         return nodeInfo;
28150     },
28151
28152     /**
28153      * Gets a range template nodes.
28154      * @param {Number} startIndex
28155      * @param {Number} endIndex
28156      * @return {Array} An array of nodes
28157      */
28158     getNodes : function(start, end){
28159         var ns = this.nodes;
28160         start = start || 0;
28161         end = typeof end == "undefined" ? ns.length - 1 : end;
28162         var nodes = [];
28163         if(start <= end){
28164             for(var i = start; i <= end; i++){
28165                 nodes.push(ns[i]);
28166             }
28167         } else{
28168             for(var i = start; i >= end; i--){
28169                 nodes.push(ns[i]);
28170             }
28171         }
28172         return nodes;
28173     },
28174
28175     /**
28176      * Finds the index of the passed node
28177      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28178      * @return {Number} The index of the node or -1
28179      */
28180     indexOf : function(node){
28181         node = this.getNode(node);
28182         if(typeof node.nodeIndex == "number"){
28183             return node.nodeIndex;
28184         }
28185         var ns = this.nodes;
28186         for(var i = 0, len = ns.length; i < len; i++){
28187             if(ns[i] == node){
28188                 return i;
28189             }
28190         }
28191         return -1;
28192     }
28193 });
28194 /*
28195  * Based on:
28196  * Ext JS Library 1.1.1
28197  * Copyright(c) 2006-2007, Ext JS, LLC.
28198  *
28199  * Originally Released Under LGPL - original licence link has changed is not relivant.
28200  *
28201  * Fork - LGPL
28202  * <script type="text/javascript">
28203  */
28204
28205 /**
28206  * @class Roo.JsonView
28207  * @extends Roo.View
28208  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28209 <pre><code>
28210 var view = new Roo.JsonView({
28211     container: "my-element",
28212     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28213     multiSelect: true, 
28214     jsonRoot: "data" 
28215 });
28216
28217 // listen for node click?
28218 view.on("click", function(vw, index, node, e){
28219     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28220 });
28221
28222 // direct load of JSON data
28223 view.load("foobar.php");
28224
28225 // Example from my blog list
28226 var tpl = new Roo.Template(
28227     '&lt;div class="entry"&gt;' +
28228     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28229     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28230     "&lt;/div&gt;&lt;hr /&gt;"
28231 );
28232
28233 var moreView = new Roo.JsonView({
28234     container :  "entry-list", 
28235     template : tpl,
28236     jsonRoot: "posts"
28237 });
28238 moreView.on("beforerender", this.sortEntries, this);
28239 moreView.load({
28240     url: "/blog/get-posts.php",
28241     params: "allposts=true",
28242     text: "Loading Blog Entries..."
28243 });
28244 </code></pre>
28245
28246 * Note: old code is supported with arguments : (container, template, config)
28247
28248
28249  * @constructor
28250  * Create a new JsonView
28251  * 
28252  * @param {Object} config The config object
28253  * 
28254  */
28255 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28256     
28257     
28258     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28259
28260     var um = this.el.getUpdateManager();
28261     um.setRenderer(this);
28262     um.on("update", this.onLoad, this);
28263     um.on("failure", this.onLoadException, this);
28264
28265     /**
28266      * @event beforerender
28267      * Fires before rendering of the downloaded JSON data.
28268      * @param {Roo.JsonView} this
28269      * @param {Object} data The JSON data loaded
28270      */
28271     /**
28272      * @event load
28273      * Fires when data is loaded.
28274      * @param {Roo.JsonView} this
28275      * @param {Object} data The JSON data loaded
28276      * @param {Object} response The raw Connect response object
28277      */
28278     /**
28279      * @event loadexception
28280      * Fires when loading fails.
28281      * @param {Roo.JsonView} this
28282      * @param {Object} response The raw Connect response object
28283      */
28284     this.addEvents({
28285         'beforerender' : true,
28286         'load' : true,
28287         'loadexception' : true
28288     });
28289 };
28290 Roo.extend(Roo.JsonView, Roo.View, {
28291     /**
28292      * @type {String} The root property in the loaded JSON object that contains the data
28293      */
28294     jsonRoot : "",
28295
28296     /**
28297      * Refreshes the view.
28298      */
28299     refresh : function(){
28300         this.clearSelections();
28301         this.el.update("");
28302         var html = [];
28303         var o = this.jsonData;
28304         if(o && o.length > 0){
28305             for(var i = 0, len = o.length; i < len; i++){
28306                 var data = this.prepareData(o[i], i, o);
28307                 html[html.length] = this.tpl.apply(data);
28308             }
28309         }else{
28310             html.push(this.emptyText);
28311         }
28312         this.el.update(html.join(""));
28313         this.nodes = this.el.dom.childNodes;
28314         this.updateIndexes(0);
28315     },
28316
28317     /**
28318      * 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.
28319      * @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:
28320      <pre><code>
28321      view.load({
28322          url: "your-url.php",
28323          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28324          callback: yourFunction,
28325          scope: yourObject, //(optional scope)
28326          discardUrl: false,
28327          nocache: false,
28328          text: "Loading...",
28329          timeout: 30,
28330          scripts: false
28331      });
28332      </code></pre>
28333      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28334      * 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.
28335      * @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}
28336      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28337      * @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.
28338      */
28339     load : function(){
28340         var um = this.el.getUpdateManager();
28341         um.update.apply(um, arguments);
28342     },
28343
28344     // note - render is a standard framework call...
28345     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28346     render : function(el, response){
28347         
28348         this.clearSelections();
28349         this.el.update("");
28350         var o;
28351         try{
28352             if (response != '') {
28353                 o = Roo.util.JSON.decode(response.responseText);
28354                 if(this.jsonRoot){
28355                     
28356                     o = o[this.jsonRoot];
28357                 }
28358             }
28359         } catch(e){
28360         }
28361         /**
28362          * The current JSON data or null
28363          */
28364         this.jsonData = o;
28365         this.beforeRender();
28366         this.refresh();
28367     },
28368
28369 /**
28370  * Get the number of records in the current JSON dataset
28371  * @return {Number}
28372  */
28373     getCount : function(){
28374         return this.jsonData ? this.jsonData.length : 0;
28375     },
28376
28377 /**
28378  * Returns the JSON object for the specified node(s)
28379  * @param {HTMLElement/Array} node The node or an array of nodes
28380  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28381  * you get the JSON object for the node
28382  */
28383     getNodeData : function(node){
28384         if(node instanceof Array){
28385             var data = [];
28386             for(var i = 0, len = node.length; i < len; i++){
28387                 data.push(this.getNodeData(node[i]));
28388             }
28389             return data;
28390         }
28391         return this.jsonData[this.indexOf(node)] || null;
28392     },
28393
28394     beforeRender : function(){
28395         this.snapshot = this.jsonData;
28396         if(this.sortInfo){
28397             this.sort.apply(this, this.sortInfo);
28398         }
28399         this.fireEvent("beforerender", this, this.jsonData);
28400     },
28401
28402     onLoad : function(el, o){
28403         this.fireEvent("load", this, this.jsonData, o);
28404     },
28405
28406     onLoadException : function(el, o){
28407         this.fireEvent("loadexception", this, o);
28408     },
28409
28410 /**
28411  * Filter the data by a specific property.
28412  * @param {String} property A property on your JSON objects
28413  * @param {String/RegExp} value Either string that the property values
28414  * should start with, or a RegExp to test against the property
28415  */
28416     filter : function(property, value){
28417         if(this.jsonData){
28418             var data = [];
28419             var ss = this.snapshot;
28420             if(typeof value == "string"){
28421                 var vlen = value.length;
28422                 if(vlen == 0){
28423                     this.clearFilter();
28424                     return;
28425                 }
28426                 value = value.toLowerCase();
28427                 for(var i = 0, len = ss.length; i < len; i++){
28428                     var o = ss[i];
28429                     if(o[property].substr(0, vlen).toLowerCase() == value){
28430                         data.push(o);
28431                     }
28432                 }
28433             } else if(value.exec){ // regex?
28434                 for(var i = 0, len = ss.length; i < len; i++){
28435                     var o = ss[i];
28436                     if(value.test(o[property])){
28437                         data.push(o);
28438                     }
28439                 }
28440             } else{
28441                 return;
28442             }
28443             this.jsonData = data;
28444             this.refresh();
28445         }
28446     },
28447
28448 /**
28449  * Filter by a function. The passed function will be called with each
28450  * object in the current dataset. If the function returns true the value is kept,
28451  * otherwise it is filtered.
28452  * @param {Function} fn
28453  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28454  */
28455     filterBy : function(fn, scope){
28456         if(this.jsonData){
28457             var data = [];
28458             var ss = this.snapshot;
28459             for(var i = 0, len = ss.length; i < len; i++){
28460                 var o = ss[i];
28461                 if(fn.call(scope || this, o)){
28462                     data.push(o);
28463                 }
28464             }
28465             this.jsonData = data;
28466             this.refresh();
28467         }
28468     },
28469
28470 /**
28471  * Clears the current filter.
28472  */
28473     clearFilter : function(){
28474         if(this.snapshot && this.jsonData != this.snapshot){
28475             this.jsonData = this.snapshot;
28476             this.refresh();
28477         }
28478     },
28479
28480
28481 /**
28482  * Sorts the data for this view and refreshes it.
28483  * @param {String} property A property on your JSON objects to sort on
28484  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28485  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28486  */
28487     sort : function(property, dir, sortType){
28488         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28489         if(this.jsonData){
28490             var p = property;
28491             var dsc = dir && dir.toLowerCase() == "desc";
28492             var f = function(o1, o2){
28493                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28494                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28495                 ;
28496                 if(v1 < v2){
28497                     return dsc ? +1 : -1;
28498                 } else if(v1 > v2){
28499                     return dsc ? -1 : +1;
28500                 } else{
28501                     return 0;
28502                 }
28503             };
28504             this.jsonData.sort(f);
28505             this.refresh();
28506             if(this.jsonData != this.snapshot){
28507                 this.snapshot.sort(f);
28508             }
28509         }
28510     }
28511 });/*
28512  * Based on:
28513  * Ext JS Library 1.1.1
28514  * Copyright(c) 2006-2007, Ext JS, LLC.
28515  *
28516  * Originally Released Under LGPL - original licence link has changed is not relivant.
28517  *
28518  * Fork - LGPL
28519  * <script type="text/javascript">
28520  */
28521  
28522
28523 /**
28524  * @class Roo.ColorPalette
28525  * @extends Roo.Component
28526  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28527  * Here's an example of typical usage:
28528  * <pre><code>
28529 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28530 cp.render('my-div');
28531
28532 cp.on('select', function(palette, selColor){
28533     // do something with selColor
28534 });
28535 </code></pre>
28536  * @constructor
28537  * Create a new ColorPalette
28538  * @param {Object} config The config object
28539  */
28540 Roo.ColorPalette = function(config){
28541     Roo.ColorPalette.superclass.constructor.call(this, config);
28542     this.addEvents({
28543         /**
28544              * @event select
28545              * Fires when a color is selected
28546              * @param {ColorPalette} this
28547              * @param {String} color The 6-digit color hex code (without the # symbol)
28548              */
28549         select: true
28550     });
28551
28552     if(this.handler){
28553         this.on("select", this.handler, this.scope, true);
28554     }
28555 };
28556 Roo.extend(Roo.ColorPalette, Roo.Component, {
28557     /**
28558      * @cfg {String} itemCls
28559      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28560      */
28561     itemCls : "x-color-palette",
28562     /**
28563      * @cfg {String} value
28564      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28565      * the hex codes are case-sensitive.
28566      */
28567     value : null,
28568     clickEvent:'click',
28569     // private
28570     ctype: "Roo.ColorPalette",
28571
28572     /**
28573      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28574      */
28575     allowReselect : false,
28576
28577     /**
28578      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28579      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28580      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28581      * of colors with the width setting until the box is symmetrical.</p>
28582      * <p>You can override individual colors if needed:</p>
28583      * <pre><code>
28584 var cp = new Roo.ColorPalette();
28585 cp.colors[0] = "FF0000";  // change the first box to red
28586 </code></pre>
28587
28588 Or you can provide a custom array of your own for complete control:
28589 <pre><code>
28590 var cp = new Roo.ColorPalette();
28591 cp.colors = ["000000", "993300", "333300"];
28592 </code></pre>
28593      * @type Array
28594      */
28595     colors : [
28596         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28597         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28598         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28599         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28600         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28601     ],
28602
28603     // private
28604     onRender : function(container, position){
28605         var t = new Roo.MasterTemplate(
28606             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28607         );
28608         var c = this.colors;
28609         for(var i = 0, len = c.length; i < len; i++){
28610             t.add([c[i]]);
28611         }
28612         var el = document.createElement("div");
28613         el.className = this.itemCls;
28614         t.overwrite(el);
28615         container.dom.insertBefore(el, position);
28616         this.el = Roo.get(el);
28617         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28618         if(this.clickEvent != 'click'){
28619             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28620         }
28621     },
28622
28623     // private
28624     afterRender : function(){
28625         Roo.ColorPalette.superclass.afterRender.call(this);
28626         if(this.value){
28627             var s = this.value;
28628             this.value = null;
28629             this.select(s);
28630         }
28631     },
28632
28633     // private
28634     handleClick : function(e, t){
28635         e.preventDefault();
28636         if(!this.disabled){
28637             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28638             this.select(c.toUpperCase());
28639         }
28640     },
28641
28642     /**
28643      * Selects the specified color in the palette (fires the select event)
28644      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28645      */
28646     select : function(color){
28647         color = color.replace("#", "");
28648         if(color != this.value || this.allowReselect){
28649             var el = this.el;
28650             if(this.value){
28651                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28652             }
28653             el.child("a.color-"+color).addClass("x-color-palette-sel");
28654             this.value = color;
28655             this.fireEvent("select", this, color);
28656         }
28657     }
28658 });/*
28659  * Based on:
28660  * Ext JS Library 1.1.1
28661  * Copyright(c) 2006-2007, Ext JS, LLC.
28662  *
28663  * Originally Released Under LGPL - original licence link has changed is not relivant.
28664  *
28665  * Fork - LGPL
28666  * <script type="text/javascript">
28667  */
28668  
28669 /**
28670  * @class Roo.DatePicker
28671  * @extends Roo.Component
28672  * Simple date picker class.
28673  * @constructor
28674  * Create a new DatePicker
28675  * @param {Object} config The config object
28676  */
28677 Roo.DatePicker = function(config){
28678     Roo.DatePicker.superclass.constructor.call(this, config);
28679
28680     this.value = config && config.value ?
28681                  config.value.clearTime() : new Date().clearTime();
28682
28683     this.addEvents({
28684         /**
28685              * @event select
28686              * Fires when a date is selected
28687              * @param {DatePicker} this
28688              * @param {Date} date The selected date
28689              */
28690         'select': true,
28691         /**
28692              * @event monthchange
28693              * Fires when the displayed month changes 
28694              * @param {DatePicker} this
28695              * @param {Date} date The selected month
28696              */
28697         'monthchange': true
28698     });
28699
28700     if(this.handler){
28701         this.on("select", this.handler,  this.scope || this);
28702     }
28703     // build the disabledDatesRE
28704     if(!this.disabledDatesRE && this.disabledDates){
28705         var dd = this.disabledDates;
28706         var re = "(?:";
28707         for(var i = 0; i < dd.length; i++){
28708             re += dd[i];
28709             if(i != dd.length-1) {
28710                 re += "|";
28711             }
28712         }
28713         this.disabledDatesRE = new RegExp(re + ")");
28714     }
28715 };
28716
28717 Roo.extend(Roo.DatePicker, Roo.Component, {
28718     /**
28719      * @cfg {String} todayText
28720      * The text to display on the button that selects the current date (defaults to "Today")
28721      */
28722     todayText : "Today",
28723     /**
28724      * @cfg {String} okText
28725      * The text to display on the ok button
28726      */
28727     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28728     /**
28729      * @cfg {String} cancelText
28730      * The text to display on the cancel button
28731      */
28732     cancelText : "Cancel",
28733     /**
28734      * @cfg {String} todayTip
28735      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28736      */
28737     todayTip : "{0} (Spacebar)",
28738     /**
28739      * @cfg {Date} minDate
28740      * Minimum allowable date (JavaScript date object, defaults to null)
28741      */
28742     minDate : null,
28743     /**
28744      * @cfg {Date} maxDate
28745      * Maximum allowable date (JavaScript date object, defaults to null)
28746      */
28747     maxDate : null,
28748     /**
28749      * @cfg {String} minText
28750      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28751      */
28752     minText : "This date is before the minimum date",
28753     /**
28754      * @cfg {String} maxText
28755      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28756      */
28757     maxText : "This date is after the maximum date",
28758     /**
28759      * @cfg {String} format
28760      * The default date format string which can be overriden for localization support.  The format must be
28761      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28762      */
28763     format : "m/d/y",
28764     /**
28765      * @cfg {Array} disabledDays
28766      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28767      */
28768     disabledDays : null,
28769     /**
28770      * @cfg {String} disabledDaysText
28771      * The tooltip to display when the date falls on a disabled day (defaults to "")
28772      */
28773     disabledDaysText : "",
28774     /**
28775      * @cfg {RegExp} disabledDatesRE
28776      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28777      */
28778     disabledDatesRE : null,
28779     /**
28780      * @cfg {String} disabledDatesText
28781      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28782      */
28783     disabledDatesText : "",
28784     /**
28785      * @cfg {Boolean} constrainToViewport
28786      * True to constrain the date picker to the viewport (defaults to true)
28787      */
28788     constrainToViewport : true,
28789     /**
28790      * @cfg {Array} monthNames
28791      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28792      */
28793     monthNames : Date.monthNames,
28794     /**
28795      * @cfg {Array} dayNames
28796      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28797      */
28798     dayNames : Date.dayNames,
28799     /**
28800      * @cfg {String} nextText
28801      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28802      */
28803     nextText: 'Next Month (Control+Right)',
28804     /**
28805      * @cfg {String} prevText
28806      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28807      */
28808     prevText: 'Previous Month (Control+Left)',
28809     /**
28810      * @cfg {String} monthYearText
28811      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28812      */
28813     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28814     /**
28815      * @cfg {Number} startDay
28816      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28817      */
28818     startDay : 0,
28819     /**
28820      * @cfg {Bool} showClear
28821      * Show a clear button (usefull for date form elements that can be blank.)
28822      */
28823     
28824     showClear: false,
28825     
28826     /**
28827      * Sets the value of the date field
28828      * @param {Date} value The date to set
28829      */
28830     setValue : function(value){
28831         var old = this.value;
28832         
28833         if (typeof(value) == 'string') {
28834          
28835             value = Date.parseDate(value, this.format);
28836         }
28837         if (!value) {
28838             value = new Date();
28839         }
28840         
28841         this.value = value.clearTime(true);
28842         if(this.el){
28843             this.update(this.value);
28844         }
28845     },
28846
28847     /**
28848      * Gets the current selected value of the date field
28849      * @return {Date} The selected date
28850      */
28851     getValue : function(){
28852         return this.value;
28853     },
28854
28855     // private
28856     focus : function(){
28857         if(this.el){
28858             this.update(this.activeDate);
28859         }
28860     },
28861
28862     // privateval
28863     onRender : function(container, position){
28864         
28865         var m = [
28866              '<table cellspacing="0">',
28867                 '<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>',
28868                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28869         var dn = this.dayNames;
28870         for(var i = 0; i < 7; i++){
28871             var d = this.startDay+i;
28872             if(d > 6){
28873                 d = d-7;
28874             }
28875             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28876         }
28877         m[m.length] = "</tr></thead><tbody><tr>";
28878         for(var i = 0; i < 42; i++) {
28879             if(i % 7 == 0 && i != 0){
28880                 m[m.length] = "</tr><tr>";
28881             }
28882             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28883         }
28884         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28885             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28886
28887         var el = document.createElement("div");
28888         el.className = "x-date-picker";
28889         el.innerHTML = m.join("");
28890
28891         container.dom.insertBefore(el, position);
28892
28893         this.el = Roo.get(el);
28894         this.eventEl = Roo.get(el.firstChild);
28895
28896         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28897             handler: this.showPrevMonth,
28898             scope: this,
28899             preventDefault:true,
28900             stopDefault:true
28901         });
28902
28903         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28904             handler: this.showNextMonth,
28905             scope: this,
28906             preventDefault:true,
28907             stopDefault:true
28908         });
28909
28910         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28911
28912         this.monthPicker = this.el.down('div.x-date-mp');
28913         this.monthPicker.enableDisplayMode('block');
28914         
28915         var kn = new Roo.KeyNav(this.eventEl, {
28916             "left" : function(e){
28917                 e.ctrlKey ?
28918                     this.showPrevMonth() :
28919                     this.update(this.activeDate.add("d", -1));
28920             },
28921
28922             "right" : function(e){
28923                 e.ctrlKey ?
28924                     this.showNextMonth() :
28925                     this.update(this.activeDate.add("d", 1));
28926             },
28927
28928             "up" : function(e){
28929                 e.ctrlKey ?
28930                     this.showNextYear() :
28931                     this.update(this.activeDate.add("d", -7));
28932             },
28933
28934             "down" : function(e){
28935                 e.ctrlKey ?
28936                     this.showPrevYear() :
28937                     this.update(this.activeDate.add("d", 7));
28938             },
28939
28940             "pageUp" : function(e){
28941                 this.showNextMonth();
28942             },
28943
28944             "pageDown" : function(e){
28945                 this.showPrevMonth();
28946             },
28947
28948             "enter" : function(e){
28949                 e.stopPropagation();
28950                 return true;
28951             },
28952
28953             scope : this
28954         });
28955
28956         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28957
28958         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28959
28960         this.el.unselectable();
28961         
28962         this.cells = this.el.select("table.x-date-inner tbody td");
28963         this.textNodes = this.el.query("table.x-date-inner tbody span");
28964
28965         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28966             text: "&#160;",
28967             tooltip: this.monthYearText
28968         });
28969
28970         this.mbtn.on('click', this.showMonthPicker, this);
28971         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28972
28973
28974         var today = (new Date()).dateFormat(this.format);
28975         
28976         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28977         if (this.showClear) {
28978             baseTb.add( new Roo.Toolbar.Fill());
28979         }
28980         baseTb.add({
28981             text: String.format(this.todayText, today),
28982             tooltip: String.format(this.todayTip, today),
28983             handler: this.selectToday,
28984             scope: this
28985         });
28986         
28987         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28988             
28989         //});
28990         if (this.showClear) {
28991             
28992             baseTb.add( new Roo.Toolbar.Fill());
28993             baseTb.add({
28994                 text: '&#160;',
28995                 cls: 'x-btn-icon x-btn-clear',
28996                 handler: function() {
28997                     //this.value = '';
28998                     this.fireEvent("select", this, '');
28999                 },
29000                 scope: this
29001             });
29002         }
29003         
29004         
29005         if(Roo.isIE){
29006             this.el.repaint();
29007         }
29008         this.update(this.value);
29009     },
29010
29011     createMonthPicker : function(){
29012         if(!this.monthPicker.dom.firstChild){
29013             var buf = ['<table border="0" cellspacing="0">'];
29014             for(var i = 0; i < 6; i++){
29015                 buf.push(
29016                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29017                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29018                     i == 0 ?
29019                     '<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>' :
29020                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29021                 );
29022             }
29023             buf.push(
29024                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29025                     this.okText,
29026                     '</button><button type="button" class="x-date-mp-cancel">',
29027                     this.cancelText,
29028                     '</button></td></tr>',
29029                 '</table>'
29030             );
29031             this.monthPicker.update(buf.join(''));
29032             this.monthPicker.on('click', this.onMonthClick, this);
29033             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29034
29035             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29036             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29037
29038             this.mpMonths.each(function(m, a, i){
29039                 i += 1;
29040                 if((i%2) == 0){
29041                     m.dom.xmonth = 5 + Math.round(i * .5);
29042                 }else{
29043                     m.dom.xmonth = Math.round((i-1) * .5);
29044                 }
29045             });
29046         }
29047     },
29048
29049     showMonthPicker : function(){
29050         this.createMonthPicker();
29051         var size = this.el.getSize();
29052         this.monthPicker.setSize(size);
29053         this.monthPicker.child('table').setSize(size);
29054
29055         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29056         this.updateMPMonth(this.mpSelMonth);
29057         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29058         this.updateMPYear(this.mpSelYear);
29059
29060         this.monthPicker.slideIn('t', {duration:.2});
29061     },
29062
29063     updateMPYear : function(y){
29064         this.mpyear = y;
29065         var ys = this.mpYears.elements;
29066         for(var i = 1; i <= 10; i++){
29067             var td = ys[i-1], y2;
29068             if((i%2) == 0){
29069                 y2 = y + Math.round(i * .5);
29070                 td.firstChild.innerHTML = y2;
29071                 td.xyear = y2;
29072             }else{
29073                 y2 = y - (5-Math.round(i * .5));
29074                 td.firstChild.innerHTML = y2;
29075                 td.xyear = y2;
29076             }
29077             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29078         }
29079     },
29080
29081     updateMPMonth : function(sm){
29082         this.mpMonths.each(function(m, a, i){
29083             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29084         });
29085     },
29086
29087     selectMPMonth: function(m){
29088         
29089     },
29090
29091     onMonthClick : function(e, t){
29092         e.stopEvent();
29093         var el = new Roo.Element(t), pn;
29094         if(el.is('button.x-date-mp-cancel')){
29095             this.hideMonthPicker();
29096         }
29097         else if(el.is('button.x-date-mp-ok')){
29098             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29099             this.hideMonthPicker();
29100         }
29101         else if(pn = el.up('td.x-date-mp-month', 2)){
29102             this.mpMonths.removeClass('x-date-mp-sel');
29103             pn.addClass('x-date-mp-sel');
29104             this.mpSelMonth = pn.dom.xmonth;
29105         }
29106         else if(pn = el.up('td.x-date-mp-year', 2)){
29107             this.mpYears.removeClass('x-date-mp-sel');
29108             pn.addClass('x-date-mp-sel');
29109             this.mpSelYear = pn.dom.xyear;
29110         }
29111         else if(el.is('a.x-date-mp-prev')){
29112             this.updateMPYear(this.mpyear-10);
29113         }
29114         else if(el.is('a.x-date-mp-next')){
29115             this.updateMPYear(this.mpyear+10);
29116         }
29117     },
29118
29119     onMonthDblClick : function(e, t){
29120         e.stopEvent();
29121         var el = new Roo.Element(t), pn;
29122         if(pn = el.up('td.x-date-mp-month', 2)){
29123             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29124             this.hideMonthPicker();
29125         }
29126         else if(pn = el.up('td.x-date-mp-year', 2)){
29127             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29128             this.hideMonthPicker();
29129         }
29130     },
29131
29132     hideMonthPicker : function(disableAnim){
29133         if(this.monthPicker){
29134             if(disableAnim === true){
29135                 this.monthPicker.hide();
29136             }else{
29137                 this.monthPicker.slideOut('t', {duration:.2});
29138             }
29139         }
29140     },
29141
29142     // private
29143     showPrevMonth : function(e){
29144         this.update(this.activeDate.add("mo", -1));
29145     },
29146
29147     // private
29148     showNextMonth : function(e){
29149         this.update(this.activeDate.add("mo", 1));
29150     },
29151
29152     // private
29153     showPrevYear : function(){
29154         this.update(this.activeDate.add("y", -1));
29155     },
29156
29157     // private
29158     showNextYear : function(){
29159         this.update(this.activeDate.add("y", 1));
29160     },
29161
29162     // private
29163     handleMouseWheel : function(e){
29164         var delta = e.getWheelDelta();
29165         if(delta > 0){
29166             this.showPrevMonth();
29167             e.stopEvent();
29168         } else if(delta < 0){
29169             this.showNextMonth();
29170             e.stopEvent();
29171         }
29172     },
29173
29174     // private
29175     handleDateClick : function(e, t){
29176         e.stopEvent();
29177         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29178             this.setValue(new Date(t.dateValue));
29179             this.fireEvent("select", this, this.value);
29180         }
29181     },
29182
29183     // private
29184     selectToday : function(){
29185         this.setValue(new Date().clearTime());
29186         this.fireEvent("select", this, this.value);
29187     },
29188
29189     // private
29190     update : function(date)
29191     {
29192         var vd = this.activeDate;
29193         this.activeDate = date;
29194         if(vd && this.el){
29195             var t = date.getTime();
29196             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29197                 this.cells.removeClass("x-date-selected");
29198                 this.cells.each(function(c){
29199                    if(c.dom.firstChild.dateValue == t){
29200                        c.addClass("x-date-selected");
29201                        setTimeout(function(){
29202                             try{c.dom.firstChild.focus();}catch(e){}
29203                        }, 50);
29204                        return false;
29205                    }
29206                 });
29207                 return;
29208             }
29209         }
29210         
29211         var days = date.getDaysInMonth();
29212         var firstOfMonth = date.getFirstDateOfMonth();
29213         var startingPos = firstOfMonth.getDay()-this.startDay;
29214
29215         if(startingPos <= this.startDay){
29216             startingPos += 7;
29217         }
29218
29219         var pm = date.add("mo", -1);
29220         var prevStart = pm.getDaysInMonth()-startingPos;
29221
29222         var cells = this.cells.elements;
29223         var textEls = this.textNodes;
29224         days += startingPos;
29225
29226         // convert everything to numbers so it's fast
29227         var day = 86400000;
29228         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29229         var today = new Date().clearTime().getTime();
29230         var sel = date.clearTime().getTime();
29231         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29232         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29233         var ddMatch = this.disabledDatesRE;
29234         var ddText = this.disabledDatesText;
29235         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29236         var ddaysText = this.disabledDaysText;
29237         var format = this.format;
29238
29239         var setCellClass = function(cal, cell){
29240             cell.title = "";
29241             var t = d.getTime();
29242             cell.firstChild.dateValue = t;
29243             if(t == today){
29244                 cell.className += " x-date-today";
29245                 cell.title = cal.todayText;
29246             }
29247             if(t == sel){
29248                 cell.className += " x-date-selected";
29249                 setTimeout(function(){
29250                     try{cell.firstChild.focus();}catch(e){}
29251                 }, 50);
29252             }
29253             // disabling
29254             if(t < min) {
29255                 cell.className = " x-date-disabled";
29256                 cell.title = cal.minText;
29257                 return;
29258             }
29259             if(t > max) {
29260                 cell.className = " x-date-disabled";
29261                 cell.title = cal.maxText;
29262                 return;
29263             }
29264             if(ddays){
29265                 if(ddays.indexOf(d.getDay()) != -1){
29266                     cell.title = ddaysText;
29267                     cell.className = " x-date-disabled";
29268                 }
29269             }
29270             if(ddMatch && format){
29271                 var fvalue = d.dateFormat(format);
29272                 if(ddMatch.test(fvalue)){
29273                     cell.title = ddText.replace("%0", fvalue);
29274                     cell.className = " x-date-disabled";
29275                 }
29276             }
29277         };
29278
29279         var i = 0;
29280         for(; i < startingPos; i++) {
29281             textEls[i].innerHTML = (++prevStart);
29282             d.setDate(d.getDate()+1);
29283             cells[i].className = "x-date-prevday";
29284             setCellClass(this, cells[i]);
29285         }
29286         for(; i < days; i++){
29287             intDay = i - startingPos + 1;
29288             textEls[i].innerHTML = (intDay);
29289             d.setDate(d.getDate()+1);
29290             cells[i].className = "x-date-active";
29291             setCellClass(this, cells[i]);
29292         }
29293         var extraDays = 0;
29294         for(; i < 42; i++) {
29295              textEls[i].innerHTML = (++extraDays);
29296              d.setDate(d.getDate()+1);
29297              cells[i].className = "x-date-nextday";
29298              setCellClass(this, cells[i]);
29299         }
29300
29301         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29302         this.fireEvent('monthchange', this, date);
29303         
29304         if(!this.internalRender){
29305             var main = this.el.dom.firstChild;
29306             var w = main.offsetWidth;
29307             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29308             Roo.fly(main).setWidth(w);
29309             this.internalRender = true;
29310             // opera does not respect the auto grow header center column
29311             // then, after it gets a width opera refuses to recalculate
29312             // without a second pass
29313             if(Roo.isOpera && !this.secondPass){
29314                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29315                 this.secondPass = true;
29316                 this.update.defer(10, this, [date]);
29317             }
29318         }
29319         
29320         
29321     }
29322 });        /*
29323  * Based on:
29324  * Ext JS Library 1.1.1
29325  * Copyright(c) 2006-2007, Ext JS, LLC.
29326  *
29327  * Originally Released Under LGPL - original licence link has changed is not relivant.
29328  *
29329  * Fork - LGPL
29330  * <script type="text/javascript">
29331  */
29332 /**
29333  * @class Roo.TabPanel
29334  * @extends Roo.util.Observable
29335  * A lightweight tab container.
29336  * <br><br>
29337  * Usage:
29338  * <pre><code>
29339 // basic tabs 1, built from existing content
29340 var tabs = new Roo.TabPanel("tabs1");
29341 tabs.addTab("script", "View Script");
29342 tabs.addTab("markup", "View Markup");
29343 tabs.activate("script");
29344
29345 // more advanced tabs, built from javascript
29346 var jtabs = new Roo.TabPanel("jtabs");
29347 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29348
29349 // set up the UpdateManager
29350 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29351 var updater = tab2.getUpdateManager();
29352 updater.setDefaultUrl("ajax1.htm");
29353 tab2.on('activate', updater.refresh, updater, true);
29354
29355 // Use setUrl for Ajax loading
29356 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29357 tab3.setUrl("ajax2.htm", null, true);
29358
29359 // Disabled tab
29360 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29361 tab4.disable();
29362
29363 jtabs.activate("jtabs-1");
29364  * </code></pre>
29365  * @constructor
29366  * Create a new TabPanel.
29367  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29368  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29369  */
29370 Roo.TabPanel = function(container, config){
29371     /**
29372     * The container element for this TabPanel.
29373     * @type Roo.Element
29374     */
29375     this.el = Roo.get(container, true);
29376     if(config){
29377         if(typeof config == "boolean"){
29378             this.tabPosition = config ? "bottom" : "top";
29379         }else{
29380             Roo.apply(this, config);
29381         }
29382     }
29383     if(this.tabPosition == "bottom"){
29384         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29385         this.el.addClass("x-tabs-bottom");
29386     }
29387     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29388     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29389     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29390     if(Roo.isIE){
29391         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29392     }
29393     if(this.tabPosition != "bottom"){
29394         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29395          * @type Roo.Element
29396          */
29397         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29398         this.el.addClass("x-tabs-top");
29399     }
29400     this.items = [];
29401
29402     this.bodyEl.setStyle("position", "relative");
29403
29404     this.active = null;
29405     this.activateDelegate = this.activate.createDelegate(this);
29406
29407     this.addEvents({
29408         /**
29409          * @event tabchange
29410          * Fires when the active tab changes
29411          * @param {Roo.TabPanel} this
29412          * @param {Roo.TabPanelItem} activePanel The new active tab
29413          */
29414         "tabchange": true,
29415         /**
29416          * @event beforetabchange
29417          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29418          * @param {Roo.TabPanel} this
29419          * @param {Object} e Set cancel to true on this object to cancel the tab change
29420          * @param {Roo.TabPanelItem} tab The tab being changed to
29421          */
29422         "beforetabchange" : true
29423     });
29424
29425     Roo.EventManager.onWindowResize(this.onResize, this);
29426     this.cpad = this.el.getPadding("lr");
29427     this.hiddenCount = 0;
29428
29429
29430     // toolbar on the tabbar support...
29431     if (this.toolbar) {
29432         var tcfg = this.toolbar;
29433         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29434         this.toolbar = new Roo.Toolbar(tcfg);
29435         if (Roo.isSafari) {
29436             var tbl = tcfg.container.child('table', true);
29437             tbl.setAttribute('width', '100%');
29438         }
29439         
29440     }
29441    
29442
29443
29444     Roo.TabPanel.superclass.constructor.call(this);
29445 };
29446
29447 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29448     /*
29449      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29450      */
29451     tabPosition : "top",
29452     /*
29453      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29454      */
29455     currentTabWidth : 0,
29456     /*
29457      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29458      */
29459     minTabWidth : 40,
29460     /*
29461      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29462      */
29463     maxTabWidth : 250,
29464     /*
29465      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29466      */
29467     preferredTabWidth : 175,
29468     /*
29469      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29470      */
29471     resizeTabs : false,
29472     /*
29473      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29474      */
29475     monitorResize : true,
29476     /*
29477      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29478      */
29479     toolbar : false,
29480
29481     /**
29482      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29483      * @param {String} id The id of the div to use <b>or create</b>
29484      * @param {String} text The text for the tab
29485      * @param {String} content (optional) Content to put in the TabPanelItem body
29486      * @param {Boolean} closable (optional) True to create a close icon on the tab
29487      * @return {Roo.TabPanelItem} The created TabPanelItem
29488      */
29489     addTab : function(id, text, content, closable){
29490         var item = new Roo.TabPanelItem(this, id, text, closable);
29491         this.addTabItem(item);
29492         if(content){
29493             item.setContent(content);
29494         }
29495         return item;
29496     },
29497
29498     /**
29499      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29500      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29501      * @return {Roo.TabPanelItem}
29502      */
29503     getTab : function(id){
29504         return this.items[id];
29505     },
29506
29507     /**
29508      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29509      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29510      */
29511     hideTab : function(id){
29512         var t = this.items[id];
29513         if(!t.isHidden()){
29514            t.setHidden(true);
29515            this.hiddenCount++;
29516            this.autoSizeTabs();
29517         }
29518     },
29519
29520     /**
29521      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29522      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29523      */
29524     unhideTab : function(id){
29525         var t = this.items[id];
29526         if(t.isHidden()){
29527            t.setHidden(false);
29528            this.hiddenCount--;
29529            this.autoSizeTabs();
29530         }
29531     },
29532
29533     /**
29534      * Adds an existing {@link Roo.TabPanelItem}.
29535      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29536      */
29537     addTabItem : function(item){
29538         this.items[item.id] = item;
29539         this.items.push(item);
29540         if(this.resizeTabs){
29541            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29542            this.autoSizeTabs();
29543         }else{
29544             item.autoSize();
29545         }
29546     },
29547
29548     /**
29549      * Removes a {@link Roo.TabPanelItem}.
29550      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29551      */
29552     removeTab : function(id){
29553         var items = this.items;
29554         var tab = items[id];
29555         if(!tab) { return; }
29556         var index = items.indexOf(tab);
29557         if(this.active == tab && items.length > 1){
29558             var newTab = this.getNextAvailable(index);
29559             if(newTab) {
29560                 newTab.activate();
29561             }
29562         }
29563         this.stripEl.dom.removeChild(tab.pnode.dom);
29564         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29565             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29566         }
29567         items.splice(index, 1);
29568         delete this.items[tab.id];
29569         tab.fireEvent("close", tab);
29570         tab.purgeListeners();
29571         this.autoSizeTabs();
29572     },
29573
29574     getNextAvailable : function(start){
29575         var items = this.items;
29576         var index = start;
29577         // look for a next tab that will slide over to
29578         // replace the one being removed
29579         while(index < items.length){
29580             var item = items[++index];
29581             if(item && !item.isHidden()){
29582                 return item;
29583             }
29584         }
29585         // if one isn't found select the previous tab (on the left)
29586         index = start;
29587         while(index >= 0){
29588             var item = items[--index];
29589             if(item && !item.isHidden()){
29590                 return item;
29591             }
29592         }
29593         return null;
29594     },
29595
29596     /**
29597      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29598      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29599      */
29600     disableTab : function(id){
29601         var tab = this.items[id];
29602         if(tab && this.active != tab){
29603             tab.disable();
29604         }
29605     },
29606
29607     /**
29608      * Enables a {@link Roo.TabPanelItem} that is disabled.
29609      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29610      */
29611     enableTab : function(id){
29612         var tab = this.items[id];
29613         tab.enable();
29614     },
29615
29616     /**
29617      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29618      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29619      * @return {Roo.TabPanelItem} The TabPanelItem.
29620      */
29621     activate : function(id){
29622         var tab = this.items[id];
29623         if(!tab){
29624             return null;
29625         }
29626         if(tab == this.active || tab.disabled){
29627             return tab;
29628         }
29629         var e = {};
29630         this.fireEvent("beforetabchange", this, e, tab);
29631         if(e.cancel !== true && !tab.disabled){
29632             if(this.active){
29633                 this.active.hide();
29634             }
29635             this.active = this.items[id];
29636             this.active.show();
29637             this.fireEvent("tabchange", this, this.active);
29638         }
29639         return tab;
29640     },
29641
29642     /**
29643      * Gets the active {@link Roo.TabPanelItem}.
29644      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29645      */
29646     getActiveTab : function(){
29647         return this.active;
29648     },
29649
29650     /**
29651      * Updates the tab body element to fit the height of the container element
29652      * for overflow scrolling
29653      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29654      */
29655     syncHeight : function(targetHeight){
29656         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29657         var bm = this.bodyEl.getMargins();
29658         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29659         this.bodyEl.setHeight(newHeight);
29660         return newHeight;
29661     },
29662
29663     onResize : function(){
29664         if(this.monitorResize){
29665             this.autoSizeTabs();
29666         }
29667     },
29668
29669     /**
29670      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29671      */
29672     beginUpdate : function(){
29673         this.updating = true;
29674     },
29675
29676     /**
29677      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29678      */
29679     endUpdate : function(){
29680         this.updating = false;
29681         this.autoSizeTabs();
29682     },
29683
29684     /**
29685      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29686      */
29687     autoSizeTabs : function(){
29688         var count = this.items.length;
29689         var vcount = count - this.hiddenCount;
29690         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29691             return;
29692         }
29693         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29694         var availWidth = Math.floor(w / vcount);
29695         var b = this.stripBody;
29696         if(b.getWidth() > w){
29697             var tabs = this.items;
29698             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29699             if(availWidth < this.minTabWidth){
29700                 /*if(!this.sleft){    // incomplete scrolling code
29701                     this.createScrollButtons();
29702                 }
29703                 this.showScroll();
29704                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29705             }
29706         }else{
29707             if(this.currentTabWidth < this.preferredTabWidth){
29708                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29709             }
29710         }
29711     },
29712
29713     /**
29714      * Returns the number of tabs in this TabPanel.
29715      * @return {Number}
29716      */
29717      getCount : function(){
29718          return this.items.length;
29719      },
29720
29721     /**
29722      * Resizes all the tabs to the passed width
29723      * @param {Number} The new width
29724      */
29725     setTabWidth : function(width){
29726         this.currentTabWidth = width;
29727         for(var i = 0, len = this.items.length; i < len; i++) {
29728                 if(!this.items[i].isHidden()) {
29729                 this.items[i].setWidth(width);
29730             }
29731         }
29732     },
29733
29734     /**
29735      * Destroys this TabPanel
29736      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29737      */
29738     destroy : function(removeEl){
29739         Roo.EventManager.removeResizeListener(this.onResize, this);
29740         for(var i = 0, len = this.items.length; i < len; i++){
29741             this.items[i].purgeListeners();
29742         }
29743         if(removeEl === true){
29744             this.el.update("");
29745             this.el.remove();
29746         }
29747     }
29748 });
29749
29750 /**
29751  * @class Roo.TabPanelItem
29752  * @extends Roo.util.Observable
29753  * Represents an individual item (tab plus body) in a TabPanel.
29754  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29755  * @param {String} id The id of this TabPanelItem
29756  * @param {String} text The text for the tab of this TabPanelItem
29757  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29758  */
29759 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29760     /**
29761      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29762      * @type Roo.TabPanel
29763      */
29764     this.tabPanel = tabPanel;
29765     /**
29766      * The id for this TabPanelItem
29767      * @type String
29768      */
29769     this.id = id;
29770     /** @private */
29771     this.disabled = false;
29772     /** @private */
29773     this.text = text;
29774     /** @private */
29775     this.loaded = false;
29776     this.closable = closable;
29777
29778     /**
29779      * The body element for this TabPanelItem.
29780      * @type Roo.Element
29781      */
29782     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29783     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29784     this.bodyEl.setStyle("display", "block");
29785     this.bodyEl.setStyle("zoom", "1");
29786     this.hideAction();
29787
29788     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29789     /** @private */
29790     this.el = Roo.get(els.el, true);
29791     this.inner = Roo.get(els.inner, true);
29792     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29793     this.pnode = Roo.get(els.el.parentNode, true);
29794     this.el.on("mousedown", this.onTabMouseDown, this);
29795     this.el.on("click", this.onTabClick, this);
29796     /** @private */
29797     if(closable){
29798         var c = Roo.get(els.close, true);
29799         c.dom.title = this.closeText;
29800         c.addClassOnOver("close-over");
29801         c.on("click", this.closeClick, this);
29802      }
29803
29804     this.addEvents({
29805          /**
29806          * @event activate
29807          * Fires when this tab becomes the active tab.
29808          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29809          * @param {Roo.TabPanelItem} this
29810          */
29811         "activate": true,
29812         /**
29813          * @event beforeclose
29814          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29815          * @param {Roo.TabPanelItem} this
29816          * @param {Object} e Set cancel to true on this object to cancel the close.
29817          */
29818         "beforeclose": true,
29819         /**
29820          * @event close
29821          * Fires when this tab is closed.
29822          * @param {Roo.TabPanelItem} this
29823          */
29824          "close": true,
29825         /**
29826          * @event deactivate
29827          * Fires when this tab is no longer the active tab.
29828          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29829          * @param {Roo.TabPanelItem} this
29830          */
29831          "deactivate" : true
29832     });
29833     this.hidden = false;
29834
29835     Roo.TabPanelItem.superclass.constructor.call(this);
29836 };
29837
29838 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29839     purgeListeners : function(){
29840        Roo.util.Observable.prototype.purgeListeners.call(this);
29841        this.el.removeAllListeners();
29842     },
29843     /**
29844      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29845      */
29846     show : function(){
29847         this.pnode.addClass("on");
29848         this.showAction();
29849         if(Roo.isOpera){
29850             this.tabPanel.stripWrap.repaint();
29851         }
29852         this.fireEvent("activate", this.tabPanel, this);
29853     },
29854
29855     /**
29856      * Returns true if this tab is the active tab.
29857      * @return {Boolean}
29858      */
29859     isActive : function(){
29860         return this.tabPanel.getActiveTab() == this;
29861     },
29862
29863     /**
29864      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29865      */
29866     hide : function(){
29867         this.pnode.removeClass("on");
29868         this.hideAction();
29869         this.fireEvent("deactivate", this.tabPanel, this);
29870     },
29871
29872     hideAction : function(){
29873         this.bodyEl.hide();
29874         this.bodyEl.setStyle("position", "absolute");
29875         this.bodyEl.setLeft("-20000px");
29876         this.bodyEl.setTop("-20000px");
29877     },
29878
29879     showAction : function(){
29880         this.bodyEl.setStyle("position", "relative");
29881         this.bodyEl.setTop("");
29882         this.bodyEl.setLeft("");
29883         this.bodyEl.show();
29884     },
29885
29886     /**
29887      * Set the tooltip for the tab.
29888      * @param {String} tooltip The tab's tooltip
29889      */
29890     setTooltip : function(text){
29891         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29892             this.textEl.dom.qtip = text;
29893             this.textEl.dom.removeAttribute('title');
29894         }else{
29895             this.textEl.dom.title = text;
29896         }
29897     },
29898
29899     onTabClick : function(e){
29900         e.preventDefault();
29901         this.tabPanel.activate(this.id);
29902     },
29903
29904     onTabMouseDown : function(e){
29905         e.preventDefault();
29906         this.tabPanel.activate(this.id);
29907     },
29908
29909     getWidth : function(){
29910         return this.inner.getWidth();
29911     },
29912
29913     setWidth : function(width){
29914         var iwidth = width - this.pnode.getPadding("lr");
29915         this.inner.setWidth(iwidth);
29916         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29917         this.pnode.setWidth(width);
29918     },
29919
29920     /**
29921      * Show or hide the tab
29922      * @param {Boolean} hidden True to hide or false to show.
29923      */
29924     setHidden : function(hidden){
29925         this.hidden = hidden;
29926         this.pnode.setStyle("display", hidden ? "none" : "");
29927     },
29928
29929     /**
29930      * Returns true if this tab is "hidden"
29931      * @return {Boolean}
29932      */
29933     isHidden : function(){
29934         return this.hidden;
29935     },
29936
29937     /**
29938      * Returns the text for this tab
29939      * @return {String}
29940      */
29941     getText : function(){
29942         return this.text;
29943     },
29944
29945     autoSize : function(){
29946         //this.el.beginMeasure();
29947         this.textEl.setWidth(1);
29948         /*
29949          *  #2804 [new] Tabs in Roojs
29950          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29951          */
29952         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29953         //this.el.endMeasure();
29954     },
29955
29956     /**
29957      * Sets the text for the tab (Note: this also sets the tooltip text)
29958      * @param {String} text The tab's text and tooltip
29959      */
29960     setText : function(text){
29961         this.text = text;
29962         this.textEl.update(text);
29963         this.setTooltip(text);
29964         if(!this.tabPanel.resizeTabs){
29965             this.autoSize();
29966         }
29967     },
29968     /**
29969      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29970      */
29971     activate : function(){
29972         this.tabPanel.activate(this.id);
29973     },
29974
29975     /**
29976      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29977      */
29978     disable : function(){
29979         if(this.tabPanel.active != this){
29980             this.disabled = true;
29981             this.pnode.addClass("disabled");
29982         }
29983     },
29984
29985     /**
29986      * Enables this TabPanelItem if it was previously disabled.
29987      */
29988     enable : function(){
29989         this.disabled = false;
29990         this.pnode.removeClass("disabled");
29991     },
29992
29993     /**
29994      * Sets the content for this TabPanelItem.
29995      * @param {String} content The content
29996      * @param {Boolean} loadScripts true to look for and load scripts
29997      */
29998     setContent : function(content, loadScripts){
29999         this.bodyEl.update(content, loadScripts);
30000     },
30001
30002     /**
30003      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30004      * @return {Roo.UpdateManager} The UpdateManager
30005      */
30006     getUpdateManager : function(){
30007         return this.bodyEl.getUpdateManager();
30008     },
30009
30010     /**
30011      * Set a URL to be used to load the content for this TabPanelItem.
30012      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30013      * @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)
30014      * @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)
30015      * @return {Roo.UpdateManager} The UpdateManager
30016      */
30017     setUrl : function(url, params, loadOnce){
30018         if(this.refreshDelegate){
30019             this.un('activate', this.refreshDelegate);
30020         }
30021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30022         this.on("activate", this.refreshDelegate);
30023         return this.bodyEl.getUpdateManager();
30024     },
30025
30026     /** @private */
30027     _handleRefresh : function(url, params, loadOnce){
30028         if(!loadOnce || !this.loaded){
30029             var updater = this.bodyEl.getUpdateManager();
30030             updater.update(url, params, this._setLoaded.createDelegate(this));
30031         }
30032     },
30033
30034     /**
30035      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30036      *   Will fail silently if the setUrl method has not been called.
30037      *   This does not activate the panel, just updates its content.
30038      */
30039     refresh : function(){
30040         if(this.refreshDelegate){
30041            this.loaded = false;
30042            this.refreshDelegate();
30043         }
30044     },
30045
30046     /** @private */
30047     _setLoaded : function(){
30048         this.loaded = true;
30049     },
30050
30051     /** @private */
30052     closeClick : function(e){
30053         var o = {};
30054         e.stopEvent();
30055         this.fireEvent("beforeclose", this, o);
30056         if(o.cancel !== true){
30057             this.tabPanel.removeTab(this.id);
30058         }
30059     },
30060     /**
30061      * The text displayed in the tooltip for the close icon.
30062      * @type String
30063      */
30064     closeText : "Close this tab"
30065 });
30066
30067 /** @private */
30068 Roo.TabPanel.prototype.createStrip = function(container){
30069     var strip = document.createElement("div");
30070     strip.className = "x-tabs-wrap";
30071     container.appendChild(strip);
30072     return strip;
30073 };
30074 /** @private */
30075 Roo.TabPanel.prototype.createStripList = function(strip){
30076     // div wrapper for retard IE
30077     // returns the "tr" element.
30078     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30079         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30080         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30081     return strip.firstChild.firstChild.firstChild.firstChild;
30082 };
30083 /** @private */
30084 Roo.TabPanel.prototype.createBody = function(container){
30085     var body = document.createElement("div");
30086     Roo.id(body, "tab-body");
30087     Roo.fly(body).addClass("x-tabs-body");
30088     container.appendChild(body);
30089     return body;
30090 };
30091 /** @private */
30092 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30093     var body = Roo.getDom(id);
30094     if(!body){
30095         body = document.createElement("div");
30096         body.id = id;
30097     }
30098     Roo.fly(body).addClass("x-tabs-item-body");
30099     bodyEl.insertBefore(body, bodyEl.firstChild);
30100     return body;
30101 };
30102 /** @private */
30103 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30104     var td = document.createElement("td");
30105     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30106     //stripEl.appendChild(td);
30107     if(closable){
30108         td.className = "x-tabs-closable";
30109         if(!this.closeTpl){
30110             this.closeTpl = new Roo.Template(
30111                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30112                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30113                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30114             );
30115         }
30116         var el = this.closeTpl.overwrite(td, {"text": text});
30117         var close = el.getElementsByTagName("div")[0];
30118         var inner = el.getElementsByTagName("em")[0];
30119         return {"el": el, "close": close, "inner": inner};
30120     } else {
30121         if(!this.tabTpl){
30122             this.tabTpl = new Roo.Template(
30123                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30124                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30125             );
30126         }
30127         var el = this.tabTpl.overwrite(td, {"text": text});
30128         var inner = el.getElementsByTagName("em")[0];
30129         return {"el": el, "inner": inner};
30130     }
30131 };/*
30132  * Based on:
30133  * Ext JS Library 1.1.1
30134  * Copyright(c) 2006-2007, Ext JS, LLC.
30135  *
30136  * Originally Released Under LGPL - original licence link has changed is not relivant.
30137  *
30138  * Fork - LGPL
30139  * <script type="text/javascript">
30140  */
30141
30142 /**
30143  * @class Roo.Button
30144  * @extends Roo.util.Observable
30145  * Simple Button class
30146  * @cfg {String} text The button text
30147  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30148  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30149  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30150  * @cfg {Object} scope The scope of the handler
30151  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30152  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30153  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30154  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30155  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30156  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30157    applies if enableToggle = true)
30158  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30159  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30160   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30161  * @constructor
30162  * Create a new button
30163  * @param {Object} config The config object
30164  */
30165 Roo.Button = function(renderTo, config)
30166 {
30167     if (!config) {
30168         config = renderTo;
30169         renderTo = config.renderTo || false;
30170     }
30171     
30172     Roo.apply(this, config);
30173     this.addEvents({
30174         /**
30175              * @event click
30176              * Fires when this button is clicked
30177              * @param {Button} this
30178              * @param {EventObject} e The click event
30179              */
30180             "click" : true,
30181         /**
30182              * @event toggle
30183              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30184              * @param {Button} this
30185              * @param {Boolean} pressed
30186              */
30187             "toggle" : true,
30188         /**
30189              * @event mouseover
30190              * Fires when the mouse hovers over the button
30191              * @param {Button} this
30192              * @param {Event} e The event object
30193              */
30194         'mouseover' : true,
30195         /**
30196              * @event mouseout
30197              * Fires when the mouse exits the button
30198              * @param {Button} this
30199              * @param {Event} e The event object
30200              */
30201         'mouseout': true,
30202          /**
30203              * @event render
30204              * Fires when the button is rendered
30205              * @param {Button} this
30206              */
30207         'render': true
30208     });
30209     if(this.menu){
30210         this.menu = Roo.menu.MenuMgr.get(this.menu);
30211     }
30212     // register listeners first!!  - so render can be captured..
30213     Roo.util.Observable.call(this);
30214     if(renderTo){
30215         this.render(renderTo);
30216     }
30217     
30218   
30219 };
30220
30221 Roo.extend(Roo.Button, Roo.util.Observable, {
30222     /**
30223      * 
30224      */
30225     
30226     /**
30227      * Read-only. True if this button is hidden
30228      * @type Boolean
30229      */
30230     hidden : false,
30231     /**
30232      * Read-only. True if this button is disabled
30233      * @type Boolean
30234      */
30235     disabled : false,
30236     /**
30237      * Read-only. True if this button is pressed (only if enableToggle = true)
30238      * @type Boolean
30239      */
30240     pressed : false,
30241
30242     /**
30243      * @cfg {Number} tabIndex 
30244      * The DOM tabIndex for this button (defaults to undefined)
30245      */
30246     tabIndex : undefined,
30247
30248     /**
30249      * @cfg {Boolean} enableToggle
30250      * True to enable pressed/not pressed toggling (defaults to false)
30251      */
30252     enableToggle: false,
30253     /**
30254      * @cfg {Roo.menu.Menu} menu
30255      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30256      */
30257     menu : undefined,
30258     /**
30259      * @cfg {String} menuAlign
30260      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30261      */
30262     menuAlign : "tl-bl?",
30263
30264     /**
30265      * @cfg {String} iconCls
30266      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30267      */
30268     iconCls : undefined,
30269     /**
30270      * @cfg {String} type
30271      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30272      */
30273     type : 'button',
30274
30275     // private
30276     menuClassTarget: 'tr',
30277
30278     /**
30279      * @cfg {String} clickEvent
30280      * The type of event to map to the button's event handler (defaults to 'click')
30281      */
30282     clickEvent : 'click',
30283
30284     /**
30285      * @cfg {Boolean} handleMouseEvents
30286      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30287      */
30288     handleMouseEvents : true,
30289
30290     /**
30291      * @cfg {String} tooltipType
30292      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30293      */
30294     tooltipType : 'qtip',
30295
30296     /**
30297      * @cfg {String} cls
30298      * A CSS class to apply to the button's main element.
30299      */
30300     
30301     /**
30302      * @cfg {Roo.Template} template (Optional)
30303      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30304      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30305      * require code modifications if required elements (e.g. a button) aren't present.
30306      */
30307
30308     // private
30309     render : function(renderTo){
30310         var btn;
30311         if(this.hideParent){
30312             this.parentEl = Roo.get(renderTo);
30313         }
30314         if(!this.dhconfig){
30315             if(!this.template){
30316                 if(!Roo.Button.buttonTemplate){
30317                     // hideous table template
30318                     Roo.Button.buttonTemplate = new Roo.Template(
30319                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30320                         '<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>',
30321                         "</tr></tbody></table>");
30322                 }
30323                 this.template = Roo.Button.buttonTemplate;
30324             }
30325             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30326             var btnEl = btn.child("button:first");
30327             btnEl.on('focus', this.onFocus, this);
30328             btnEl.on('blur', this.onBlur, this);
30329             if(this.cls){
30330                 btn.addClass(this.cls);
30331             }
30332             if(this.icon){
30333                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30334             }
30335             if(this.iconCls){
30336                 btnEl.addClass(this.iconCls);
30337                 if(!this.cls){
30338                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30339                 }
30340             }
30341             if(this.tabIndex !== undefined){
30342                 btnEl.dom.tabIndex = this.tabIndex;
30343             }
30344             if(this.tooltip){
30345                 if(typeof this.tooltip == 'object'){
30346                     Roo.QuickTips.tips(Roo.apply({
30347                           target: btnEl.id
30348                     }, this.tooltip));
30349                 } else {
30350                     btnEl.dom[this.tooltipType] = this.tooltip;
30351                 }
30352             }
30353         }else{
30354             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30355         }
30356         this.el = btn;
30357         if(this.id){
30358             this.el.dom.id = this.el.id = this.id;
30359         }
30360         if(this.menu){
30361             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30362             this.menu.on("show", this.onMenuShow, this);
30363             this.menu.on("hide", this.onMenuHide, this);
30364         }
30365         btn.addClass("x-btn");
30366         if(Roo.isIE && !Roo.isIE7){
30367             this.autoWidth.defer(1, this);
30368         }else{
30369             this.autoWidth();
30370         }
30371         if(this.handleMouseEvents){
30372             btn.on("mouseover", this.onMouseOver, this);
30373             btn.on("mouseout", this.onMouseOut, this);
30374             btn.on("mousedown", this.onMouseDown, this);
30375         }
30376         btn.on(this.clickEvent, this.onClick, this);
30377         //btn.on("mouseup", this.onMouseUp, this);
30378         if(this.hidden){
30379             this.hide();
30380         }
30381         if(this.disabled){
30382             this.disable();
30383         }
30384         Roo.ButtonToggleMgr.register(this);
30385         if(this.pressed){
30386             this.el.addClass("x-btn-pressed");
30387         }
30388         if(this.repeat){
30389             var repeater = new Roo.util.ClickRepeater(btn,
30390                 typeof this.repeat == "object" ? this.repeat : {}
30391             );
30392             repeater.on("click", this.onClick,  this);
30393         }
30394         
30395         this.fireEvent('render', this);
30396         
30397     },
30398     /**
30399      * Returns the button's underlying element
30400      * @return {Roo.Element} The element
30401      */
30402     getEl : function(){
30403         return this.el;  
30404     },
30405     
30406     /**
30407      * Destroys this Button and removes any listeners.
30408      */
30409     destroy : function(){
30410         Roo.ButtonToggleMgr.unregister(this);
30411         this.el.removeAllListeners();
30412         this.purgeListeners();
30413         this.el.remove();
30414     },
30415
30416     // private
30417     autoWidth : function(){
30418         if(this.el){
30419             this.el.setWidth("auto");
30420             if(Roo.isIE7 && Roo.isStrict){
30421                 var ib = this.el.child('button');
30422                 if(ib && ib.getWidth() > 20){
30423                     ib.clip();
30424                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30425                 }
30426             }
30427             if(this.minWidth){
30428                 if(this.hidden){
30429                     this.el.beginMeasure();
30430                 }
30431                 if(this.el.getWidth() < this.minWidth){
30432                     this.el.setWidth(this.minWidth);
30433                 }
30434                 if(this.hidden){
30435                     this.el.endMeasure();
30436                 }
30437             }
30438         }
30439     },
30440
30441     /**
30442      * Assigns this button's click handler
30443      * @param {Function} handler The function to call when the button is clicked
30444      * @param {Object} scope (optional) Scope for the function passed in
30445      */
30446     setHandler : function(handler, scope){
30447         this.handler = handler;
30448         this.scope = scope;  
30449     },
30450     
30451     /**
30452      * Sets this button's text
30453      * @param {String} text The button text
30454      */
30455     setText : function(text){
30456         this.text = text;
30457         if(this.el){
30458             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30459         }
30460         this.autoWidth();
30461     },
30462     
30463     /**
30464      * Gets the text for this button
30465      * @return {String} The button text
30466      */
30467     getText : function(){
30468         return this.text;  
30469     },
30470     
30471     /**
30472      * Show this button
30473      */
30474     show: function(){
30475         this.hidden = false;
30476         if(this.el){
30477             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30478         }
30479     },
30480     
30481     /**
30482      * Hide this button
30483      */
30484     hide: function(){
30485         this.hidden = true;
30486         if(this.el){
30487             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30488         }
30489     },
30490     
30491     /**
30492      * Convenience function for boolean show/hide
30493      * @param {Boolean} visible True to show, false to hide
30494      */
30495     setVisible: function(visible){
30496         if(visible) {
30497             this.show();
30498         }else{
30499             this.hide();
30500         }
30501     },
30502     
30503     /**
30504      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30505      * @param {Boolean} state (optional) Force a particular state
30506      */
30507     toggle : function(state){
30508         state = state === undefined ? !this.pressed : state;
30509         if(state != this.pressed){
30510             if(state){
30511                 this.el.addClass("x-btn-pressed");
30512                 this.pressed = true;
30513                 this.fireEvent("toggle", this, true);
30514             }else{
30515                 this.el.removeClass("x-btn-pressed");
30516                 this.pressed = false;
30517                 this.fireEvent("toggle", this, false);
30518             }
30519             if(this.toggleHandler){
30520                 this.toggleHandler.call(this.scope || this, this, state);
30521             }
30522         }
30523     },
30524     
30525     /**
30526      * Focus the button
30527      */
30528     focus : function(){
30529         this.el.child('button:first').focus();
30530     },
30531     
30532     /**
30533      * Disable this button
30534      */
30535     disable : function(){
30536         if(this.el){
30537             this.el.addClass("x-btn-disabled");
30538         }
30539         this.disabled = true;
30540     },
30541     
30542     /**
30543      * Enable this button
30544      */
30545     enable : function(){
30546         if(this.el){
30547             this.el.removeClass("x-btn-disabled");
30548         }
30549         this.disabled = false;
30550     },
30551
30552     /**
30553      * Convenience function for boolean enable/disable
30554      * @param {Boolean} enabled True to enable, false to disable
30555      */
30556     setDisabled : function(v){
30557         this[v !== true ? "enable" : "disable"]();
30558     },
30559
30560     // private
30561     onClick : function(e)
30562     {
30563         if(e){
30564             e.preventDefault();
30565         }
30566         if(e.button != 0){
30567             return;
30568         }
30569         if(!this.disabled){
30570             if(this.enableToggle){
30571                 this.toggle();
30572             }
30573             if(this.menu && !this.menu.isVisible()){
30574                 this.menu.show(this.el, this.menuAlign);
30575             }
30576             this.fireEvent("click", this, e);
30577             if(this.handler){
30578                 this.el.removeClass("x-btn-over");
30579                 this.handler.call(this.scope || this, this, e);
30580             }
30581         }
30582     },
30583     // private
30584     onMouseOver : function(e){
30585         if(!this.disabled){
30586             this.el.addClass("x-btn-over");
30587             this.fireEvent('mouseover', this, e);
30588         }
30589     },
30590     // private
30591     onMouseOut : function(e){
30592         if(!e.within(this.el,  true)){
30593             this.el.removeClass("x-btn-over");
30594             this.fireEvent('mouseout', this, e);
30595         }
30596     },
30597     // private
30598     onFocus : function(e){
30599         if(!this.disabled){
30600             this.el.addClass("x-btn-focus");
30601         }
30602     },
30603     // private
30604     onBlur : function(e){
30605         this.el.removeClass("x-btn-focus");
30606     },
30607     // private
30608     onMouseDown : function(e){
30609         if(!this.disabled && e.button == 0){
30610             this.el.addClass("x-btn-click");
30611             Roo.get(document).on('mouseup', this.onMouseUp, this);
30612         }
30613     },
30614     // private
30615     onMouseUp : function(e){
30616         if(e.button == 0){
30617             this.el.removeClass("x-btn-click");
30618             Roo.get(document).un('mouseup', this.onMouseUp, this);
30619         }
30620     },
30621     // private
30622     onMenuShow : function(e){
30623         this.el.addClass("x-btn-menu-active");
30624     },
30625     // private
30626     onMenuHide : function(e){
30627         this.el.removeClass("x-btn-menu-active");
30628     }   
30629 });
30630
30631 // Private utility class used by Button
30632 Roo.ButtonToggleMgr = function(){
30633    var groups = {};
30634    
30635    function toggleGroup(btn, state){
30636        if(state){
30637            var g = groups[btn.toggleGroup];
30638            for(var i = 0, l = g.length; i < l; i++){
30639                if(g[i] != btn){
30640                    g[i].toggle(false);
30641                }
30642            }
30643        }
30644    }
30645    
30646    return {
30647        register : function(btn){
30648            if(!btn.toggleGroup){
30649                return;
30650            }
30651            var g = groups[btn.toggleGroup];
30652            if(!g){
30653                g = groups[btn.toggleGroup] = [];
30654            }
30655            g.push(btn);
30656            btn.on("toggle", toggleGroup);
30657        },
30658        
30659        unregister : function(btn){
30660            if(!btn.toggleGroup){
30661                return;
30662            }
30663            var g = groups[btn.toggleGroup];
30664            if(g){
30665                g.remove(btn);
30666                btn.un("toggle", toggleGroup);
30667            }
30668        }
30669    };
30670 }();/*
30671  * Based on:
30672  * Ext JS Library 1.1.1
30673  * Copyright(c) 2006-2007, Ext JS, LLC.
30674  *
30675  * Originally Released Under LGPL - original licence link has changed is not relivant.
30676  *
30677  * Fork - LGPL
30678  * <script type="text/javascript">
30679  */
30680  
30681 /**
30682  * @class Roo.SplitButton
30683  * @extends Roo.Button
30684  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30685  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30686  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30687  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30688  * @cfg {String} arrowTooltip The title attribute of the arrow
30689  * @constructor
30690  * Create a new menu button
30691  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30692  * @param {Object} config The config object
30693  */
30694 Roo.SplitButton = function(renderTo, config){
30695     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30696     /**
30697      * @event arrowclick
30698      * Fires when this button's arrow is clicked
30699      * @param {SplitButton} this
30700      * @param {EventObject} e The click event
30701      */
30702     this.addEvents({"arrowclick":true});
30703 };
30704
30705 Roo.extend(Roo.SplitButton, Roo.Button, {
30706     render : function(renderTo){
30707         // this is one sweet looking template!
30708         var tpl = new Roo.Template(
30709             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30710             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30711             '<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>',
30712             "</tbody></table></td><td>",
30713             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30714             '<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>',
30715             "</tbody></table></td></tr></table>"
30716         );
30717         var btn = tpl.append(renderTo, [this.text, this.type], true);
30718         var btnEl = btn.child("button");
30719         if(this.cls){
30720             btn.addClass(this.cls);
30721         }
30722         if(this.icon){
30723             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30724         }
30725         if(this.iconCls){
30726             btnEl.addClass(this.iconCls);
30727             if(!this.cls){
30728                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30729             }
30730         }
30731         this.el = btn;
30732         if(this.handleMouseEvents){
30733             btn.on("mouseover", this.onMouseOver, this);
30734             btn.on("mouseout", this.onMouseOut, this);
30735             btn.on("mousedown", this.onMouseDown, this);
30736             btn.on("mouseup", this.onMouseUp, this);
30737         }
30738         btn.on(this.clickEvent, this.onClick, this);
30739         if(this.tooltip){
30740             if(typeof this.tooltip == 'object'){
30741                 Roo.QuickTips.tips(Roo.apply({
30742                       target: btnEl.id
30743                 }, this.tooltip));
30744             } else {
30745                 btnEl.dom[this.tooltipType] = this.tooltip;
30746             }
30747         }
30748         if(this.arrowTooltip){
30749             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30750         }
30751         if(this.hidden){
30752             this.hide();
30753         }
30754         if(this.disabled){
30755             this.disable();
30756         }
30757         if(this.pressed){
30758             this.el.addClass("x-btn-pressed");
30759         }
30760         if(Roo.isIE && !Roo.isIE7){
30761             this.autoWidth.defer(1, this);
30762         }else{
30763             this.autoWidth();
30764         }
30765         if(this.menu){
30766             this.menu.on("show", this.onMenuShow, this);
30767             this.menu.on("hide", this.onMenuHide, this);
30768         }
30769         this.fireEvent('render', this);
30770     },
30771
30772     // private
30773     autoWidth : function(){
30774         if(this.el){
30775             var tbl = this.el.child("table:first");
30776             var tbl2 = this.el.child("table:last");
30777             this.el.setWidth("auto");
30778             tbl.setWidth("auto");
30779             if(Roo.isIE7 && Roo.isStrict){
30780                 var ib = this.el.child('button:first');
30781                 if(ib && ib.getWidth() > 20){
30782                     ib.clip();
30783                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30784                 }
30785             }
30786             if(this.minWidth){
30787                 if(this.hidden){
30788                     this.el.beginMeasure();
30789                 }
30790                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30791                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30792                 }
30793                 if(this.hidden){
30794                     this.el.endMeasure();
30795                 }
30796             }
30797             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30798         } 
30799     },
30800     /**
30801      * Sets this button's click handler
30802      * @param {Function} handler The function to call when the button is clicked
30803      * @param {Object} scope (optional) Scope for the function passed above
30804      */
30805     setHandler : function(handler, scope){
30806         this.handler = handler;
30807         this.scope = scope;  
30808     },
30809     
30810     /**
30811      * Sets this button's arrow click handler
30812      * @param {Function} handler The function to call when the arrow is clicked
30813      * @param {Object} scope (optional) Scope for the function passed above
30814      */
30815     setArrowHandler : function(handler, scope){
30816         this.arrowHandler = handler;
30817         this.scope = scope;  
30818     },
30819     
30820     /**
30821      * Focus the button
30822      */
30823     focus : function(){
30824         if(this.el){
30825             this.el.child("button:first").focus();
30826         }
30827     },
30828
30829     // private
30830     onClick : function(e){
30831         e.preventDefault();
30832         if(!this.disabled){
30833             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30834                 if(this.menu && !this.menu.isVisible()){
30835                     this.menu.show(this.el, this.menuAlign);
30836                 }
30837                 this.fireEvent("arrowclick", this, e);
30838                 if(this.arrowHandler){
30839                     this.arrowHandler.call(this.scope || this, this, e);
30840                 }
30841             }else{
30842                 this.fireEvent("click", this, e);
30843                 if(this.handler){
30844                     this.handler.call(this.scope || this, this, e);
30845                 }
30846             }
30847         }
30848     },
30849     // private
30850     onMouseDown : function(e){
30851         if(!this.disabled){
30852             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30853         }
30854     },
30855     // private
30856     onMouseUp : function(e){
30857         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30858     }   
30859 });
30860
30861
30862 // backwards compat
30863 Roo.MenuButton = Roo.SplitButton;/*
30864  * Based on:
30865  * Ext JS Library 1.1.1
30866  * Copyright(c) 2006-2007, Ext JS, LLC.
30867  *
30868  * Originally Released Under LGPL - original licence link has changed is not relivant.
30869  *
30870  * Fork - LGPL
30871  * <script type="text/javascript">
30872  */
30873
30874 /**
30875  * @class Roo.Toolbar
30876  * @children   Roo.Toolbar.Item Roo.form.Field
30877  * Basic Toolbar class.
30878  * @constructor
30879  * Creates a new Toolbar
30880  * @param {Object} container The config object
30881  */ 
30882 Roo.Toolbar = function(container, buttons, config)
30883 {
30884     /// old consturctor format still supported..
30885     if(container instanceof Array){ // omit the container for later rendering
30886         buttons = container;
30887         config = buttons;
30888         container = null;
30889     }
30890     if (typeof(container) == 'object' && container.xtype) {
30891         config = container;
30892         container = config.container;
30893         buttons = config.buttons || []; // not really - use items!!
30894     }
30895     var xitems = [];
30896     if (config && config.items) {
30897         xitems = config.items;
30898         delete config.items;
30899     }
30900     Roo.apply(this, config);
30901     this.buttons = buttons;
30902     
30903     if(container){
30904         this.render(container);
30905     }
30906     this.xitems = xitems;
30907     Roo.each(xitems, function(b) {
30908         this.add(b);
30909     }, this);
30910     
30911 };
30912
30913 Roo.Toolbar.prototype = {
30914     /**
30915      * @cfg {Array} items
30916      * array of button configs or elements to add (will be converted to a MixedCollection)
30917      */
30918     items: false,
30919     /**
30920      * @cfg {String/HTMLElement/Element} container
30921      * The id or element that will contain the toolbar
30922      */
30923     // private
30924     render : function(ct){
30925         this.el = Roo.get(ct);
30926         if(this.cls){
30927             this.el.addClass(this.cls);
30928         }
30929         // using a table allows for vertical alignment
30930         // 100% width is needed by Safari...
30931         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30932         this.tr = this.el.child("tr", true);
30933         var autoId = 0;
30934         this.items = new Roo.util.MixedCollection(false, function(o){
30935             return o.id || ("item" + (++autoId));
30936         });
30937         if(this.buttons){
30938             this.add.apply(this, this.buttons);
30939             delete this.buttons;
30940         }
30941     },
30942
30943     /**
30944      * Adds element(s) to the toolbar -- this function takes a variable number of 
30945      * arguments of mixed type and adds them to the toolbar.
30946      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30947      * <ul>
30948      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30949      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30950      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30951      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30952      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30953      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30954      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30955      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30956      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30957      * </ul>
30958      * @param {Mixed} arg2
30959      * @param {Mixed} etc.
30960      */
30961     add : function(){
30962         var a = arguments, l = a.length;
30963         for(var i = 0; i < l; i++){
30964             this._add(a[i]);
30965         }
30966     },
30967     // private..
30968     _add : function(el) {
30969         
30970         if (el.xtype) {
30971             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30972         }
30973         
30974         if (el.applyTo){ // some kind of form field
30975             return this.addField(el);
30976         } 
30977         if (el.render){ // some kind of Toolbar.Item
30978             return this.addItem(el);
30979         }
30980         if (typeof el == "string"){ // string
30981             if(el == "separator" || el == "-"){
30982                 return this.addSeparator();
30983             }
30984             if (el == " "){
30985                 return this.addSpacer();
30986             }
30987             if(el == "->"){
30988                 return this.addFill();
30989             }
30990             return this.addText(el);
30991             
30992         }
30993         if(el.tagName){ // element
30994             return this.addElement(el);
30995         }
30996         if(typeof el == "object"){ // must be button config?
30997             return this.addButton(el);
30998         }
30999         // and now what?!?!
31000         return false;
31001         
31002     },
31003     
31004     /**
31005      * Add an Xtype element
31006      * @param {Object} xtype Xtype Object
31007      * @return {Object} created Object
31008      */
31009     addxtype : function(e){
31010         return this.add(e);  
31011     },
31012     
31013     /**
31014      * Returns the Element for this toolbar.
31015      * @return {Roo.Element}
31016      */
31017     getEl : function(){
31018         return this.el;  
31019     },
31020     
31021     /**
31022      * Adds a separator
31023      * @return {Roo.Toolbar.Item} The separator item
31024      */
31025     addSeparator : function(){
31026         return this.addItem(new Roo.Toolbar.Separator());
31027     },
31028
31029     /**
31030      * Adds a spacer element
31031      * @return {Roo.Toolbar.Spacer} The spacer item
31032      */
31033     addSpacer : function(){
31034         return this.addItem(new Roo.Toolbar.Spacer());
31035     },
31036
31037     /**
31038      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31039      * @return {Roo.Toolbar.Fill} The fill item
31040      */
31041     addFill : function(){
31042         return this.addItem(new Roo.Toolbar.Fill());
31043     },
31044
31045     /**
31046      * Adds any standard HTML element to the toolbar
31047      * @param {String/HTMLElement/Element} el The element or id of the element to add
31048      * @return {Roo.Toolbar.Item} The element's item
31049      */
31050     addElement : function(el){
31051         return this.addItem(new Roo.Toolbar.Item(el));
31052     },
31053     /**
31054      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31055      * @type Roo.util.MixedCollection  
31056      */
31057     items : false,
31058      
31059     /**
31060      * Adds any Toolbar.Item or subclass
31061      * @param {Roo.Toolbar.Item} item
31062      * @return {Roo.Toolbar.Item} The item
31063      */
31064     addItem : function(item){
31065         var td = this.nextBlock();
31066         item.render(td);
31067         this.items.add(item);
31068         return item;
31069     },
31070     
31071     /**
31072      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31073      * @param {Object/Array} config A button config or array of configs
31074      * @return {Roo.Toolbar.Button/Array}
31075      */
31076     addButton : function(config){
31077         if(config instanceof Array){
31078             var buttons = [];
31079             for(var i = 0, len = config.length; i < len; i++) {
31080                 buttons.push(this.addButton(config[i]));
31081             }
31082             return buttons;
31083         }
31084         var b = config;
31085         if(!(config instanceof Roo.Toolbar.Button)){
31086             b = config.split ?
31087                 new Roo.Toolbar.SplitButton(config) :
31088                 new Roo.Toolbar.Button(config);
31089         }
31090         var td = this.nextBlock();
31091         b.render(td);
31092         this.items.add(b);
31093         return b;
31094     },
31095     
31096     /**
31097      * Adds text to the toolbar
31098      * @param {String} text The text to add
31099      * @return {Roo.Toolbar.Item} The element's item
31100      */
31101     addText : function(text){
31102         return this.addItem(new Roo.Toolbar.TextItem(text));
31103     },
31104     
31105     /**
31106      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31107      * @param {Number} index The index where the item is to be inserted
31108      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31109      * @return {Roo.Toolbar.Button/Item}
31110      */
31111     insertButton : function(index, item){
31112         if(item instanceof Array){
31113             var buttons = [];
31114             for(var i = 0, len = item.length; i < len; i++) {
31115                buttons.push(this.insertButton(index + i, item[i]));
31116             }
31117             return buttons;
31118         }
31119         if (!(item instanceof Roo.Toolbar.Button)){
31120            item = new Roo.Toolbar.Button(item);
31121         }
31122         var td = document.createElement("td");
31123         this.tr.insertBefore(td, this.tr.childNodes[index]);
31124         item.render(td);
31125         this.items.insert(index, item);
31126         return item;
31127     },
31128     
31129     /**
31130      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31131      * @param {Object} config
31132      * @return {Roo.Toolbar.Item} The element's item
31133      */
31134     addDom : function(config, returnEl){
31135         var td = this.nextBlock();
31136         Roo.DomHelper.overwrite(td, config);
31137         var ti = new Roo.Toolbar.Item(td.firstChild);
31138         ti.render(td);
31139         this.items.add(ti);
31140         return ti;
31141     },
31142
31143     /**
31144      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31145      * @type Roo.util.MixedCollection  
31146      */
31147     fields : false,
31148     
31149     /**
31150      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31151      * Note: the field should not have been rendered yet. For a field that has already been
31152      * rendered, use {@link #addElement}.
31153      * @param {Roo.form.Field} field
31154      * @return {Roo.ToolbarItem}
31155      */
31156      
31157       
31158     addField : function(field) {
31159         if (!this.fields) {
31160             var autoId = 0;
31161             this.fields = new Roo.util.MixedCollection(false, function(o){
31162                 return o.id || ("item" + (++autoId));
31163             });
31164
31165         }
31166         
31167         var td = this.nextBlock();
31168         field.render(td);
31169         var ti = new Roo.Toolbar.Item(td.firstChild);
31170         ti.render(td);
31171         this.items.add(ti);
31172         this.fields.add(field);
31173         return ti;
31174     },
31175     /**
31176      * Hide the toolbar
31177      * @method hide
31178      */
31179      
31180       
31181     hide : function()
31182     {
31183         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31184         this.el.child('div').hide();
31185     },
31186     /**
31187      * Show the toolbar
31188      * @method show
31189      */
31190     show : function()
31191     {
31192         this.el.child('div').show();
31193     },
31194       
31195     // private
31196     nextBlock : function(){
31197         var td = document.createElement("td");
31198         this.tr.appendChild(td);
31199         return td;
31200     },
31201
31202     // private
31203     destroy : function(){
31204         if(this.items){ // rendered?
31205             Roo.destroy.apply(Roo, this.items.items);
31206         }
31207         if(this.fields){ // rendered?
31208             Roo.destroy.apply(Roo, this.fields.items);
31209         }
31210         Roo.Element.uncache(this.el, this.tr);
31211     }
31212 };
31213
31214 /**
31215  * @class Roo.Toolbar.Item
31216  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31217  * @constructor
31218  * Creates a new Item
31219  * @param {HTMLElement} el 
31220  */
31221 Roo.Toolbar.Item = function(el){
31222     var cfg = {};
31223     if (typeof (el.xtype) != 'undefined') {
31224         cfg = el;
31225         el = cfg.el;
31226     }
31227     
31228     this.el = Roo.getDom(el);
31229     this.id = Roo.id(this.el);
31230     this.hidden = false;
31231     
31232     this.addEvents({
31233          /**
31234              * @event render
31235              * Fires when the button is rendered
31236              * @param {Button} this
31237              */
31238         'render': true
31239     });
31240     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31241 };
31242 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31243 //Roo.Toolbar.Item.prototype = {
31244     
31245     /**
31246      * Get this item's HTML Element
31247      * @return {HTMLElement}
31248      */
31249     getEl : function(){
31250        return this.el;  
31251     },
31252
31253     // private
31254     render : function(td){
31255         
31256          this.td = td;
31257         td.appendChild(this.el);
31258         
31259         this.fireEvent('render', this);
31260     },
31261     
31262     /**
31263      * Removes and destroys this item.
31264      */
31265     destroy : function(){
31266         this.td.parentNode.removeChild(this.td);
31267     },
31268     
31269     /**
31270      * Shows this item.
31271      */
31272     show: function(){
31273         this.hidden = false;
31274         this.td.style.display = "";
31275     },
31276     
31277     /**
31278      * Hides this item.
31279      */
31280     hide: function(){
31281         this.hidden = true;
31282         this.td.style.display = "none";
31283     },
31284     
31285     /**
31286      * Convenience function for boolean show/hide.
31287      * @param {Boolean} visible true to show/false to hide
31288      */
31289     setVisible: function(visible){
31290         if(visible) {
31291             this.show();
31292         }else{
31293             this.hide();
31294         }
31295     },
31296     
31297     /**
31298      * Try to focus this item.
31299      */
31300     focus : function(){
31301         Roo.fly(this.el).focus();
31302     },
31303     
31304     /**
31305      * Disables this item.
31306      */
31307     disable : function(){
31308         Roo.fly(this.td).addClass("x-item-disabled");
31309         this.disabled = true;
31310         this.el.disabled = true;
31311     },
31312     
31313     /**
31314      * Enables this item.
31315      */
31316     enable : function(){
31317         Roo.fly(this.td).removeClass("x-item-disabled");
31318         this.disabled = false;
31319         this.el.disabled = false;
31320     }
31321 });
31322
31323
31324 /**
31325  * @class Roo.Toolbar.Separator
31326  * @extends Roo.Toolbar.Item
31327  * A simple toolbar separator class
31328  * @constructor
31329  * Creates a new Separator
31330  */
31331 Roo.Toolbar.Separator = function(cfg){
31332     
31333     var s = document.createElement("span");
31334     s.className = "ytb-sep";
31335     if (cfg) {
31336         cfg.el = s;
31337     }
31338     
31339     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31340 };
31341 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31342     enable:Roo.emptyFn,
31343     disable:Roo.emptyFn,
31344     focus:Roo.emptyFn
31345 });
31346
31347 /**
31348  * @class Roo.Toolbar.Spacer
31349  * @extends Roo.Toolbar.Item
31350  * A simple element that adds extra horizontal space to a toolbar.
31351  * @constructor
31352  * Creates a new Spacer
31353  */
31354 Roo.Toolbar.Spacer = function(cfg){
31355     var s = document.createElement("div");
31356     s.className = "ytb-spacer";
31357     if (cfg) {
31358         cfg.el = s;
31359     }
31360     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31361 };
31362 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31363     enable:Roo.emptyFn,
31364     disable:Roo.emptyFn,
31365     focus:Roo.emptyFn
31366 });
31367
31368 /**
31369  * @class Roo.Toolbar.Fill
31370  * @extends Roo.Toolbar.Spacer
31371  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31372  * @constructor
31373  * Creates a new Spacer
31374  */
31375 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31376     // private
31377     render : function(td){
31378         td.style.width = '100%';
31379         Roo.Toolbar.Fill.superclass.render.call(this, td);
31380     }
31381 });
31382
31383 /**
31384  * @class Roo.Toolbar.TextItem
31385  * @extends Roo.Toolbar.Item
31386  * A simple class that renders text directly into a toolbar.
31387  * @constructor
31388  * Creates a new TextItem
31389  * @cfg {string} text 
31390  */
31391 Roo.Toolbar.TextItem = function(cfg){
31392     var  text = cfg || "";
31393     if (typeof(cfg) == 'object') {
31394         text = cfg.text || "";
31395     }  else {
31396         cfg = null;
31397     }
31398     var s = document.createElement("span");
31399     s.className = "ytb-text";
31400     s.innerHTML = text;
31401     if (cfg) {
31402         cfg.el  = s;
31403     }
31404     
31405     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31406 };
31407 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31408     
31409      
31410     enable:Roo.emptyFn,
31411     disable:Roo.emptyFn,
31412     focus:Roo.emptyFn
31413 });
31414
31415 /**
31416  * @class Roo.Toolbar.Button
31417  * @extends Roo.Button
31418  * A button that renders into a toolbar.
31419  * @constructor
31420  * Creates a new Button
31421  * @param {Object} config A standard {@link Roo.Button} config object
31422  */
31423 Roo.Toolbar.Button = function(config){
31424     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31425 };
31426 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31427 {
31428     
31429     
31430     render : function(td){
31431         this.td = td;
31432         Roo.Toolbar.Button.superclass.render.call(this, td);
31433     },
31434     
31435     /**
31436      * Removes and destroys this button
31437      */
31438     destroy : function(){
31439         Roo.Toolbar.Button.superclass.destroy.call(this);
31440         this.td.parentNode.removeChild(this.td);
31441     },
31442     
31443     /**
31444      * Shows this button
31445      */
31446     show: function(){
31447         this.hidden = false;
31448         this.td.style.display = "";
31449     },
31450     
31451     /**
31452      * Hides this button
31453      */
31454     hide: function(){
31455         this.hidden = true;
31456         this.td.style.display = "none";
31457     },
31458
31459     /**
31460      * Disables this item
31461      */
31462     disable : function(){
31463         Roo.fly(this.td).addClass("x-item-disabled");
31464         this.disabled = true;
31465     },
31466
31467     /**
31468      * Enables this item
31469      */
31470     enable : function(){
31471         Roo.fly(this.td).removeClass("x-item-disabled");
31472         this.disabled = false;
31473     }
31474 });
31475 // backwards compat
31476 Roo.ToolbarButton = Roo.Toolbar.Button;
31477
31478 /**
31479  * @class Roo.Toolbar.SplitButton
31480  * @extends Roo.SplitButton
31481  * A menu button that renders into a toolbar.
31482  * @constructor
31483  * Creates a new SplitButton
31484  * @param {Object} config A standard {@link Roo.SplitButton} config object
31485  */
31486 Roo.Toolbar.SplitButton = function(config){
31487     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31488 };
31489 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31490     render : function(td){
31491         this.td = td;
31492         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31493     },
31494     
31495     /**
31496      * Removes and destroys this button
31497      */
31498     destroy : function(){
31499         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31500         this.td.parentNode.removeChild(this.td);
31501     },
31502     
31503     /**
31504      * Shows this button
31505      */
31506     show: function(){
31507         this.hidden = false;
31508         this.td.style.display = "";
31509     },
31510     
31511     /**
31512      * Hides this button
31513      */
31514     hide: function(){
31515         this.hidden = true;
31516         this.td.style.display = "none";
31517     }
31518 });
31519
31520 // backwards compat
31521 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31522  * Based on:
31523  * Ext JS Library 1.1.1
31524  * Copyright(c) 2006-2007, Ext JS, LLC.
31525  *
31526  * Originally Released Under LGPL - original licence link has changed is not relivant.
31527  *
31528  * Fork - LGPL
31529  * <script type="text/javascript">
31530  */
31531  
31532 /**
31533  * @class Roo.PagingToolbar
31534  * @extends Roo.Toolbar
31535  * @children   Roo.Toolbar.Item Roo.form.Field
31536  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31537  * @constructor
31538  * Create a new PagingToolbar
31539  * @param {Object} config The config object
31540  */
31541 Roo.PagingToolbar = function(el, ds, config)
31542 {
31543     // old args format still supported... - xtype is prefered..
31544     if (typeof(el) == 'object' && el.xtype) {
31545         // created from xtype...
31546         config = el;
31547         ds = el.dataSource;
31548         el = config.container;
31549     }
31550     var items = [];
31551     if (config.items) {
31552         items = config.items;
31553         config.items = [];
31554     }
31555     
31556     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31557     this.ds = ds;
31558     this.cursor = 0;
31559     this.renderButtons(this.el);
31560     this.bind(ds);
31561     
31562     // supprot items array.
31563    
31564     Roo.each(items, function(e) {
31565         this.add(Roo.factory(e));
31566     },this);
31567     
31568 };
31569
31570 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31571    
31572     /**
31573      * @cfg {String/HTMLElement/Element} container
31574      * container The id or element that will contain the toolbar
31575      */
31576     /**
31577      * @cfg {Boolean} displayInfo
31578      * True to display the displayMsg (defaults to false)
31579      */
31580     
31581     
31582     /**
31583      * @cfg {Number} pageSize
31584      * The number of records to display per page (defaults to 20)
31585      */
31586     pageSize: 20,
31587     /**
31588      * @cfg {String} displayMsg
31589      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31590      */
31591     displayMsg : 'Displaying {0} - {1} of {2}',
31592     /**
31593      * @cfg {String} emptyMsg
31594      * The message to display when no records are found (defaults to "No data to display")
31595      */
31596     emptyMsg : 'No data to display',
31597     /**
31598      * Customizable piece of the default paging text (defaults to "Page")
31599      * @type String
31600      */
31601     beforePageText : "Page",
31602     /**
31603      * Customizable piece of the default paging text (defaults to "of %0")
31604      * @type String
31605      */
31606     afterPageText : "of {0}",
31607     /**
31608      * Customizable piece of the default paging text (defaults to "First Page")
31609      * @type String
31610      */
31611     firstText : "First Page",
31612     /**
31613      * Customizable piece of the default paging text (defaults to "Previous Page")
31614      * @type String
31615      */
31616     prevText : "Previous Page",
31617     /**
31618      * Customizable piece of the default paging text (defaults to "Next Page")
31619      * @type String
31620      */
31621     nextText : "Next Page",
31622     /**
31623      * Customizable piece of the default paging text (defaults to "Last Page")
31624      * @type String
31625      */
31626     lastText : "Last Page",
31627     /**
31628      * Customizable piece of the default paging text (defaults to "Refresh")
31629      * @type String
31630      */
31631     refreshText : "Refresh",
31632
31633     // private
31634     renderButtons : function(el){
31635         Roo.PagingToolbar.superclass.render.call(this, el);
31636         this.first = this.addButton({
31637             tooltip: this.firstText,
31638             cls: "x-btn-icon x-grid-page-first",
31639             disabled: true,
31640             handler: this.onClick.createDelegate(this, ["first"])
31641         });
31642         this.prev = this.addButton({
31643             tooltip: this.prevText,
31644             cls: "x-btn-icon x-grid-page-prev",
31645             disabled: true,
31646             handler: this.onClick.createDelegate(this, ["prev"])
31647         });
31648         //this.addSeparator();
31649         this.add(this.beforePageText);
31650         this.field = Roo.get(this.addDom({
31651            tag: "input",
31652            type: "text",
31653            size: "3",
31654            value: "1",
31655            cls: "x-grid-page-number"
31656         }).el);
31657         this.field.on("keydown", this.onPagingKeydown, this);
31658         this.field.on("focus", function(){this.dom.select();});
31659         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31660         this.field.setHeight(18);
31661         //this.addSeparator();
31662         this.next = this.addButton({
31663             tooltip: this.nextText,
31664             cls: "x-btn-icon x-grid-page-next",
31665             disabled: true,
31666             handler: this.onClick.createDelegate(this, ["next"])
31667         });
31668         this.last = this.addButton({
31669             tooltip: this.lastText,
31670             cls: "x-btn-icon x-grid-page-last",
31671             disabled: true,
31672             handler: this.onClick.createDelegate(this, ["last"])
31673         });
31674         //this.addSeparator();
31675         this.loading = this.addButton({
31676             tooltip: this.refreshText,
31677             cls: "x-btn-icon x-grid-loading",
31678             handler: this.onClick.createDelegate(this, ["refresh"])
31679         });
31680
31681         if(this.displayInfo){
31682             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31683         }
31684     },
31685
31686     // private
31687     updateInfo : function(){
31688         if(this.displayEl){
31689             var count = this.ds.getCount();
31690             var msg = count == 0 ?
31691                 this.emptyMsg :
31692                 String.format(
31693                     this.displayMsg,
31694                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31695                 );
31696             this.displayEl.update(msg);
31697         }
31698     },
31699
31700     // private
31701     onLoad : function(ds, r, o){
31702        this.cursor = o.params ? o.params.start : 0;
31703        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31704
31705        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31706        this.field.dom.value = ap;
31707        this.first.setDisabled(ap == 1);
31708        this.prev.setDisabled(ap == 1);
31709        this.next.setDisabled(ap == ps);
31710        this.last.setDisabled(ap == ps);
31711        this.loading.enable();
31712        this.updateInfo();
31713     },
31714
31715     // private
31716     getPageData : function(){
31717         var total = this.ds.getTotalCount();
31718         return {
31719             total : total,
31720             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31721             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31722         };
31723     },
31724
31725     // private
31726     onLoadError : function(){
31727         this.loading.enable();
31728     },
31729
31730     // private
31731     onPagingKeydown : function(e){
31732         var k = e.getKey();
31733         var d = this.getPageData();
31734         if(k == e.RETURN){
31735             var v = this.field.dom.value, pageNum;
31736             if(!v || isNaN(pageNum = parseInt(v, 10))){
31737                 this.field.dom.value = d.activePage;
31738                 return;
31739             }
31740             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31741             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31742             e.stopEvent();
31743         }
31744         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))
31745         {
31746           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31747           this.field.dom.value = pageNum;
31748           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31749           e.stopEvent();
31750         }
31751         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31752         {
31753           var v = this.field.dom.value, pageNum; 
31754           var increment = (e.shiftKey) ? 10 : 1;
31755           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31756             increment *= -1;
31757           }
31758           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31759             this.field.dom.value = d.activePage;
31760             return;
31761           }
31762           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31763           {
31764             this.field.dom.value = parseInt(v, 10) + increment;
31765             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31766             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31767           }
31768           e.stopEvent();
31769         }
31770     },
31771
31772     // private
31773     beforeLoad : function(){
31774         if(this.loading){
31775             this.loading.disable();
31776         }
31777     },
31778
31779     // private
31780     onClick : function(which){
31781         var ds = this.ds;
31782         switch(which){
31783             case "first":
31784                 ds.load({params:{start: 0, limit: this.pageSize}});
31785             break;
31786             case "prev":
31787                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31788             break;
31789             case "next":
31790                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31791             break;
31792             case "last":
31793                 var total = ds.getTotalCount();
31794                 var extra = total % this.pageSize;
31795                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31796                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31797             break;
31798             case "refresh":
31799                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31800             break;
31801         }
31802     },
31803
31804     /**
31805      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31806      * @param {Roo.data.Store} store The data store to unbind
31807      */
31808     unbind : function(ds){
31809         ds.un("beforeload", this.beforeLoad, this);
31810         ds.un("load", this.onLoad, this);
31811         ds.un("loadexception", this.onLoadError, this);
31812         ds.un("remove", this.updateInfo, this);
31813         ds.un("add", this.updateInfo, this);
31814         this.ds = undefined;
31815     },
31816
31817     /**
31818      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31819      * @param {Roo.data.Store} store The data store to bind
31820      */
31821     bind : function(ds){
31822         ds.on("beforeload", this.beforeLoad, this);
31823         ds.on("load", this.onLoad, this);
31824         ds.on("loadexception", this.onLoadError, this);
31825         ds.on("remove", this.updateInfo, this);
31826         ds.on("add", this.updateInfo, this);
31827         this.ds = ds;
31828     }
31829 });/*
31830  * Based on:
31831  * Ext JS Library 1.1.1
31832  * Copyright(c) 2006-2007, Ext JS, LLC.
31833  *
31834  * Originally Released Under LGPL - original licence link has changed is not relivant.
31835  *
31836  * Fork - LGPL
31837  * <script type="text/javascript">
31838  */
31839
31840 /**
31841  * @class Roo.Resizable
31842  * @extends Roo.util.Observable
31843  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31844  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31845  * 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
31846  * the element will be wrapped for you automatically.</p>
31847  * <p>Here is the list of valid resize handles:</p>
31848  * <pre>
31849 Value   Description
31850 ------  -------------------
31851  'n'     north
31852  's'     south
31853  'e'     east
31854  'w'     west
31855  'nw'    northwest
31856  'sw'    southwest
31857  'se'    southeast
31858  'ne'    northeast
31859  'hd'    horizontal drag
31860  'all'   all
31861 </pre>
31862  * <p>Here's an example showing the creation of a typical Resizable:</p>
31863  * <pre><code>
31864 var resizer = new Roo.Resizable("element-id", {
31865     handles: 'all',
31866     minWidth: 200,
31867     minHeight: 100,
31868     maxWidth: 500,
31869     maxHeight: 400,
31870     pinned: true
31871 });
31872 resizer.on("resize", myHandler);
31873 </code></pre>
31874  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31875  * resizer.east.setDisplayed(false);</p>
31876  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31877  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31878  * resize operation's new size (defaults to [0, 0])
31879  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31880  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31881  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31882  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31883  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31884  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31885  * @cfg {Number} width The width of the element in pixels (defaults to null)
31886  * @cfg {Number} height The height of the element in pixels (defaults to null)
31887  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31888  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31889  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31890  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31891  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31892  * in favor of the handles config option (defaults to false)
31893  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31894  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31895  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31896  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31897  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31898  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31899  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31900  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31901  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31902  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31903  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31904  * @constructor
31905  * Create a new resizable component
31906  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31907  * @param {Object} config configuration options
31908   */
31909 Roo.Resizable = function(el, config)
31910 {
31911     this.el = Roo.get(el);
31912
31913     if(config && config.wrap){
31914         config.resizeChild = this.el;
31915         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31916         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31917         this.el.setStyle("overflow", "hidden");
31918         this.el.setPositioning(config.resizeChild.getPositioning());
31919         config.resizeChild.clearPositioning();
31920         if(!config.width || !config.height){
31921             var csize = config.resizeChild.getSize();
31922             this.el.setSize(csize.width, csize.height);
31923         }
31924         if(config.pinned && !config.adjustments){
31925             config.adjustments = "auto";
31926         }
31927     }
31928
31929     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31930     this.proxy.unselectable();
31931     this.proxy.enableDisplayMode('block');
31932
31933     Roo.apply(this, config);
31934
31935     if(this.pinned){
31936         this.disableTrackOver = true;
31937         this.el.addClass("x-resizable-pinned");
31938     }
31939     // if the element isn't positioned, make it relative
31940     var position = this.el.getStyle("position");
31941     if(position != "absolute" && position != "fixed"){
31942         this.el.setStyle("position", "relative");
31943     }
31944     if(!this.handles){ // no handles passed, must be legacy style
31945         this.handles = 's,e,se';
31946         if(this.multiDirectional){
31947             this.handles += ',n,w';
31948         }
31949     }
31950     if(this.handles == "all"){
31951         this.handles = "n s e w ne nw se sw";
31952     }
31953     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31954     var ps = Roo.Resizable.positions;
31955     for(var i = 0, len = hs.length; i < len; i++){
31956         if(hs[i] && ps[hs[i]]){
31957             var pos = ps[hs[i]];
31958             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31959         }
31960     }
31961     // legacy
31962     this.corner = this.southeast;
31963     
31964     // updateBox = the box can move..
31965     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31966         this.updateBox = true;
31967     }
31968
31969     this.activeHandle = null;
31970
31971     if(this.resizeChild){
31972         if(typeof this.resizeChild == "boolean"){
31973             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31974         }else{
31975             this.resizeChild = Roo.get(this.resizeChild, true);
31976         }
31977     }
31978     
31979     if(this.adjustments == "auto"){
31980         var rc = this.resizeChild;
31981         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31982         if(rc && (hw || hn)){
31983             rc.position("relative");
31984             rc.setLeft(hw ? hw.el.getWidth() : 0);
31985             rc.setTop(hn ? hn.el.getHeight() : 0);
31986         }
31987         this.adjustments = [
31988             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31989             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31990         ];
31991     }
31992
31993     if(this.draggable){
31994         this.dd = this.dynamic ?
31995             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31996         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31997     }
31998
31999     // public events
32000     this.addEvents({
32001         /**
32002          * @event beforeresize
32003          * Fired before resize is allowed. Set enabled to false to cancel resize.
32004          * @param {Roo.Resizable} this
32005          * @param {Roo.EventObject} e The mousedown event
32006          */
32007         "beforeresize" : true,
32008         /**
32009          * @event resizing
32010          * Fired a resizing.
32011          * @param {Roo.Resizable} this
32012          * @param {Number} x The new x position
32013          * @param {Number} y The new y position
32014          * @param {Number} w The new w width
32015          * @param {Number} h The new h hight
32016          * @param {Roo.EventObject} e The mouseup event
32017          */
32018         "resizing" : true,
32019         /**
32020          * @event resize
32021          * Fired after a resize.
32022          * @param {Roo.Resizable} this
32023          * @param {Number} width The new width
32024          * @param {Number} height The new height
32025          * @param {Roo.EventObject} e The mouseup event
32026          */
32027         "resize" : true
32028     });
32029
32030     if(this.width !== null && this.height !== null){
32031         this.resizeTo(this.width, this.height);
32032     }else{
32033         this.updateChildSize();
32034     }
32035     if(Roo.isIE){
32036         this.el.dom.style.zoom = 1;
32037     }
32038     Roo.Resizable.superclass.constructor.call(this);
32039 };
32040
32041 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32042         resizeChild : false,
32043         adjustments : [0, 0],
32044         minWidth : 5,
32045         minHeight : 5,
32046         maxWidth : 10000,
32047         maxHeight : 10000,
32048         enabled : true,
32049         animate : false,
32050         duration : .35,
32051         dynamic : false,
32052         handles : false,
32053         multiDirectional : false,
32054         disableTrackOver : false,
32055         easing : 'easeOutStrong',
32056         widthIncrement : 0,
32057         heightIncrement : 0,
32058         pinned : false,
32059         width : null,
32060         height : null,
32061         preserveRatio : false,
32062         transparent: false,
32063         minX: 0,
32064         minY: 0,
32065         draggable: false,
32066
32067         /**
32068          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32069          */
32070         constrainTo: undefined,
32071         /**
32072          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32073          */
32074         resizeRegion: undefined,
32075
32076
32077     /**
32078      * Perform a manual resize
32079      * @param {Number} width
32080      * @param {Number} height
32081      */
32082     resizeTo : function(width, height){
32083         this.el.setSize(width, height);
32084         this.updateChildSize();
32085         this.fireEvent("resize", this, width, height, null);
32086     },
32087
32088     // private
32089     startSizing : function(e, handle){
32090         this.fireEvent("beforeresize", this, e);
32091         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32092
32093             if(!this.overlay){
32094                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32095                 this.overlay.unselectable();
32096                 this.overlay.enableDisplayMode("block");
32097                 this.overlay.on("mousemove", this.onMouseMove, this);
32098                 this.overlay.on("mouseup", this.onMouseUp, this);
32099             }
32100             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32101
32102             this.resizing = true;
32103             this.startBox = this.el.getBox();
32104             this.startPoint = e.getXY();
32105             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32106                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32107
32108             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32109             this.overlay.show();
32110
32111             if(this.constrainTo) {
32112                 var ct = Roo.get(this.constrainTo);
32113                 this.resizeRegion = ct.getRegion().adjust(
32114                     ct.getFrameWidth('t'),
32115                     ct.getFrameWidth('l'),
32116                     -ct.getFrameWidth('b'),
32117                     -ct.getFrameWidth('r')
32118                 );
32119             }
32120
32121             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32122             this.proxy.show();
32123             this.proxy.setBox(this.startBox);
32124             if(!this.dynamic){
32125                 this.proxy.setStyle('visibility', 'visible');
32126             }
32127         }
32128     },
32129
32130     // private
32131     onMouseDown : function(handle, e){
32132         if(this.enabled){
32133             e.stopEvent();
32134             this.activeHandle = handle;
32135             this.startSizing(e, handle);
32136         }
32137     },
32138
32139     // private
32140     onMouseUp : function(e){
32141         var size = this.resizeElement();
32142         this.resizing = false;
32143         this.handleOut();
32144         this.overlay.hide();
32145         this.proxy.hide();
32146         this.fireEvent("resize", this, size.width, size.height, e);
32147     },
32148
32149     // private
32150     updateChildSize : function(){
32151         
32152         if(this.resizeChild){
32153             var el = this.el;
32154             var child = this.resizeChild;
32155             var adj = this.adjustments;
32156             if(el.dom.offsetWidth){
32157                 var b = el.getSize(true);
32158                 child.setSize(b.width+adj[0], b.height+adj[1]);
32159             }
32160             // Second call here for IE
32161             // The first call enables instant resizing and
32162             // the second call corrects scroll bars if they
32163             // exist
32164             if(Roo.isIE){
32165                 setTimeout(function(){
32166                     if(el.dom.offsetWidth){
32167                         var b = el.getSize(true);
32168                         child.setSize(b.width+adj[0], b.height+adj[1]);
32169                     }
32170                 }, 10);
32171             }
32172         }
32173     },
32174
32175     // private
32176     snap : function(value, inc, min){
32177         if(!inc || !value) {
32178             return value;
32179         }
32180         var newValue = value;
32181         var m = value % inc;
32182         if(m > 0){
32183             if(m > (inc/2)){
32184                 newValue = value + (inc-m);
32185             }else{
32186                 newValue = value - m;
32187             }
32188         }
32189         return Math.max(min, newValue);
32190     },
32191
32192     // private
32193     resizeElement : function(){
32194         var box = this.proxy.getBox();
32195         if(this.updateBox){
32196             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32197         }else{
32198             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32199         }
32200         this.updateChildSize();
32201         if(!this.dynamic){
32202             this.proxy.hide();
32203         }
32204         return box;
32205     },
32206
32207     // private
32208     constrain : function(v, diff, m, mx){
32209         if(v - diff < m){
32210             diff = v - m;
32211         }else if(v - diff > mx){
32212             diff = mx - v;
32213         }
32214         return diff;
32215     },
32216
32217     // private
32218     onMouseMove : function(e){
32219         
32220         if(this.enabled){
32221             try{// try catch so if something goes wrong the user doesn't get hung
32222
32223             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32224                 return;
32225             }
32226
32227             //var curXY = this.startPoint;
32228             var curSize = this.curSize || this.startBox;
32229             var x = this.startBox.x, y = this.startBox.y;
32230             var ox = x, oy = y;
32231             var w = curSize.width, h = curSize.height;
32232             var ow = w, oh = h;
32233             var mw = this.minWidth, mh = this.minHeight;
32234             var mxw = this.maxWidth, mxh = this.maxHeight;
32235             var wi = this.widthIncrement;
32236             var hi = this.heightIncrement;
32237
32238             var eventXY = e.getXY();
32239             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32240             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32241
32242             var pos = this.activeHandle.position;
32243
32244             switch(pos){
32245                 case "east":
32246                     w += diffX;
32247                     w = Math.min(Math.max(mw, w), mxw);
32248                     break;
32249              
32250                 case "south":
32251                     h += diffY;
32252                     h = Math.min(Math.max(mh, h), mxh);
32253                     break;
32254                 case "southeast":
32255                     w += diffX;
32256                     h += diffY;
32257                     w = Math.min(Math.max(mw, w), mxw);
32258                     h = Math.min(Math.max(mh, h), mxh);
32259                     break;
32260                 case "north":
32261                     diffY = this.constrain(h, diffY, mh, mxh);
32262                     y += diffY;
32263                     h -= diffY;
32264                     break;
32265                 case "hdrag":
32266                     
32267                     if (wi) {
32268                         var adiffX = Math.abs(diffX);
32269                         var sub = (adiffX % wi); // how much 
32270                         if (sub > (wi/2)) { // far enough to snap
32271                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32272                         } else {
32273                             // remove difference.. 
32274                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32275                         }
32276                     }
32277                     x += diffX;
32278                     x = Math.max(this.minX, x);
32279                     break;
32280                 case "west":
32281                     diffX = this.constrain(w, diffX, mw, mxw);
32282                     x += diffX;
32283                     w -= diffX;
32284                     break;
32285                 case "northeast":
32286                     w += diffX;
32287                     w = Math.min(Math.max(mw, w), mxw);
32288                     diffY = this.constrain(h, diffY, mh, mxh);
32289                     y += diffY;
32290                     h -= diffY;
32291                     break;
32292                 case "northwest":
32293                     diffX = this.constrain(w, diffX, mw, mxw);
32294                     diffY = this.constrain(h, diffY, mh, mxh);
32295                     y += diffY;
32296                     h -= diffY;
32297                     x += diffX;
32298                     w -= diffX;
32299                     break;
32300                case "southwest":
32301                     diffX = this.constrain(w, diffX, mw, mxw);
32302                     h += diffY;
32303                     h = Math.min(Math.max(mh, h), mxh);
32304                     x += diffX;
32305                     w -= diffX;
32306                     break;
32307             }
32308
32309             var sw = this.snap(w, wi, mw);
32310             var sh = this.snap(h, hi, mh);
32311             if(sw != w || sh != h){
32312                 switch(pos){
32313                     case "northeast":
32314                         y -= sh - h;
32315                     break;
32316                     case "north":
32317                         y -= sh - h;
32318                         break;
32319                     case "southwest":
32320                         x -= sw - w;
32321                     break;
32322                     case "west":
32323                         x -= sw - w;
32324                         break;
32325                     case "northwest":
32326                         x -= sw - w;
32327                         y -= sh - h;
32328                     break;
32329                 }
32330                 w = sw;
32331                 h = sh;
32332             }
32333
32334             if(this.preserveRatio){
32335                 switch(pos){
32336                     case "southeast":
32337                     case "east":
32338                         h = oh * (w/ow);
32339                         h = Math.min(Math.max(mh, h), mxh);
32340                         w = ow * (h/oh);
32341                        break;
32342                     case "south":
32343                         w = ow * (h/oh);
32344                         w = Math.min(Math.max(mw, w), mxw);
32345                         h = oh * (w/ow);
32346                         break;
32347                     case "northeast":
32348                         w = ow * (h/oh);
32349                         w = Math.min(Math.max(mw, w), mxw);
32350                         h = oh * (w/ow);
32351                     break;
32352                     case "north":
32353                         var tw = w;
32354                         w = ow * (h/oh);
32355                         w = Math.min(Math.max(mw, w), mxw);
32356                         h = oh * (w/ow);
32357                         x += (tw - w) / 2;
32358                         break;
32359                     case "southwest":
32360                         h = oh * (w/ow);
32361                         h = Math.min(Math.max(mh, h), mxh);
32362                         var tw = w;
32363                         w = ow * (h/oh);
32364                         x += tw - w;
32365                         break;
32366                     case "west":
32367                         var th = h;
32368                         h = oh * (w/ow);
32369                         h = Math.min(Math.max(mh, h), mxh);
32370                         y += (th - h) / 2;
32371                         var tw = w;
32372                         w = ow * (h/oh);
32373                         x += tw - w;
32374                        break;
32375                     case "northwest":
32376                         var tw = w;
32377                         var th = h;
32378                         h = oh * (w/ow);
32379                         h = Math.min(Math.max(mh, h), mxh);
32380                         w = ow * (h/oh);
32381                         y += th - h;
32382                         x += tw - w;
32383                        break;
32384
32385                 }
32386             }
32387             if (pos == 'hdrag') {
32388                 w = ow;
32389             }
32390             this.proxy.setBounds(x, y, w, h);
32391             if(this.dynamic){
32392                 this.resizeElement();
32393             }
32394             }catch(e){}
32395         }
32396         this.fireEvent("resizing", this, x, y, w, h, e);
32397     },
32398
32399     // private
32400     handleOver : function(){
32401         if(this.enabled){
32402             this.el.addClass("x-resizable-over");
32403         }
32404     },
32405
32406     // private
32407     handleOut : function(){
32408         if(!this.resizing){
32409             this.el.removeClass("x-resizable-over");
32410         }
32411     },
32412
32413     /**
32414      * Returns the element this component is bound to.
32415      * @return {Roo.Element}
32416      */
32417     getEl : function(){
32418         return this.el;
32419     },
32420
32421     /**
32422      * Returns the resizeChild element (or null).
32423      * @return {Roo.Element}
32424      */
32425     getResizeChild : function(){
32426         return this.resizeChild;
32427     },
32428     groupHandler : function()
32429     {
32430         
32431     },
32432     /**
32433      * Destroys this resizable. If the element was wrapped and
32434      * removeEl is not true then the element remains.
32435      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32436      */
32437     destroy : function(removeEl){
32438         this.proxy.remove();
32439         if(this.overlay){
32440             this.overlay.removeAllListeners();
32441             this.overlay.remove();
32442         }
32443         var ps = Roo.Resizable.positions;
32444         for(var k in ps){
32445             if(typeof ps[k] != "function" && this[ps[k]]){
32446                 var h = this[ps[k]];
32447                 h.el.removeAllListeners();
32448                 h.el.remove();
32449             }
32450         }
32451         if(removeEl){
32452             this.el.update("");
32453             this.el.remove();
32454         }
32455     }
32456 });
32457
32458 // private
32459 // hash to map config positions to true positions
32460 Roo.Resizable.positions = {
32461     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32462     hd: "hdrag"
32463 };
32464
32465 // private
32466 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32467     if(!this.tpl){
32468         // only initialize the template if resizable is used
32469         var tpl = Roo.DomHelper.createTemplate(
32470             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32471         );
32472         tpl.compile();
32473         Roo.Resizable.Handle.prototype.tpl = tpl;
32474     }
32475     this.position = pos;
32476     this.rz = rz;
32477     // show north drag fro topdra
32478     var handlepos = pos == 'hdrag' ? 'north' : pos;
32479     
32480     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32481     if (pos == 'hdrag') {
32482         this.el.setStyle('cursor', 'pointer');
32483     }
32484     this.el.unselectable();
32485     if(transparent){
32486         this.el.setOpacity(0);
32487     }
32488     this.el.on("mousedown", this.onMouseDown, this);
32489     if(!disableTrackOver){
32490         this.el.on("mouseover", this.onMouseOver, this);
32491         this.el.on("mouseout", this.onMouseOut, this);
32492     }
32493 };
32494
32495 // private
32496 Roo.Resizable.Handle.prototype = {
32497     afterResize : function(rz){
32498         Roo.log('after?');
32499         // do nothing
32500     },
32501     // private
32502     onMouseDown : function(e){
32503         this.rz.onMouseDown(this, e);
32504     },
32505     // private
32506     onMouseOver : function(e){
32507         this.rz.handleOver(this, e);
32508     },
32509     // private
32510     onMouseOut : function(e){
32511         this.rz.handleOut(this, e);
32512     }
32513 };/*
32514  * Based on:
32515  * Ext JS Library 1.1.1
32516  * Copyright(c) 2006-2007, Ext JS, LLC.
32517  *
32518  * Originally Released Under LGPL - original licence link has changed is not relivant.
32519  *
32520  * Fork - LGPL
32521  * <script type="text/javascript">
32522  */
32523
32524 /**
32525  * @class Roo.Editor
32526  * @extends Roo.Component
32527  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32528  * @constructor
32529  * Create a new Editor
32530  * @param {Roo.form.Field} field The Field object (or descendant)
32531  * @param {Object} config The config object
32532  */
32533 Roo.Editor = function(field, config){
32534     Roo.Editor.superclass.constructor.call(this, config);
32535     this.field = field;
32536     this.addEvents({
32537         /**
32538              * @event beforestartedit
32539              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32540              * false from the handler of this event.
32541              * @param {Editor} this
32542              * @param {Roo.Element} boundEl The underlying element bound to this editor
32543              * @param {Mixed} value The field value being set
32544              */
32545         "beforestartedit" : true,
32546         /**
32547              * @event startedit
32548              * Fires when this editor is displayed
32549              * @param {Roo.Element} boundEl The underlying element bound to this editor
32550              * @param {Mixed} value The starting field value
32551              */
32552         "startedit" : true,
32553         /**
32554              * @event beforecomplete
32555              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32556              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32557              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32558              * event will not fire since no edit actually occurred.
32559              * @param {Editor} this
32560              * @param {Mixed} value The current field value
32561              * @param {Mixed} startValue The original field value
32562              */
32563         "beforecomplete" : true,
32564         /**
32565              * @event complete
32566              * Fires after editing is complete and any changed value has been written to the underlying field.
32567              * @param {Editor} this
32568              * @param {Mixed} value The current field value
32569              * @param {Mixed} startValue The original field value
32570              */
32571         "complete" : true,
32572         /**
32573          * @event specialkey
32574          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32575          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32576          * @param {Roo.form.Field} this
32577          * @param {Roo.EventObject} e The event object
32578          */
32579         "specialkey" : true
32580     });
32581 };
32582
32583 Roo.extend(Roo.Editor, Roo.Component, {
32584     /**
32585      * @cfg {Boolean/String} autosize
32586      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32587      * or "height" to adopt the height only (defaults to false)
32588      */
32589     /**
32590      * @cfg {Boolean} revertInvalid
32591      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32592      * validation fails (defaults to true)
32593      */
32594     /**
32595      * @cfg {Boolean} ignoreNoChange
32596      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32597      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32598      * will never be ignored.
32599      */
32600     /**
32601      * @cfg {Boolean} hideEl
32602      * False to keep the bound element visible while the editor is displayed (defaults to true)
32603      */
32604     /**
32605      * @cfg {Mixed} value
32606      * The data value of the underlying field (defaults to "")
32607      */
32608     value : "",
32609     /**
32610      * @cfg {String} alignment
32611      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32612      */
32613     alignment: "c-c?",
32614     /**
32615      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32616      * for bottom-right shadow (defaults to "frame")
32617      */
32618     shadow : "frame",
32619     /**
32620      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32621      */
32622     constrain : false,
32623     /**
32624      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32625      */
32626     completeOnEnter : false,
32627     /**
32628      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32629      */
32630     cancelOnEsc : false,
32631     /**
32632      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32633      */
32634     updateEl : false,
32635
32636     // private
32637     onRender : function(ct, position){
32638         this.el = new Roo.Layer({
32639             shadow: this.shadow,
32640             cls: "x-editor",
32641             parentEl : ct,
32642             shim : this.shim,
32643             shadowOffset:4,
32644             id: this.id,
32645             constrain: this.constrain
32646         });
32647         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32648         if(this.field.msgTarget != 'title'){
32649             this.field.msgTarget = 'qtip';
32650         }
32651         this.field.render(this.el);
32652         if(Roo.isGecko){
32653             this.field.el.dom.setAttribute('autocomplete', 'off');
32654         }
32655         this.field.on("specialkey", this.onSpecialKey, this);
32656         if(this.swallowKeys){
32657             this.field.el.swallowEvent(['keydown','keypress']);
32658         }
32659         this.field.show();
32660         this.field.on("blur", this.onBlur, this);
32661         if(this.field.grow){
32662             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32663         }
32664     },
32665
32666     onSpecialKey : function(field, e)
32667     {
32668         //Roo.log('editor onSpecialKey');
32669         if(this.completeOnEnter && e.getKey() == e.ENTER){
32670             e.stopEvent();
32671             this.completeEdit();
32672             return;
32673         }
32674         // do not fire special key otherwise it might hide close the editor...
32675         if(e.getKey() == e.ENTER){    
32676             return;
32677         }
32678         if(this.cancelOnEsc && e.getKey() == e.ESC){
32679             this.cancelEdit();
32680             return;
32681         } 
32682         this.fireEvent('specialkey', field, e);
32683     
32684     },
32685
32686     /**
32687      * Starts the editing process and shows the editor.
32688      * @param {String/HTMLElement/Element} el The element to edit
32689      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32690       * to the innerHTML of el.
32691      */
32692     startEdit : function(el, value){
32693         if(this.editing){
32694             this.completeEdit();
32695         }
32696         this.boundEl = Roo.get(el);
32697         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32698         if(!this.rendered){
32699             this.render(this.parentEl || document.body);
32700         }
32701         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32702             return;
32703         }
32704         this.startValue = v;
32705         this.field.setValue(v);
32706         if(this.autoSize){
32707             var sz = this.boundEl.getSize();
32708             switch(this.autoSize){
32709                 case "width":
32710                 this.setSize(sz.width,  "");
32711                 break;
32712                 case "height":
32713                 this.setSize("",  sz.height);
32714                 break;
32715                 default:
32716                 this.setSize(sz.width,  sz.height);
32717             }
32718         }
32719         this.el.alignTo(this.boundEl, this.alignment);
32720         this.editing = true;
32721         if(Roo.QuickTips){
32722             Roo.QuickTips.disable();
32723         }
32724         this.show();
32725     },
32726
32727     /**
32728      * Sets the height and width of this editor.
32729      * @param {Number} width The new width
32730      * @param {Number} height The new height
32731      */
32732     setSize : function(w, h){
32733         this.field.setSize(w, h);
32734         if(this.el){
32735             this.el.sync();
32736         }
32737     },
32738
32739     /**
32740      * Realigns the editor to the bound field based on the current alignment config value.
32741      */
32742     realign : function(){
32743         this.el.alignTo(this.boundEl, this.alignment);
32744     },
32745
32746     /**
32747      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32748      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32749      */
32750     completeEdit : function(remainVisible){
32751         if(!this.editing){
32752             return;
32753         }
32754         var v = this.getValue();
32755         if(this.revertInvalid !== false && !this.field.isValid()){
32756             v = this.startValue;
32757             this.cancelEdit(true);
32758         }
32759         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32760             this.editing = false;
32761             this.hide();
32762             return;
32763         }
32764         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32765             this.editing = false;
32766             if(this.updateEl && this.boundEl){
32767                 this.boundEl.update(v);
32768             }
32769             if(remainVisible !== true){
32770                 this.hide();
32771             }
32772             this.fireEvent("complete", this, v, this.startValue);
32773         }
32774     },
32775
32776     // private
32777     onShow : function(){
32778         this.el.show();
32779         if(this.hideEl !== false){
32780             this.boundEl.hide();
32781         }
32782         this.field.show();
32783         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32784             this.fixIEFocus = true;
32785             this.deferredFocus.defer(50, this);
32786         }else{
32787             this.field.focus();
32788         }
32789         this.fireEvent("startedit", this.boundEl, this.startValue);
32790     },
32791
32792     deferredFocus : function(){
32793         if(this.editing){
32794             this.field.focus();
32795         }
32796     },
32797
32798     /**
32799      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32800      * reverted to the original starting value.
32801      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32802      * cancel (defaults to false)
32803      */
32804     cancelEdit : function(remainVisible){
32805         if(this.editing){
32806             this.setValue(this.startValue);
32807             if(remainVisible !== true){
32808                 this.hide();
32809             }
32810         }
32811     },
32812
32813     // private
32814     onBlur : function(){
32815         if(this.allowBlur !== true && this.editing){
32816             this.completeEdit();
32817         }
32818     },
32819
32820     // private
32821     onHide : function(){
32822         if(this.editing){
32823             this.completeEdit();
32824             return;
32825         }
32826         this.field.blur();
32827         if(this.field.collapse){
32828             this.field.collapse();
32829         }
32830         this.el.hide();
32831         if(this.hideEl !== false){
32832             this.boundEl.show();
32833         }
32834         if(Roo.QuickTips){
32835             Roo.QuickTips.enable();
32836         }
32837     },
32838
32839     /**
32840      * Sets the data value of the editor
32841      * @param {Mixed} value Any valid value supported by the underlying field
32842      */
32843     setValue : function(v){
32844         this.field.setValue(v);
32845     },
32846
32847     /**
32848      * Gets the data value of the editor
32849      * @return {Mixed} The data value
32850      */
32851     getValue : function(){
32852         return this.field.getValue();
32853     }
32854 });/*
32855  * Based on:
32856  * Ext JS Library 1.1.1
32857  * Copyright(c) 2006-2007, Ext JS, LLC.
32858  *
32859  * Originally Released Under LGPL - original licence link has changed is not relivant.
32860  *
32861  * Fork - LGPL
32862  * <script type="text/javascript">
32863  */
32864  
32865 /**
32866  * @class Roo.BasicDialog
32867  * @extends Roo.util.Observable
32868  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32869  * <pre><code>
32870 var dlg = new Roo.BasicDialog("my-dlg", {
32871     height: 200,
32872     width: 300,
32873     minHeight: 100,
32874     minWidth: 150,
32875     modal: true,
32876     proxyDrag: true,
32877     shadow: true
32878 });
32879 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32880 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32881 dlg.addButton('Cancel', dlg.hide, dlg);
32882 dlg.show();
32883 </code></pre>
32884   <b>A Dialog should always be a direct child of the body element.</b>
32885  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32886  * @cfg {String} title Default text to display in the title bar (defaults to null)
32887  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32888  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32889  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32890  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32891  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32892  * (defaults to null with no animation)
32893  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32894  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32895  * property for valid values (defaults to 'all')
32896  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32897  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32898  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32899  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32900  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32901  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32902  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32903  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32904  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32905  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32906  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32907  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32908  * draggable = true (defaults to false)
32909  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32910  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32911  * shadow (defaults to false)
32912  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32913  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32914  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32915  * @cfg {Array} buttons Array of buttons
32916  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32917  * @constructor
32918  * Create a new BasicDialog.
32919  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32920  * @param {Object} config Configuration options
32921  */
32922 Roo.BasicDialog = function(el, config){
32923     this.el = Roo.get(el);
32924     var dh = Roo.DomHelper;
32925     if(!this.el && config && config.autoCreate){
32926         if(typeof config.autoCreate == "object"){
32927             if(!config.autoCreate.id){
32928                 config.autoCreate.id = el;
32929             }
32930             this.el = dh.append(document.body,
32931                         config.autoCreate, true);
32932         }else{
32933             this.el = dh.append(document.body,
32934                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32935         }
32936     }
32937     el = this.el;
32938     el.setDisplayed(true);
32939     el.hide = this.hideAction;
32940     this.id = el.id;
32941     el.addClass("x-dlg");
32942
32943     Roo.apply(this, config);
32944
32945     this.proxy = el.createProxy("x-dlg-proxy");
32946     this.proxy.hide = this.hideAction;
32947     this.proxy.setOpacity(.5);
32948     this.proxy.hide();
32949
32950     if(config.width){
32951         el.setWidth(config.width);
32952     }
32953     if(config.height){
32954         el.setHeight(config.height);
32955     }
32956     this.size = el.getSize();
32957     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32958         this.xy = [config.x,config.y];
32959     }else{
32960         this.xy = el.getCenterXY(true);
32961     }
32962     /** The header element @type Roo.Element */
32963     this.header = el.child("> .x-dlg-hd");
32964     /** The body element @type Roo.Element */
32965     this.body = el.child("> .x-dlg-bd");
32966     /** The footer element @type Roo.Element */
32967     this.footer = el.child("> .x-dlg-ft");
32968
32969     if(!this.header){
32970         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32971     }
32972     if(!this.body){
32973         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32974     }
32975
32976     this.header.unselectable();
32977     if(this.title){
32978         this.header.update(this.title);
32979     }
32980     // this element allows the dialog to be focused for keyboard event
32981     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32982     this.focusEl.swallowEvent("click", true);
32983
32984     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32985
32986     // wrap the body and footer for special rendering
32987     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32988     if(this.footer){
32989         this.bwrap.dom.appendChild(this.footer.dom);
32990     }
32991
32992     this.bg = this.el.createChild({
32993         tag: "div", cls:"x-dlg-bg",
32994         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32995     });
32996     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32997
32998
32999     if(this.autoScroll !== false && !this.autoTabs){
33000         this.body.setStyle("overflow", "auto");
33001     }
33002
33003     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33004
33005     if(this.closable !== false){
33006         this.el.addClass("x-dlg-closable");
33007         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33008         this.close.on("click", this.closeClick, this);
33009         this.close.addClassOnOver("x-dlg-close-over");
33010     }
33011     if(this.collapsible !== false){
33012         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33013         this.collapseBtn.on("click", this.collapseClick, this);
33014         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33015         this.header.on("dblclick", this.collapseClick, this);
33016     }
33017     if(this.resizable !== false){
33018         this.el.addClass("x-dlg-resizable");
33019         this.resizer = new Roo.Resizable(el, {
33020             minWidth: this.minWidth || 80,
33021             minHeight:this.minHeight || 80,
33022             handles: this.resizeHandles || "all",
33023             pinned: true
33024         });
33025         this.resizer.on("beforeresize", this.beforeResize, this);
33026         this.resizer.on("resize", this.onResize, this);
33027     }
33028     if(this.draggable !== false){
33029         el.addClass("x-dlg-draggable");
33030         if (!this.proxyDrag) {
33031             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33032         }
33033         else {
33034             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33035         }
33036         dd.setHandleElId(this.header.id);
33037         dd.endDrag = this.endMove.createDelegate(this);
33038         dd.startDrag = this.startMove.createDelegate(this);
33039         dd.onDrag = this.onDrag.createDelegate(this);
33040         dd.scroll = false;
33041         this.dd = dd;
33042     }
33043     if(this.modal){
33044         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33045         this.mask.enableDisplayMode("block");
33046         this.mask.hide();
33047         this.el.addClass("x-dlg-modal");
33048     }
33049     if(this.shadow){
33050         this.shadow = new Roo.Shadow({
33051             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33052             offset : this.shadowOffset
33053         });
33054     }else{
33055         this.shadowOffset = 0;
33056     }
33057     if(Roo.useShims && this.shim !== false){
33058         this.shim = this.el.createShim();
33059         this.shim.hide = this.hideAction;
33060         this.shim.hide();
33061     }else{
33062         this.shim = false;
33063     }
33064     if(this.autoTabs){
33065         this.initTabs();
33066     }
33067     if (this.buttons) { 
33068         var bts= this.buttons;
33069         this.buttons = [];
33070         Roo.each(bts, function(b) {
33071             this.addButton(b);
33072         }, this);
33073     }
33074     
33075     
33076     this.addEvents({
33077         /**
33078          * @event keydown
33079          * Fires when a key is pressed
33080          * @param {Roo.BasicDialog} this
33081          * @param {Roo.EventObject} e
33082          */
33083         "keydown" : true,
33084         /**
33085          * @event move
33086          * Fires when this dialog is moved by the user.
33087          * @param {Roo.BasicDialog} this
33088          * @param {Number} x The new page X
33089          * @param {Number} y The new page Y
33090          */
33091         "move" : true,
33092         /**
33093          * @event resize
33094          * Fires when this dialog is resized by the user.
33095          * @param {Roo.BasicDialog} this
33096          * @param {Number} width The new width
33097          * @param {Number} height The new height
33098          */
33099         "resize" : true,
33100         /**
33101          * @event beforehide
33102          * Fires before this dialog is hidden.
33103          * @param {Roo.BasicDialog} this
33104          */
33105         "beforehide" : true,
33106         /**
33107          * @event hide
33108          * Fires when this dialog is hidden.
33109          * @param {Roo.BasicDialog} this
33110          */
33111         "hide" : true,
33112         /**
33113          * @event beforeshow
33114          * Fires before this dialog is shown.
33115          * @param {Roo.BasicDialog} this
33116          */
33117         "beforeshow" : true,
33118         /**
33119          * @event show
33120          * Fires when this dialog is shown.
33121          * @param {Roo.BasicDialog} this
33122          */
33123         "show" : true
33124     });
33125     el.on("keydown", this.onKeyDown, this);
33126     el.on("mousedown", this.toFront, this);
33127     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33128     this.el.hide();
33129     Roo.DialogManager.register(this);
33130     Roo.BasicDialog.superclass.constructor.call(this);
33131 };
33132
33133 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33134     shadowOffset: Roo.isIE ? 6 : 5,
33135     minHeight: 80,
33136     minWidth: 200,
33137     minButtonWidth: 75,
33138     defaultButton: null,
33139     buttonAlign: "right",
33140     tabTag: 'div',
33141     firstShow: true,
33142
33143     /**
33144      * Sets the dialog title text
33145      * @param {String} text The title text to display
33146      * @return {Roo.BasicDialog} this
33147      */
33148     setTitle : function(text){
33149         this.header.update(text);
33150         return this;
33151     },
33152
33153     // private
33154     closeClick : function(){
33155         this.hide();
33156     },
33157
33158     // private
33159     collapseClick : function(){
33160         this[this.collapsed ? "expand" : "collapse"]();
33161     },
33162
33163     /**
33164      * Collapses the dialog to its minimized state (only the title bar is visible).
33165      * Equivalent to the user clicking the collapse dialog button.
33166      */
33167     collapse : function(){
33168         if(!this.collapsed){
33169             this.collapsed = true;
33170             this.el.addClass("x-dlg-collapsed");
33171             this.restoreHeight = this.el.getHeight();
33172             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33173         }
33174     },
33175
33176     /**
33177      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33178      * clicking the expand dialog button.
33179      */
33180     expand : function(){
33181         if(this.collapsed){
33182             this.collapsed = false;
33183             this.el.removeClass("x-dlg-collapsed");
33184             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33185         }
33186     },
33187
33188     /**
33189      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33190      * @return {Roo.TabPanel} The tabs component
33191      */
33192     initTabs : function(){
33193         var tabs = this.getTabs();
33194         while(tabs.getTab(0)){
33195             tabs.removeTab(0);
33196         }
33197         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33198             var dom = el.dom;
33199             tabs.addTab(Roo.id(dom), dom.title);
33200             dom.title = "";
33201         });
33202         tabs.activate(0);
33203         return tabs;
33204     },
33205
33206     // private
33207     beforeResize : function(){
33208         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33209     },
33210
33211     // private
33212     onResize : function(){
33213         this.refreshSize();
33214         this.syncBodyHeight();
33215         this.adjustAssets();
33216         this.focus();
33217         this.fireEvent("resize", this, this.size.width, this.size.height);
33218     },
33219
33220     // private
33221     onKeyDown : function(e){
33222         if(this.isVisible()){
33223             this.fireEvent("keydown", this, e);
33224         }
33225     },
33226
33227     /**
33228      * Resizes the dialog.
33229      * @param {Number} width
33230      * @param {Number} height
33231      * @return {Roo.BasicDialog} this
33232      */
33233     resizeTo : function(width, height){
33234         this.el.setSize(width, height);
33235         this.size = {width: width, height: height};
33236         this.syncBodyHeight();
33237         if(this.fixedcenter){
33238             this.center();
33239         }
33240         if(this.isVisible()){
33241             this.constrainXY();
33242             this.adjustAssets();
33243         }
33244         this.fireEvent("resize", this, width, height);
33245         return this;
33246     },
33247
33248
33249     /**
33250      * Resizes the dialog to fit the specified content size.
33251      * @param {Number} width
33252      * @param {Number} height
33253      * @return {Roo.BasicDialog} this
33254      */
33255     setContentSize : function(w, h){
33256         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33257         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33258         //if(!this.el.isBorderBox()){
33259             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33260             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33261         //}
33262         if(this.tabs){
33263             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33264             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33265         }
33266         this.resizeTo(w, h);
33267         return this;
33268     },
33269
33270     /**
33271      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33272      * executed in response to a particular key being pressed while the dialog is active.
33273      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33274      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33275      * @param {Function} fn The function to call
33276      * @param {Object} scope (optional) The scope of the function
33277      * @return {Roo.BasicDialog} this
33278      */
33279     addKeyListener : function(key, fn, scope){
33280         var keyCode, shift, ctrl, alt;
33281         if(typeof key == "object" && !(key instanceof Array)){
33282             keyCode = key["key"];
33283             shift = key["shift"];
33284             ctrl = key["ctrl"];
33285             alt = key["alt"];
33286         }else{
33287             keyCode = key;
33288         }
33289         var handler = function(dlg, e){
33290             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33291                 var k = e.getKey();
33292                 if(keyCode instanceof Array){
33293                     for(var i = 0, len = keyCode.length; i < len; i++){
33294                         if(keyCode[i] == k){
33295                           fn.call(scope || window, dlg, k, e);
33296                           return;
33297                         }
33298                     }
33299                 }else{
33300                     if(k == keyCode){
33301                         fn.call(scope || window, dlg, k, e);
33302                     }
33303                 }
33304             }
33305         };
33306         this.on("keydown", handler);
33307         return this;
33308     },
33309
33310     /**
33311      * Returns the TabPanel component (creates it if it doesn't exist).
33312      * Note: If you wish to simply check for the existence of tabs without creating them,
33313      * check for a null 'tabs' property.
33314      * @return {Roo.TabPanel} The tabs component
33315      */
33316     getTabs : function(){
33317         if(!this.tabs){
33318             this.el.addClass("x-dlg-auto-tabs");
33319             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33320             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33321         }
33322         return this.tabs;
33323     },
33324
33325     /**
33326      * Adds a button to the footer section of the dialog.
33327      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33328      * object or a valid Roo.DomHelper element config
33329      * @param {Function} handler The function called when the button is clicked
33330      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33331      * @return {Roo.Button} The new button
33332      */
33333     addButton : function(config, handler, scope){
33334         var dh = Roo.DomHelper;
33335         if(!this.footer){
33336             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33337         }
33338         if(!this.btnContainer){
33339             var tb = this.footer.createChild({
33340
33341                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33342                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33343             }, null, true);
33344             this.btnContainer = tb.firstChild.firstChild.firstChild;
33345         }
33346         var bconfig = {
33347             handler: handler,
33348             scope: scope,
33349             minWidth: this.minButtonWidth,
33350             hideParent:true
33351         };
33352         if(typeof config == "string"){
33353             bconfig.text = config;
33354         }else{
33355             if(config.tag){
33356                 bconfig.dhconfig = config;
33357             }else{
33358                 Roo.apply(bconfig, config);
33359             }
33360         }
33361         var fc = false;
33362         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33363             bconfig.position = Math.max(0, bconfig.position);
33364             fc = this.btnContainer.childNodes[bconfig.position];
33365         }
33366          
33367         var btn = new Roo.Button(
33368             fc ? 
33369                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33370                 : this.btnContainer.appendChild(document.createElement("td")),
33371             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33372             bconfig
33373         );
33374         this.syncBodyHeight();
33375         if(!this.buttons){
33376             /**
33377              * Array of all the buttons that have been added to this dialog via addButton
33378              * @type Array
33379              */
33380             this.buttons = [];
33381         }
33382         this.buttons.push(btn);
33383         return btn;
33384     },
33385
33386     /**
33387      * Sets the default button to be focused when the dialog is displayed.
33388      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33389      * @return {Roo.BasicDialog} this
33390      */
33391     setDefaultButton : function(btn){
33392         this.defaultButton = btn;
33393         return this;
33394     },
33395
33396     // private
33397     getHeaderFooterHeight : function(safe){
33398         var height = 0;
33399         if(this.header){
33400            height += this.header.getHeight();
33401         }
33402         if(this.footer){
33403            var fm = this.footer.getMargins();
33404             height += (this.footer.getHeight()+fm.top+fm.bottom);
33405         }
33406         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33407         height += this.centerBg.getPadding("tb");
33408         return height;
33409     },
33410
33411     // private
33412     syncBodyHeight : function()
33413     {
33414         var bd = this.body, // the text
33415             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33416             bw = this.bwrap;
33417         var height = this.size.height - this.getHeaderFooterHeight(false);
33418         bd.setHeight(height-bd.getMargins("tb"));
33419         var hh = this.header.getHeight();
33420         var h = this.size.height-hh;
33421         cb.setHeight(h);
33422         
33423         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33424         bw.setHeight(h-cb.getPadding("tb"));
33425         
33426         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33427         bd.setWidth(bw.getWidth(true));
33428         if(this.tabs){
33429             this.tabs.syncHeight();
33430             if(Roo.isIE){
33431                 this.tabs.el.repaint();
33432             }
33433         }
33434     },
33435
33436     /**
33437      * Restores the previous state of the dialog if Roo.state is configured.
33438      * @return {Roo.BasicDialog} this
33439      */
33440     restoreState : function(){
33441         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33442         if(box && box.width){
33443             this.xy = [box.x, box.y];
33444             this.resizeTo(box.width, box.height);
33445         }
33446         return this;
33447     },
33448
33449     // private
33450     beforeShow : function(){
33451         this.expand();
33452         if(this.fixedcenter){
33453             this.xy = this.el.getCenterXY(true);
33454         }
33455         if(this.modal){
33456             Roo.get(document.body).addClass("x-body-masked");
33457             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33458             this.mask.show();
33459         }
33460         this.constrainXY();
33461     },
33462
33463     // private
33464     animShow : function(){
33465         var b = Roo.get(this.animateTarget).getBox();
33466         this.proxy.setSize(b.width, b.height);
33467         this.proxy.setLocation(b.x, b.y);
33468         this.proxy.show();
33469         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33470                     true, .35, this.showEl.createDelegate(this));
33471     },
33472
33473     /**
33474      * Shows the dialog.
33475      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33476      * @return {Roo.BasicDialog} this
33477      */
33478     show : function(animateTarget){
33479         if (this.fireEvent("beforeshow", this) === false){
33480             return;
33481         }
33482         if(this.syncHeightBeforeShow){
33483             this.syncBodyHeight();
33484         }else if(this.firstShow){
33485             this.firstShow = false;
33486             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33487         }
33488         this.animateTarget = animateTarget || this.animateTarget;
33489         if(!this.el.isVisible()){
33490             this.beforeShow();
33491             if(this.animateTarget && Roo.get(this.animateTarget)){
33492                 this.animShow();
33493             }else{
33494                 this.showEl();
33495             }
33496         }
33497         return this;
33498     },
33499
33500     // private
33501     showEl : function(){
33502         this.proxy.hide();
33503         this.el.setXY(this.xy);
33504         this.el.show();
33505         this.adjustAssets(true);
33506         this.toFront();
33507         this.focus();
33508         // IE peekaboo bug - fix found by Dave Fenwick
33509         if(Roo.isIE){
33510             this.el.repaint();
33511         }
33512         this.fireEvent("show", this);
33513     },
33514
33515     /**
33516      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33517      * dialog itself will receive focus.
33518      */
33519     focus : function(){
33520         if(this.defaultButton){
33521             this.defaultButton.focus();
33522         }else{
33523             this.focusEl.focus();
33524         }
33525     },
33526
33527     // private
33528     constrainXY : function(){
33529         if(this.constraintoviewport !== false){
33530             if(!this.viewSize){
33531                 if(this.container){
33532                     var s = this.container.getSize();
33533                     this.viewSize = [s.width, s.height];
33534                 }else{
33535                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33536                 }
33537             }
33538             var s = Roo.get(this.container||document).getScroll();
33539
33540             var x = this.xy[0], y = this.xy[1];
33541             var w = this.size.width, h = this.size.height;
33542             var vw = this.viewSize[0], vh = this.viewSize[1];
33543             // only move it if it needs it
33544             var moved = false;
33545             // first validate right/bottom
33546             if(x + w > vw+s.left){
33547                 x = vw - w;
33548                 moved = true;
33549             }
33550             if(y + h > vh+s.top){
33551                 y = vh - h;
33552                 moved = true;
33553             }
33554             // then make sure top/left isn't negative
33555             if(x < s.left){
33556                 x = s.left;
33557                 moved = true;
33558             }
33559             if(y < s.top){
33560                 y = s.top;
33561                 moved = true;
33562             }
33563             if(moved){
33564                 // cache xy
33565                 this.xy = [x, y];
33566                 if(this.isVisible()){
33567                     this.el.setLocation(x, y);
33568                     this.adjustAssets();
33569                 }
33570             }
33571         }
33572     },
33573
33574     // private
33575     onDrag : function(){
33576         if(!this.proxyDrag){
33577             this.xy = this.el.getXY();
33578             this.adjustAssets();
33579         }
33580     },
33581
33582     // private
33583     adjustAssets : function(doShow){
33584         var x = this.xy[0], y = this.xy[1];
33585         var w = this.size.width, h = this.size.height;
33586         if(doShow === true){
33587             if(this.shadow){
33588                 this.shadow.show(this.el);
33589             }
33590             if(this.shim){
33591                 this.shim.show();
33592             }
33593         }
33594         if(this.shadow && this.shadow.isVisible()){
33595             this.shadow.show(this.el);
33596         }
33597         if(this.shim && this.shim.isVisible()){
33598             this.shim.setBounds(x, y, w, h);
33599         }
33600     },
33601
33602     // private
33603     adjustViewport : function(w, h){
33604         if(!w || !h){
33605             w = Roo.lib.Dom.getViewWidth();
33606             h = Roo.lib.Dom.getViewHeight();
33607         }
33608         // cache the size
33609         this.viewSize = [w, h];
33610         if(this.modal && this.mask.isVisible()){
33611             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33612             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33613         }
33614         if(this.isVisible()){
33615             this.constrainXY();
33616         }
33617     },
33618
33619     /**
33620      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33621      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33622      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33623      */
33624     destroy : function(removeEl){
33625         if(this.isVisible()){
33626             this.animateTarget = null;
33627             this.hide();
33628         }
33629         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33630         if(this.tabs){
33631             this.tabs.destroy(removeEl);
33632         }
33633         Roo.destroy(
33634              this.shim,
33635              this.proxy,
33636              this.resizer,
33637              this.close,
33638              this.mask
33639         );
33640         if(this.dd){
33641             this.dd.unreg();
33642         }
33643         if(this.buttons){
33644            for(var i = 0, len = this.buttons.length; i < len; i++){
33645                this.buttons[i].destroy();
33646            }
33647         }
33648         this.el.removeAllListeners();
33649         if(removeEl === true){
33650             this.el.update("");
33651             this.el.remove();
33652         }
33653         Roo.DialogManager.unregister(this);
33654     },
33655
33656     // private
33657     startMove : function(){
33658         if(this.proxyDrag){
33659             this.proxy.show();
33660         }
33661         if(this.constraintoviewport !== false){
33662             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33663         }
33664     },
33665
33666     // private
33667     endMove : function(){
33668         if(!this.proxyDrag){
33669             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33670         }else{
33671             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33672             this.proxy.hide();
33673         }
33674         this.refreshSize();
33675         this.adjustAssets();
33676         this.focus();
33677         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33678     },
33679
33680     /**
33681      * Brings this dialog to the front of any other visible dialogs
33682      * @return {Roo.BasicDialog} this
33683      */
33684     toFront : function(){
33685         Roo.DialogManager.bringToFront(this);
33686         return this;
33687     },
33688
33689     /**
33690      * Sends this dialog to the back (under) of any other visible dialogs
33691      * @return {Roo.BasicDialog} this
33692      */
33693     toBack : function(){
33694         Roo.DialogManager.sendToBack(this);
33695         return this;
33696     },
33697
33698     /**
33699      * Centers this dialog in the viewport
33700      * @return {Roo.BasicDialog} this
33701      */
33702     center : function(){
33703         var xy = this.el.getCenterXY(true);
33704         this.moveTo(xy[0], xy[1]);
33705         return this;
33706     },
33707
33708     /**
33709      * Moves the dialog's top-left corner to the specified point
33710      * @param {Number} x
33711      * @param {Number} y
33712      * @return {Roo.BasicDialog} this
33713      */
33714     moveTo : function(x, y){
33715         this.xy = [x,y];
33716         if(this.isVisible()){
33717             this.el.setXY(this.xy);
33718             this.adjustAssets();
33719         }
33720         return this;
33721     },
33722
33723     /**
33724      * Aligns the dialog to the specified element
33725      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33726      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33727      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33728      * @return {Roo.BasicDialog} this
33729      */
33730     alignTo : function(element, position, offsets){
33731         this.xy = this.el.getAlignToXY(element, position, offsets);
33732         if(this.isVisible()){
33733             this.el.setXY(this.xy);
33734             this.adjustAssets();
33735         }
33736         return this;
33737     },
33738
33739     /**
33740      * Anchors an element to another element and realigns it when the window is resized.
33741      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33742      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33743      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33744      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33745      * is a number, it is used as the buffer delay (defaults to 50ms).
33746      * @return {Roo.BasicDialog} this
33747      */
33748     anchorTo : function(el, alignment, offsets, monitorScroll){
33749         var action = function(){
33750             this.alignTo(el, alignment, offsets);
33751         };
33752         Roo.EventManager.onWindowResize(action, this);
33753         var tm = typeof monitorScroll;
33754         if(tm != 'undefined'){
33755             Roo.EventManager.on(window, 'scroll', action, this,
33756                 {buffer: tm == 'number' ? monitorScroll : 50});
33757         }
33758         action.call(this);
33759         return this;
33760     },
33761
33762     /**
33763      * Returns true if the dialog is visible
33764      * @return {Boolean}
33765      */
33766     isVisible : function(){
33767         return this.el.isVisible();
33768     },
33769
33770     // private
33771     animHide : function(callback){
33772         var b = Roo.get(this.animateTarget).getBox();
33773         this.proxy.show();
33774         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33775         this.el.hide();
33776         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33777                     this.hideEl.createDelegate(this, [callback]));
33778     },
33779
33780     /**
33781      * Hides the dialog.
33782      * @param {Function} callback (optional) Function to call when the dialog is hidden
33783      * @return {Roo.BasicDialog} this
33784      */
33785     hide : function(callback){
33786         if (this.fireEvent("beforehide", this) === false){
33787             return;
33788         }
33789         if(this.shadow){
33790             this.shadow.hide();
33791         }
33792         if(this.shim) {
33793           this.shim.hide();
33794         }
33795         // sometimes animateTarget seems to get set.. causing problems...
33796         // this just double checks..
33797         if(this.animateTarget && Roo.get(this.animateTarget)) {
33798            this.animHide(callback);
33799         }else{
33800             this.el.hide();
33801             this.hideEl(callback);
33802         }
33803         return this;
33804     },
33805
33806     // private
33807     hideEl : function(callback){
33808         this.proxy.hide();
33809         if(this.modal){
33810             this.mask.hide();
33811             Roo.get(document.body).removeClass("x-body-masked");
33812         }
33813         this.fireEvent("hide", this);
33814         if(typeof callback == "function"){
33815             callback();
33816         }
33817     },
33818
33819     // private
33820     hideAction : function(){
33821         this.setLeft("-10000px");
33822         this.setTop("-10000px");
33823         this.setStyle("visibility", "hidden");
33824     },
33825
33826     // private
33827     refreshSize : function(){
33828         this.size = this.el.getSize();
33829         this.xy = this.el.getXY();
33830         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33831     },
33832
33833     // private
33834     // z-index is managed by the DialogManager and may be overwritten at any time
33835     setZIndex : function(index){
33836         if(this.modal){
33837             this.mask.setStyle("z-index", index);
33838         }
33839         if(this.shim){
33840             this.shim.setStyle("z-index", ++index);
33841         }
33842         if(this.shadow){
33843             this.shadow.setZIndex(++index);
33844         }
33845         this.el.setStyle("z-index", ++index);
33846         if(this.proxy){
33847             this.proxy.setStyle("z-index", ++index);
33848         }
33849         if(this.resizer){
33850             this.resizer.proxy.setStyle("z-index", ++index);
33851         }
33852
33853         this.lastZIndex = index;
33854     },
33855
33856     /**
33857      * Returns the element for this dialog
33858      * @return {Roo.Element} The underlying dialog Element
33859      */
33860     getEl : function(){
33861         return this.el;
33862     }
33863 });
33864
33865 /**
33866  * @class Roo.DialogManager
33867  * Provides global access to BasicDialogs that have been created and
33868  * support for z-indexing (layering) multiple open dialogs.
33869  */
33870 Roo.DialogManager = function(){
33871     var list = {};
33872     var accessList = [];
33873     var front = null;
33874
33875     // private
33876     var sortDialogs = function(d1, d2){
33877         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33878     };
33879
33880     // private
33881     var orderDialogs = function(){
33882         accessList.sort(sortDialogs);
33883         var seed = Roo.DialogManager.zseed;
33884         for(var i = 0, len = accessList.length; i < len; i++){
33885             var dlg = accessList[i];
33886             if(dlg){
33887                 dlg.setZIndex(seed + (i*10));
33888             }
33889         }
33890     };
33891
33892     return {
33893         /**
33894          * The starting z-index for BasicDialogs (defaults to 9000)
33895          * @type Number The z-index value
33896          */
33897         zseed : 9000,
33898
33899         // private
33900         register : function(dlg){
33901             list[dlg.id] = dlg;
33902             accessList.push(dlg);
33903         },
33904
33905         // private
33906         unregister : function(dlg){
33907             delete list[dlg.id];
33908             var i=0;
33909             var len=0;
33910             if(!accessList.indexOf){
33911                 for(  i = 0, len = accessList.length; i < len; i++){
33912                     if(accessList[i] == dlg){
33913                         accessList.splice(i, 1);
33914                         return;
33915                     }
33916                 }
33917             }else{
33918                  i = accessList.indexOf(dlg);
33919                 if(i != -1){
33920                     accessList.splice(i, 1);
33921                 }
33922             }
33923         },
33924
33925         /**
33926          * Gets a registered dialog by id
33927          * @param {String/Object} id The id of the dialog or a dialog
33928          * @return {Roo.BasicDialog} this
33929          */
33930         get : function(id){
33931             return typeof id == "object" ? id : list[id];
33932         },
33933
33934         /**
33935          * Brings the specified dialog to the front
33936          * @param {String/Object} dlg The id of the dialog or a dialog
33937          * @return {Roo.BasicDialog} this
33938          */
33939         bringToFront : function(dlg){
33940             dlg = this.get(dlg);
33941             if(dlg != front){
33942                 front = dlg;
33943                 dlg._lastAccess = new Date().getTime();
33944                 orderDialogs();
33945             }
33946             return dlg;
33947         },
33948
33949         /**
33950          * Sends the specified dialog to the back
33951          * @param {String/Object} dlg The id of the dialog or a dialog
33952          * @return {Roo.BasicDialog} this
33953          */
33954         sendToBack : function(dlg){
33955             dlg = this.get(dlg);
33956             dlg._lastAccess = -(new Date().getTime());
33957             orderDialogs();
33958             return dlg;
33959         },
33960
33961         /**
33962          * Hides all dialogs
33963          */
33964         hideAll : function(){
33965             for(var id in list){
33966                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33967                     list[id].hide();
33968                 }
33969             }
33970         }
33971     };
33972 }();
33973
33974 /**
33975  * @class Roo.LayoutDialog
33976  * @extends Roo.BasicDialog
33977  * @children Roo.ContentPanel
33978  * @parent builder none
33979  * Dialog which provides adjustments for working with a layout in a Dialog.
33980  * Add your necessary layout config options to the dialog's config.<br>
33981  * Example usage (including a nested layout):
33982  * <pre><code>
33983 if(!dialog){
33984     dialog = new Roo.LayoutDialog("download-dlg", {
33985         modal: true,
33986         width:600,
33987         height:450,
33988         shadow:true,
33989         minWidth:500,
33990         minHeight:350,
33991         autoTabs:true,
33992         proxyDrag:true,
33993         // layout config merges with the dialog config
33994         center:{
33995             tabPosition: "top",
33996             alwaysShowTabs: true
33997         }
33998     });
33999     dialog.addKeyListener(27, dialog.hide, dialog);
34000     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34001     dialog.addButton("Build It!", this.getDownload, this);
34002
34003     // we can even add nested layouts
34004     var innerLayout = new Roo.BorderLayout("dl-inner", {
34005         east: {
34006             initialSize: 200,
34007             autoScroll:true,
34008             split:true
34009         },
34010         center: {
34011             autoScroll:true
34012         }
34013     });
34014     innerLayout.beginUpdate();
34015     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34016     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34017     innerLayout.endUpdate(true);
34018
34019     var layout = dialog.getLayout();
34020     layout.beginUpdate();
34021     layout.add("center", new Roo.ContentPanel("standard-panel",
34022                         {title: "Download the Source", fitToFrame:true}));
34023     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34024                {title: "Build your own roo.js"}));
34025     layout.getRegion("center").showPanel(sp);
34026     layout.endUpdate();
34027 }
34028 </code></pre>
34029     * @constructor
34030     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34031     * @param {Object} config configuration options
34032   */
34033 Roo.LayoutDialog = function(el, cfg){
34034     
34035     var config=  cfg;
34036     if (typeof(cfg) == 'undefined') {
34037         config = Roo.apply({}, el);
34038         // not sure why we use documentElement here.. - it should always be body.
34039         // IE7 borks horribly if we use documentElement.
34040         // webkit also does not like documentElement - it creates a body element...
34041         el = Roo.get( document.body || document.documentElement ).createChild();
34042         //config.autoCreate = true;
34043     }
34044     
34045     
34046     config.autoTabs = false;
34047     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34048     this.body.setStyle({overflow:"hidden", position:"relative"});
34049     this.layout = new Roo.BorderLayout(this.body.dom, config);
34050     this.layout.monitorWindowResize = false;
34051     this.el.addClass("x-dlg-auto-layout");
34052     // fix case when center region overwrites center function
34053     this.center = Roo.BasicDialog.prototype.center;
34054     this.on("show", this.layout.layout, this.layout, true);
34055     if (config.items) {
34056         var xitems = config.items;
34057         delete config.items;
34058         Roo.each(xitems, this.addxtype, this);
34059     }
34060     
34061     
34062 };
34063 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34064     
34065     
34066     /**
34067      * @cfg {Roo.LayoutRegion} east  
34068      */
34069     /**
34070      * @cfg {Roo.LayoutRegion} west
34071      */
34072     /**
34073      * @cfg {Roo.LayoutRegion} south
34074      */
34075     /**
34076      * @cfg {Roo.LayoutRegion} north
34077      */
34078     /**
34079      * @cfg {Roo.LayoutRegion} center
34080      */
34081     /**
34082      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34083      */
34084     
34085     
34086     /**
34087      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34088      * @deprecated
34089      */
34090     endUpdate : function(){
34091         this.layout.endUpdate();
34092     },
34093
34094     /**
34095      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34096      *  @deprecated
34097      */
34098     beginUpdate : function(){
34099         this.layout.beginUpdate();
34100     },
34101
34102     /**
34103      * Get the BorderLayout for this dialog
34104      * @return {Roo.BorderLayout}
34105      */
34106     getLayout : function(){
34107         return this.layout;
34108     },
34109
34110     showEl : function(){
34111         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34112         if(Roo.isIE7){
34113             this.layout.layout();
34114         }
34115     },
34116
34117     // private
34118     // Use the syncHeightBeforeShow config option to control this automatically
34119     syncBodyHeight : function(){
34120         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34121         if(this.layout){this.layout.layout();}
34122     },
34123     
34124       /**
34125      * Add an xtype element (actually adds to the layout.)
34126      * @return {Object} xdata xtype object data.
34127      */
34128     
34129     addxtype : function(c) {
34130         return this.layout.addxtype(c);
34131     }
34132 });/*
34133  * Based on:
34134  * Ext JS Library 1.1.1
34135  * Copyright(c) 2006-2007, Ext JS, LLC.
34136  *
34137  * Originally Released Under LGPL - original licence link has changed is not relivant.
34138  *
34139  * Fork - LGPL
34140  * <script type="text/javascript">
34141  */
34142  
34143 /**
34144  * @class Roo.MessageBox
34145  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34146  * Example usage:
34147  *<pre><code>
34148 // Basic alert:
34149 Roo.Msg.alert('Status', 'Changes saved successfully.');
34150
34151 // Prompt for user data:
34152 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34153     if (btn == 'ok'){
34154         // process text value...
34155     }
34156 });
34157
34158 // Show a dialog using config options:
34159 Roo.Msg.show({
34160    title:'Save Changes?',
34161    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34162    buttons: Roo.Msg.YESNOCANCEL,
34163    fn: processResult,
34164    animEl: 'elId'
34165 });
34166 </code></pre>
34167  * @static
34168  */
34169 Roo.MessageBox = function(){
34170     var dlg, opt, mask, waitTimer;
34171     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34172     var buttons, activeTextEl, bwidth;
34173
34174     // private
34175     var handleButton = function(button){
34176         dlg.hide();
34177         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34178     };
34179
34180     // private
34181     var handleHide = function(){
34182         if(opt && opt.cls){
34183             dlg.el.removeClass(opt.cls);
34184         }
34185         if(waitTimer){
34186             Roo.TaskMgr.stop(waitTimer);
34187             waitTimer = null;
34188         }
34189     };
34190
34191     // private
34192     var updateButtons = function(b){
34193         var width = 0;
34194         if(!b){
34195             buttons["ok"].hide();
34196             buttons["cancel"].hide();
34197             buttons["yes"].hide();
34198             buttons["no"].hide();
34199             dlg.footer.dom.style.display = 'none';
34200             return width;
34201         }
34202         dlg.footer.dom.style.display = '';
34203         for(var k in buttons){
34204             if(typeof buttons[k] != "function"){
34205                 if(b[k]){
34206                     buttons[k].show();
34207                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34208                     width += buttons[k].el.getWidth()+15;
34209                 }else{
34210                     buttons[k].hide();
34211                 }
34212             }
34213         }
34214         return width;
34215     };
34216
34217     // private
34218     var handleEsc = function(d, k, e){
34219         if(opt && opt.closable !== false){
34220             dlg.hide();
34221         }
34222         if(e){
34223             e.stopEvent();
34224         }
34225     };
34226
34227     return {
34228         /**
34229          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34230          * @return {Roo.BasicDialog} The BasicDialog element
34231          */
34232         getDialog : function(){
34233            if(!dlg){
34234                 dlg = new Roo.BasicDialog("x-msg-box", {
34235                     autoCreate : true,
34236                     shadow: true,
34237                     draggable: true,
34238                     resizable:false,
34239                     constraintoviewport:false,
34240                     fixedcenter:true,
34241                     collapsible : false,
34242                     shim:true,
34243                     modal: true,
34244                     width:400, height:100,
34245                     buttonAlign:"center",
34246                     closeClick : function(){
34247                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34248                             handleButton("no");
34249                         }else{
34250                             handleButton("cancel");
34251                         }
34252                     }
34253                 });
34254                 dlg.on("hide", handleHide);
34255                 mask = dlg.mask;
34256                 dlg.addKeyListener(27, handleEsc);
34257                 buttons = {};
34258                 var bt = this.buttonText;
34259                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34260                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34261                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34262                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34263                 bodyEl = dlg.body.createChild({
34264
34265                     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>'
34266                 });
34267                 msgEl = bodyEl.dom.firstChild;
34268                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34269                 textboxEl.enableDisplayMode();
34270                 textboxEl.addKeyListener([10,13], function(){
34271                     if(dlg.isVisible() && opt && opt.buttons){
34272                         if(opt.buttons.ok){
34273                             handleButton("ok");
34274                         }else if(opt.buttons.yes){
34275                             handleButton("yes");
34276                         }
34277                     }
34278                 });
34279                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34280                 textareaEl.enableDisplayMode();
34281                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34282                 progressEl.enableDisplayMode();
34283                 var pf = progressEl.dom.firstChild;
34284                 if (pf) {
34285                     pp = Roo.get(pf.firstChild);
34286                     pp.setHeight(pf.offsetHeight);
34287                 }
34288                 
34289             }
34290             return dlg;
34291         },
34292
34293         /**
34294          * Updates the message box body text
34295          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34296          * the XHTML-compliant non-breaking space character '&amp;#160;')
34297          * @return {Roo.MessageBox} This message box
34298          */
34299         updateText : function(text){
34300             if(!dlg.isVisible() && !opt.width){
34301                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34302             }
34303             msgEl.innerHTML = text || '&#160;';
34304       
34305             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34306             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34307             var w = Math.max(
34308                     Math.min(opt.width || cw , this.maxWidth), 
34309                     Math.max(opt.minWidth || this.minWidth, bwidth)
34310             );
34311             if(opt.prompt){
34312                 activeTextEl.setWidth(w);
34313             }
34314             if(dlg.isVisible()){
34315                 dlg.fixedcenter = false;
34316             }
34317             // to big, make it scroll. = But as usual stupid IE does not support
34318             // !important..
34319             
34320             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34321                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34322                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34323             } else {
34324                 bodyEl.dom.style.height = '';
34325                 bodyEl.dom.style.overflowY = '';
34326             }
34327             if (cw > w) {
34328                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34329             } else {
34330                 bodyEl.dom.style.overflowX = '';
34331             }
34332             
34333             dlg.setContentSize(w, bodyEl.getHeight());
34334             if(dlg.isVisible()){
34335                 dlg.fixedcenter = true;
34336             }
34337             return this;
34338         },
34339
34340         /**
34341          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34342          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34343          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34344          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34345          * @return {Roo.MessageBox} This message box
34346          */
34347         updateProgress : function(value, text){
34348             if(text){
34349                 this.updateText(text);
34350             }
34351             if (pp) { // weird bug on my firefox - for some reason this is not defined
34352                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34353             }
34354             return this;
34355         },        
34356
34357         /**
34358          * Returns true if the message box is currently displayed
34359          * @return {Boolean} True if the message box is visible, else false
34360          */
34361         isVisible : function(){
34362             return dlg && dlg.isVisible();  
34363         },
34364
34365         /**
34366          * Hides the message box if it is displayed
34367          */
34368         hide : function(){
34369             if(this.isVisible()){
34370                 dlg.hide();
34371             }  
34372         },
34373
34374         /**
34375          * Displays a new message box, or reinitializes an existing message box, based on the config options
34376          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34377          * The following config object properties are supported:
34378          * <pre>
34379 Property    Type             Description
34380 ----------  ---------------  ------------------------------------------------------------------------------------
34381 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34382                                    closes (defaults to undefined)
34383 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34384                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34385 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34386                                    progress and wait dialogs will ignore this property and always hide the
34387                                    close button as they can only be closed programmatically.
34388 cls               String           A custom CSS class to apply to the message box element
34389 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34390                                    displayed (defaults to 75)
34391 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34392                                    function will be btn (the name of the button that was clicked, if applicable,
34393                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34394                                    Progress and wait dialogs will ignore this option since they do not respond to
34395                                    user actions and can only be closed programmatically, so any required function
34396                                    should be called by the same code after it closes the dialog.
34397 icon              String           A CSS class that provides a background image to be used as an icon for
34398                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34399 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34400 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34401 modal             Boolean          False to allow user interaction with the page while the message box is
34402                                    displayed (defaults to true)
34403 msg               String           A string that will replace the existing message box body text (defaults
34404                                    to the XHTML-compliant non-breaking space character '&#160;')
34405 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34406 progress          Boolean          True to display a progress bar (defaults to false)
34407 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34408 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34409 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34410 title             String           The title text
34411 value             String           The string value to set into the active textbox element if displayed
34412 wait              Boolean          True to display a progress bar (defaults to false)
34413 width             Number           The width of the dialog in pixels
34414 </pre>
34415          *
34416          * Example usage:
34417          * <pre><code>
34418 Roo.Msg.show({
34419    title: 'Address',
34420    msg: 'Please enter your address:',
34421    width: 300,
34422    buttons: Roo.MessageBox.OKCANCEL,
34423    multiline: true,
34424    fn: saveAddress,
34425    animEl: 'addAddressBtn'
34426 });
34427 </code></pre>
34428          * @param {Object} config Configuration options
34429          * @return {Roo.MessageBox} This message box
34430          */
34431         show : function(options)
34432         {
34433             
34434             // this causes nightmares if you show one dialog after another
34435             // especially on callbacks..
34436              
34437             if(this.isVisible()){
34438                 
34439                 this.hide();
34440                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34441                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34442                 Roo.log("New Dialog Message:" +  options.msg )
34443                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34444                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34445                 
34446             }
34447             var d = this.getDialog();
34448             opt = options;
34449             d.setTitle(opt.title || "&#160;");
34450             d.close.setDisplayed(opt.closable !== false);
34451             activeTextEl = textboxEl;
34452             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34453             if(opt.prompt){
34454                 if(opt.multiline){
34455                     textboxEl.hide();
34456                     textareaEl.show();
34457                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34458                         opt.multiline : this.defaultTextHeight);
34459                     activeTextEl = textareaEl;
34460                 }else{
34461                     textboxEl.show();
34462                     textareaEl.hide();
34463                 }
34464             }else{
34465                 textboxEl.hide();
34466                 textareaEl.hide();
34467             }
34468             progressEl.setDisplayed(opt.progress === true);
34469             this.updateProgress(0);
34470             activeTextEl.dom.value = opt.value || "";
34471             if(opt.prompt){
34472                 dlg.setDefaultButton(activeTextEl);
34473             }else{
34474                 var bs = opt.buttons;
34475                 var db = null;
34476                 if(bs && bs.ok){
34477                     db = buttons["ok"];
34478                 }else if(bs && bs.yes){
34479                     db = buttons["yes"];
34480                 }
34481                 dlg.setDefaultButton(db);
34482             }
34483             bwidth = updateButtons(opt.buttons);
34484             this.updateText(opt.msg);
34485             if(opt.cls){
34486                 d.el.addClass(opt.cls);
34487             }
34488             d.proxyDrag = opt.proxyDrag === true;
34489             d.modal = opt.modal !== false;
34490             d.mask = opt.modal !== false ? mask : false;
34491             if(!d.isVisible()){
34492                 // force it to the end of the z-index stack so it gets a cursor in FF
34493                 document.body.appendChild(dlg.el.dom);
34494                 d.animateTarget = null;
34495                 d.show(options.animEl);
34496             }
34497             return this;
34498         },
34499
34500         /**
34501          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34502          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34503          * and closing the message box when the process is complete.
34504          * @param {String} title The title bar text
34505          * @param {String} msg The message box body text
34506          * @return {Roo.MessageBox} This message box
34507          */
34508         progress : function(title, msg){
34509             this.show({
34510                 title : title,
34511                 msg : msg,
34512                 buttons: false,
34513                 progress:true,
34514                 closable:false,
34515                 minWidth: this.minProgressWidth,
34516                 modal : true
34517             });
34518             return this;
34519         },
34520
34521         /**
34522          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34523          * If a callback function is passed it will be called after the user clicks the button, and the
34524          * id of the button that was clicked will be passed as the only parameter to the callback
34525          * (could also be the top-right close button).
34526          * @param {String} title The title bar text
34527          * @param {String} msg The message box body text
34528          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34529          * @param {Object} scope (optional) The scope of the callback function
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         alert : function(title, msg, fn, scope){
34533             this.show({
34534                 title : title,
34535                 msg : msg,
34536                 buttons: this.OK,
34537                 fn: fn,
34538                 scope : scope,
34539                 modal : true
34540             });
34541             return this;
34542         },
34543
34544         /**
34545          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34546          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34547          * You are responsible for closing the message box when the process is complete.
34548          * @param {String} msg The message box body text
34549          * @param {String} title (optional) The title bar text
34550          * @return {Roo.MessageBox} This message box
34551          */
34552         wait : function(msg, title){
34553             this.show({
34554                 title : title,
34555                 msg : msg,
34556                 buttons: false,
34557                 closable:false,
34558                 progress:true,
34559                 modal:true,
34560                 width:300,
34561                 wait:true
34562             });
34563             waitTimer = Roo.TaskMgr.start({
34564                 run: function(i){
34565                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34566                 },
34567                 interval: 1000
34568             });
34569             return this;
34570         },
34571
34572         /**
34573          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34574          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34575          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34576          * @param {String} title The title bar text
34577          * @param {String} msg The message box body text
34578          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34579          * @param {Object} scope (optional) The scope of the callback function
34580          * @return {Roo.MessageBox} This message box
34581          */
34582         confirm : function(title, msg, fn, scope){
34583             this.show({
34584                 title : title,
34585                 msg : msg,
34586                 buttons: this.YESNO,
34587                 fn: fn,
34588                 scope : scope,
34589                 modal : true
34590             });
34591             return this;
34592         },
34593
34594         /**
34595          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34596          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34597          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34598          * (could also be the top-right close button) and the text that was entered will be passed as the two
34599          * parameters to the callback.
34600          * @param {String} title The title bar text
34601          * @param {String} msg The message box body text
34602          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34603          * @param {Object} scope (optional) The scope of the callback function
34604          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34605          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34606          * @return {Roo.MessageBox} This message box
34607          */
34608         prompt : function(title, msg, fn, scope, multiline){
34609             this.show({
34610                 title : title,
34611                 msg : msg,
34612                 buttons: this.OKCANCEL,
34613                 fn: fn,
34614                 minWidth:250,
34615                 scope : scope,
34616                 prompt:true,
34617                 multiline: multiline,
34618                 modal : true
34619             });
34620             return this;
34621         },
34622
34623         /**
34624          * Button config that displays a single OK button
34625          * @type Object
34626          */
34627         OK : {ok:true},
34628         /**
34629          * Button config that displays Yes and No buttons
34630          * @type Object
34631          */
34632         YESNO : {yes:true, no:true},
34633         /**
34634          * Button config that displays OK and Cancel buttons
34635          * @type Object
34636          */
34637         OKCANCEL : {ok:true, cancel:true},
34638         /**
34639          * Button config that displays Yes, No and Cancel buttons
34640          * @type Object
34641          */
34642         YESNOCANCEL : {yes:true, no:true, cancel:true},
34643
34644         /**
34645          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34646          * @type Number
34647          */
34648         defaultTextHeight : 75,
34649         /**
34650          * The maximum width in pixels of the message box (defaults to 600)
34651          * @type Number
34652          */
34653         maxWidth : 600,
34654         /**
34655          * The minimum width in pixels of the message box (defaults to 100)
34656          * @type Number
34657          */
34658         minWidth : 100,
34659         /**
34660          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34661          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34662          * @type Number
34663          */
34664         minProgressWidth : 250,
34665         /**
34666          * An object containing the default button text strings that can be overriden for localized language support.
34667          * Supported properties are: ok, cancel, yes and no.
34668          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34669          * @type Object
34670          */
34671         buttonText : {
34672             ok : "OK",
34673             cancel : "Cancel",
34674             yes : "Yes",
34675             no : "No"
34676         }
34677     };
34678 }();
34679
34680 /**
34681  * Shorthand for {@link Roo.MessageBox}
34682  */
34683 Roo.Msg = Roo.MessageBox;/*
34684  * Based on:
34685  * Ext JS Library 1.1.1
34686  * Copyright(c) 2006-2007, Ext JS, LLC.
34687  *
34688  * Originally Released Under LGPL - original licence link has changed is not relivant.
34689  *
34690  * Fork - LGPL
34691  * <script type="text/javascript">
34692  */
34693 /**
34694  * @class Roo.QuickTips
34695  * Provides attractive and customizable tooltips for any element.
34696  * @static
34697  */
34698 Roo.QuickTips = function(){
34699     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34700     var ce, bd, xy, dd;
34701     var visible = false, disabled = true, inited = false;
34702     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34703     
34704     var onOver = function(e){
34705         if(disabled){
34706             return;
34707         }
34708         var t = e.getTarget();
34709         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34710             return;
34711         }
34712         if(ce && t == ce.el){
34713             clearTimeout(hideProc);
34714             return;
34715         }
34716         if(t && tagEls[t.id]){
34717             tagEls[t.id].el = t;
34718             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34719             return;
34720         }
34721         var ttp, et = Roo.fly(t);
34722         var ns = cfg.namespace;
34723         if(tm.interceptTitles && t.title){
34724             ttp = t.title;
34725             t.qtip = ttp;
34726             t.removeAttribute("title");
34727             e.preventDefault();
34728         }else{
34729             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34730         }
34731         if(ttp){
34732             showProc = show.defer(tm.showDelay, tm, [{
34733                 el: t, 
34734                 text: ttp.replace(/\\n/g,'<br/>'),
34735                 width: et.getAttributeNS(ns, cfg.width),
34736                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34737                 title: et.getAttributeNS(ns, cfg.title),
34738                     cls: et.getAttributeNS(ns, cfg.cls)
34739             }]);
34740         }
34741     };
34742     
34743     var onOut = function(e){
34744         clearTimeout(showProc);
34745         var t = e.getTarget();
34746         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34747             hideProc = setTimeout(hide, tm.hideDelay);
34748         }
34749     };
34750     
34751     var onMove = function(e){
34752         if(disabled){
34753             return;
34754         }
34755         xy = e.getXY();
34756         xy[1] += 18;
34757         if(tm.trackMouse && ce){
34758             el.setXY(xy);
34759         }
34760     };
34761     
34762     var onDown = function(e){
34763         clearTimeout(showProc);
34764         clearTimeout(hideProc);
34765         if(!e.within(el)){
34766             if(tm.hideOnClick){
34767                 hide();
34768                 tm.disable();
34769                 tm.enable.defer(100, tm);
34770             }
34771         }
34772     };
34773     
34774     var getPad = function(){
34775         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34776     };
34777
34778     var show = function(o){
34779         if(disabled){
34780             return;
34781         }
34782         clearTimeout(dismissProc);
34783         ce = o;
34784         if(removeCls){ // in case manually hidden
34785             el.removeClass(removeCls);
34786             removeCls = null;
34787         }
34788         if(ce.cls){
34789             el.addClass(ce.cls);
34790             removeCls = ce.cls;
34791         }
34792         if(ce.title){
34793             tipTitle.update(ce.title);
34794             tipTitle.show();
34795         }else{
34796             tipTitle.update('');
34797             tipTitle.hide();
34798         }
34799         el.dom.style.width  = tm.maxWidth+'px';
34800         //tipBody.dom.style.width = '';
34801         tipBodyText.update(o.text);
34802         var p = getPad(), w = ce.width;
34803         if(!w){
34804             var td = tipBodyText.dom;
34805             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34806             if(aw > tm.maxWidth){
34807                 w = tm.maxWidth;
34808             }else if(aw < tm.minWidth){
34809                 w = tm.minWidth;
34810             }else{
34811                 w = aw;
34812             }
34813         }
34814         //tipBody.setWidth(w);
34815         el.setWidth(parseInt(w, 10) + p);
34816         if(ce.autoHide === false){
34817             close.setDisplayed(true);
34818             if(dd){
34819                 dd.unlock();
34820             }
34821         }else{
34822             close.setDisplayed(false);
34823             if(dd){
34824                 dd.lock();
34825             }
34826         }
34827         if(xy){
34828             el.avoidY = xy[1]-18;
34829             el.setXY(xy);
34830         }
34831         if(tm.animate){
34832             el.setOpacity(.1);
34833             el.setStyle("visibility", "visible");
34834             el.fadeIn({callback: afterShow});
34835         }else{
34836             afterShow();
34837         }
34838     };
34839     
34840     var afterShow = function(){
34841         if(ce){
34842             el.show();
34843             esc.enable();
34844             if(tm.autoDismiss && ce.autoHide !== false){
34845                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34846             }
34847         }
34848     };
34849     
34850     var hide = function(noanim){
34851         clearTimeout(dismissProc);
34852         clearTimeout(hideProc);
34853         ce = null;
34854         if(el.isVisible()){
34855             esc.disable();
34856             if(noanim !== true && tm.animate){
34857                 el.fadeOut({callback: afterHide});
34858             }else{
34859                 afterHide();
34860             } 
34861         }
34862     };
34863     
34864     var afterHide = function(){
34865         el.hide();
34866         if(removeCls){
34867             el.removeClass(removeCls);
34868             removeCls = null;
34869         }
34870     };
34871     
34872     return {
34873         /**
34874         * @cfg {Number} minWidth
34875         * The minimum width of the quick tip (defaults to 40)
34876         */
34877        minWidth : 40,
34878         /**
34879         * @cfg {Number} maxWidth
34880         * The maximum width of the quick tip (defaults to 300)
34881         */
34882        maxWidth : 300,
34883         /**
34884         * @cfg {Boolean} interceptTitles
34885         * True to automatically use the element's DOM title value if available (defaults to false)
34886         */
34887        interceptTitles : false,
34888         /**
34889         * @cfg {Boolean} trackMouse
34890         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34891         */
34892        trackMouse : false,
34893         /**
34894         * @cfg {Boolean} hideOnClick
34895         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34896         */
34897        hideOnClick : true,
34898         /**
34899         * @cfg {Number} showDelay
34900         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34901         */
34902        showDelay : 500,
34903         /**
34904         * @cfg {Number} hideDelay
34905         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34906         */
34907        hideDelay : 200,
34908         /**
34909         * @cfg {Boolean} autoHide
34910         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34911         * Used in conjunction with hideDelay.
34912         */
34913        autoHide : true,
34914         /**
34915         * @cfg {Boolean}
34916         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34917         * (defaults to true).  Used in conjunction with autoDismissDelay.
34918         */
34919        autoDismiss : true,
34920         /**
34921         * @cfg {Number}
34922         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34923         */
34924        autoDismissDelay : 5000,
34925        /**
34926         * @cfg {Boolean} animate
34927         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34928         */
34929        animate : false,
34930
34931        /**
34932         * @cfg {String} title
34933         * Title text to display (defaults to '').  This can be any valid HTML markup.
34934         */
34935         title: '',
34936        /**
34937         * @cfg {String} text
34938         * Body text to display (defaults to '').  This can be any valid HTML markup.
34939         */
34940         text : '',
34941        /**
34942         * @cfg {String} cls
34943         * A CSS class to apply to the base quick tip element (defaults to '').
34944         */
34945         cls : '',
34946        /**
34947         * @cfg {Number} width
34948         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34949         * minWidth or maxWidth.
34950         */
34951         width : null,
34952
34953     /**
34954      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34955      * or display QuickTips in a page.
34956      */
34957        init : function(){
34958           tm = Roo.QuickTips;
34959           cfg = tm.tagConfig;
34960           if(!inited){
34961               if(!Roo.isReady){ // allow calling of init() before onReady
34962                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34963                   return;
34964               }
34965               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34966               el.fxDefaults = {stopFx: true};
34967               // maximum custom styling
34968               //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>');
34969               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>');              
34970               tipTitle = el.child('h3');
34971               tipTitle.enableDisplayMode("block");
34972               tipBody = el.child('div.x-tip-bd');
34973               tipBodyText = el.child('div.x-tip-bd-inner');
34974               //bdLeft = el.child('div.x-tip-bd-left');
34975               //bdRight = el.child('div.x-tip-bd-right');
34976               close = el.child('div.x-tip-close');
34977               close.enableDisplayMode("block");
34978               close.on("click", hide);
34979               var d = Roo.get(document);
34980               d.on("mousedown", onDown);
34981               d.on("mouseover", onOver);
34982               d.on("mouseout", onOut);
34983               d.on("mousemove", onMove);
34984               esc = d.addKeyListener(27, hide);
34985               esc.disable();
34986               if(Roo.dd.DD){
34987                   dd = el.initDD("default", null, {
34988                       onDrag : function(){
34989                           el.sync();  
34990                       }
34991                   });
34992                   dd.setHandleElId(tipTitle.id);
34993                   dd.lock();
34994               }
34995               inited = true;
34996           }
34997           this.enable(); 
34998        },
34999
35000     /**
35001      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35002      * are supported:
35003      * <pre>
35004 Property    Type                   Description
35005 ----------  ---------------------  ------------------------------------------------------------------------
35006 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35007      * </ul>
35008      * @param {Object} config The config object
35009      */
35010        register : function(config){
35011            var cs = config instanceof Array ? config : arguments;
35012            for(var i = 0, len = cs.length; i < len; i++) {
35013                var c = cs[i];
35014                var target = c.target;
35015                if(target){
35016                    if(target instanceof Array){
35017                        for(var j = 0, jlen = target.length; j < jlen; j++){
35018                            tagEls[target[j]] = c;
35019                        }
35020                    }else{
35021                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35022                    }
35023                }
35024            }
35025        },
35026
35027     /**
35028      * Removes this quick tip from its element and destroys it.
35029      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35030      */
35031        unregister : function(el){
35032            delete tagEls[Roo.id(el)];
35033        },
35034
35035     /**
35036      * Enable this quick tip.
35037      */
35038        enable : function(){
35039            if(inited && disabled){
35040                locks.pop();
35041                if(locks.length < 1){
35042                    disabled = false;
35043                }
35044            }
35045        },
35046
35047     /**
35048      * Disable this quick tip.
35049      */
35050        disable : function(){
35051           disabled = true;
35052           clearTimeout(showProc);
35053           clearTimeout(hideProc);
35054           clearTimeout(dismissProc);
35055           if(ce){
35056               hide(true);
35057           }
35058           locks.push(1);
35059        },
35060
35061     /**
35062      * Returns true if the quick tip is enabled, else false.
35063      */
35064        isEnabled : function(){
35065             return !disabled;
35066        },
35067
35068         // private
35069        tagConfig : {
35070            namespace : "roo", // was ext?? this may break..
35071            alt_namespace : "ext",
35072            attribute : "qtip",
35073            width : "width",
35074            target : "target",
35075            title : "qtitle",
35076            hide : "hide",
35077            cls : "qclass"
35078        }
35079    };
35080 }();
35081
35082 // backwards compat
35083 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35084  * Based on:
35085  * Ext JS Library 1.1.1
35086  * Copyright(c) 2006-2007, Ext JS, LLC.
35087  *
35088  * Originally Released Under LGPL - original licence link has changed is not relivant.
35089  *
35090  * Fork - LGPL
35091  * <script type="text/javascript">
35092  */
35093  
35094
35095 /**
35096  * @class Roo.tree.TreePanel
35097  * @extends Roo.data.Tree
35098  * @cfg {Roo.tree.TreeNode} root The root node
35099  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35100  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35101  * @cfg {Boolean} enableDD true to enable drag and drop
35102  * @cfg {Boolean} enableDrag true to enable just drag
35103  * @cfg {Boolean} enableDrop true to enable just drop
35104  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35105  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35106  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35107  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35108  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35109  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35110  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35111  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35112  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35113  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35114  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35115  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35116  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35117  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35118  * @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>
35119  * @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>
35120  * 
35121  * @constructor
35122  * @param {String/HTMLElement/Element} el The container element
35123  * @param {Object} config
35124  */
35125 Roo.tree.TreePanel = function(el, config){
35126     var root = false;
35127     var loader = false;
35128     if (config.root) {
35129         root = config.root;
35130         delete config.root;
35131     }
35132     if (config.loader) {
35133         loader = config.loader;
35134         delete config.loader;
35135     }
35136     
35137     Roo.apply(this, config);
35138     Roo.tree.TreePanel.superclass.constructor.call(this);
35139     this.el = Roo.get(el);
35140     this.el.addClass('x-tree');
35141     //console.log(root);
35142     if (root) {
35143         this.setRootNode( Roo.factory(root, Roo.tree));
35144     }
35145     if (loader) {
35146         this.loader = Roo.factory(loader, Roo.tree);
35147     }
35148    /**
35149     * Read-only. The id of the container element becomes this TreePanel's id.
35150     */
35151     this.id = this.el.id;
35152     this.addEvents({
35153         /**
35154         * @event beforeload
35155         * Fires before a node is loaded, return false to cancel
35156         * @param {Node} node The node being loaded
35157         */
35158         "beforeload" : true,
35159         /**
35160         * @event load
35161         * Fires when a node is loaded
35162         * @param {Node} node The node that was loaded
35163         */
35164         "load" : true,
35165         /**
35166         * @event textchange
35167         * Fires when the text for a node is changed
35168         * @param {Node} node The node
35169         * @param {String} text The new text
35170         * @param {String} oldText The old text
35171         */
35172         "textchange" : true,
35173         /**
35174         * @event beforeexpand
35175         * Fires before a node is expanded, return false to cancel.
35176         * @param {Node} node The node
35177         * @param {Boolean} deep
35178         * @param {Boolean} anim
35179         */
35180         "beforeexpand" : true,
35181         /**
35182         * @event beforecollapse
35183         * Fires before a node is collapsed, return false to cancel.
35184         * @param {Node} node The node
35185         * @param {Boolean} deep
35186         * @param {Boolean} anim
35187         */
35188         "beforecollapse" : true,
35189         /**
35190         * @event expand
35191         * Fires when a node is expanded
35192         * @param {Node} node The node
35193         */
35194         "expand" : true,
35195         /**
35196         * @event disabledchange
35197         * Fires when the disabled status of a node changes
35198         * @param {Node} node The node
35199         * @param {Boolean} disabled
35200         */
35201         "disabledchange" : true,
35202         /**
35203         * @event collapse
35204         * Fires when a node is collapsed
35205         * @param {Node} node The node
35206         */
35207         "collapse" : true,
35208         /**
35209         * @event beforeclick
35210         * Fires before click processing on a node. Return false to cancel the default action.
35211         * @param {Node} node The node
35212         * @param {Roo.EventObject} e The event object
35213         */
35214         "beforeclick":true,
35215         /**
35216         * @event checkchange
35217         * Fires when a node with a checkbox's checked property changes
35218         * @param {Node} this This node
35219         * @param {Boolean} checked
35220         */
35221         "checkchange":true,
35222         /**
35223         * @event click
35224         * Fires when a node is clicked
35225         * @param {Node} node The node
35226         * @param {Roo.EventObject} e The event object
35227         */
35228         "click":true,
35229         /**
35230         * @event dblclick
35231         * Fires when a node is double clicked
35232         * @param {Node} node The node
35233         * @param {Roo.EventObject} e The event object
35234         */
35235         "dblclick":true,
35236         /**
35237         * @event contextmenu
35238         * Fires when a node is right clicked
35239         * @param {Node} node The node
35240         * @param {Roo.EventObject} e The event object
35241         */
35242         "contextmenu":true,
35243         /**
35244         * @event beforechildrenrendered
35245         * Fires right before the child nodes for a node are rendered
35246         * @param {Node} node The node
35247         */
35248         "beforechildrenrendered":true,
35249         /**
35250         * @event startdrag
35251         * Fires when a node starts being dragged
35252         * @param {Roo.tree.TreePanel} this
35253         * @param {Roo.tree.TreeNode} node
35254         * @param {event} e The raw browser event
35255         */ 
35256        "startdrag" : true,
35257        /**
35258         * @event enddrag
35259         * Fires when a drag operation is complete
35260         * @param {Roo.tree.TreePanel} this
35261         * @param {Roo.tree.TreeNode} node
35262         * @param {event} e The raw browser event
35263         */
35264        "enddrag" : true,
35265        /**
35266         * @event dragdrop
35267         * Fires when a dragged node is dropped on a valid DD target
35268         * @param {Roo.tree.TreePanel} this
35269         * @param {Roo.tree.TreeNode} node
35270         * @param {DD} dd The dd it was dropped on
35271         * @param {event} e The raw browser event
35272         */
35273        "dragdrop" : true,
35274        /**
35275         * @event beforenodedrop
35276         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35277         * passed to handlers has the following properties:<br />
35278         * <ul style="padding:5px;padding-left:16px;">
35279         * <li>tree - The TreePanel</li>
35280         * <li>target - The node being targeted for the drop</li>
35281         * <li>data - The drag data from the drag source</li>
35282         * <li>point - The point of the drop - append, above or below</li>
35283         * <li>source - The drag source</li>
35284         * <li>rawEvent - Raw mouse event</li>
35285         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35286         * to be inserted by setting them on this object.</li>
35287         * <li>cancel - Set this to true to cancel the drop.</li>
35288         * </ul>
35289         * @param {Object} dropEvent
35290         */
35291        "beforenodedrop" : true,
35292        /**
35293         * @event nodedrop
35294         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35295         * passed to handlers has the following properties:<br />
35296         * <ul style="padding:5px;padding-left:16px;">
35297         * <li>tree - The TreePanel</li>
35298         * <li>target - The node being targeted for the drop</li>
35299         * <li>data - The drag data from the drag source</li>
35300         * <li>point - The point of the drop - append, above or below</li>
35301         * <li>source - The drag source</li>
35302         * <li>rawEvent - Raw mouse event</li>
35303         * <li>dropNode - Dropped node(s).</li>
35304         * </ul>
35305         * @param {Object} dropEvent
35306         */
35307        "nodedrop" : true,
35308         /**
35309         * @event nodedragover
35310         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35311         * passed to handlers has the following properties:<br />
35312         * <ul style="padding:5px;padding-left:16px;">
35313         * <li>tree - The TreePanel</li>
35314         * <li>target - The node being targeted for the drop</li>
35315         * <li>data - The drag data from the drag source</li>
35316         * <li>point - The point of the drop - append, above or below</li>
35317         * <li>source - The drag source</li>
35318         * <li>rawEvent - Raw mouse event</li>
35319         * <li>dropNode - Drop node(s) provided by the source.</li>
35320         * <li>cancel - Set this to true to signal drop not allowed.</li>
35321         * </ul>
35322         * @param {Object} dragOverEvent
35323         */
35324        "nodedragover" : true,
35325        /**
35326         * @event appendnode
35327         * Fires when append node to the tree
35328         * @param {Roo.tree.TreePanel} this
35329         * @param {Roo.tree.TreeNode} node
35330         * @param {Number} index The index of the newly appended node
35331         */
35332        "appendnode" : true
35333         
35334     });
35335     if(this.singleExpand){
35336        this.on("beforeexpand", this.restrictExpand, this);
35337     }
35338     if (this.editor) {
35339         this.editor.tree = this;
35340         this.editor = Roo.factory(this.editor, Roo.tree);
35341     }
35342     
35343     if (this.selModel) {
35344         this.selModel = Roo.factory(this.selModel, Roo.tree);
35345     }
35346    
35347 };
35348 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35349     rootVisible : true,
35350     animate: Roo.enableFx,
35351     lines : true,
35352     enableDD : false,
35353     hlDrop : Roo.enableFx,
35354   
35355     renderer: false,
35356     
35357     rendererTip: false,
35358     // private
35359     restrictExpand : function(node){
35360         var p = node.parentNode;
35361         if(p){
35362             if(p.expandedChild && p.expandedChild.parentNode == p){
35363                 p.expandedChild.collapse();
35364             }
35365             p.expandedChild = node;
35366         }
35367     },
35368
35369     // private override
35370     setRootNode : function(node){
35371         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35372         if(!this.rootVisible){
35373             node.ui = new Roo.tree.RootTreeNodeUI(node);
35374         }
35375         return node;
35376     },
35377
35378     /**
35379      * Returns the container element for this TreePanel
35380      */
35381     getEl : function(){
35382         return this.el;
35383     },
35384
35385     /**
35386      * Returns the default TreeLoader for this TreePanel
35387      */
35388     getLoader : function(){
35389         return this.loader;
35390     },
35391
35392     /**
35393      * Expand all nodes
35394      */
35395     expandAll : function(){
35396         this.root.expand(true);
35397     },
35398
35399     /**
35400      * Collapse all nodes
35401      */
35402     collapseAll : function(){
35403         this.root.collapse(true);
35404     },
35405
35406     /**
35407      * Returns the selection model used by this TreePanel
35408      */
35409     getSelectionModel : function(){
35410         if(!this.selModel){
35411             this.selModel = new Roo.tree.DefaultSelectionModel();
35412         }
35413         return this.selModel;
35414     },
35415
35416     /**
35417      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35418      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35419      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35420      * @return {Array}
35421      */
35422     getChecked : function(a, startNode){
35423         startNode = startNode || this.root;
35424         var r = [];
35425         var f = function(){
35426             if(this.attributes.checked){
35427                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35428             }
35429         }
35430         startNode.cascade(f);
35431         return r;
35432     },
35433
35434     /**
35435      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35436      * @param {String} path
35437      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35438      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35439      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35440      */
35441     expandPath : function(path, attr, callback){
35442         attr = attr || "id";
35443         var keys = path.split(this.pathSeparator);
35444         var curNode = this.root;
35445         if(curNode.attributes[attr] != keys[1]){ // invalid root
35446             if(callback){
35447                 callback(false, null);
35448             }
35449             return;
35450         }
35451         var index = 1;
35452         var f = function(){
35453             if(++index == keys.length){
35454                 if(callback){
35455                     callback(true, curNode);
35456                 }
35457                 return;
35458             }
35459             var c = curNode.findChild(attr, keys[index]);
35460             if(!c){
35461                 if(callback){
35462                     callback(false, curNode);
35463                 }
35464                 return;
35465             }
35466             curNode = c;
35467             c.expand(false, false, f);
35468         };
35469         curNode.expand(false, false, f);
35470     },
35471
35472     /**
35473      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35474      * @param {String} path
35475      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35476      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35477      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35478      */
35479     selectPath : function(path, attr, callback){
35480         attr = attr || "id";
35481         var keys = path.split(this.pathSeparator);
35482         var v = keys.pop();
35483         if(keys.length > 0){
35484             var f = function(success, node){
35485                 if(success && node){
35486                     var n = node.findChild(attr, v);
35487                     if(n){
35488                         n.select();
35489                         if(callback){
35490                             callback(true, n);
35491                         }
35492                     }else if(callback){
35493                         callback(false, n);
35494                     }
35495                 }else{
35496                     if(callback){
35497                         callback(false, n);
35498                     }
35499                 }
35500             };
35501             this.expandPath(keys.join(this.pathSeparator), attr, f);
35502         }else{
35503             this.root.select();
35504             if(callback){
35505                 callback(true, this.root);
35506             }
35507         }
35508     },
35509
35510     getTreeEl : function(){
35511         return this.el;
35512     },
35513
35514     /**
35515      * Trigger rendering of this TreePanel
35516      */
35517     render : function(){
35518         if (this.innerCt) {
35519             return this; // stop it rendering more than once!!
35520         }
35521         
35522         this.innerCt = this.el.createChild({tag:"ul",
35523                cls:"x-tree-root-ct " +
35524                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35525
35526         if(this.containerScroll){
35527             Roo.dd.ScrollManager.register(this.el);
35528         }
35529         if((this.enableDD || this.enableDrop) && !this.dropZone){
35530            /**
35531             * The dropZone used by this tree if drop is enabled
35532             * @type Roo.tree.TreeDropZone
35533             */
35534              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35535                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35536            });
35537         }
35538         if((this.enableDD || this.enableDrag) && !this.dragZone){
35539            /**
35540             * The dragZone used by this tree if drag is enabled
35541             * @type Roo.tree.TreeDragZone
35542             */
35543             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35544                ddGroup: this.ddGroup || "TreeDD",
35545                scroll: this.ddScroll
35546            });
35547         }
35548         this.getSelectionModel().init(this);
35549         if (!this.root) {
35550             Roo.log("ROOT not set in tree");
35551             return this;
35552         }
35553         this.root.render();
35554         if(!this.rootVisible){
35555             this.root.renderChildren();
35556         }
35557         return this;
35558     }
35559 });/*
35560  * Based on:
35561  * Ext JS Library 1.1.1
35562  * Copyright(c) 2006-2007, Ext JS, LLC.
35563  *
35564  * Originally Released Under LGPL - original licence link has changed is not relivant.
35565  *
35566  * Fork - LGPL
35567  * <script type="text/javascript">
35568  */
35569  
35570
35571 /**
35572  * @class Roo.tree.DefaultSelectionModel
35573  * @extends Roo.util.Observable
35574  * The default single selection for a TreePanel.
35575  * @param {Object} cfg Configuration
35576  */
35577 Roo.tree.DefaultSelectionModel = function(cfg){
35578    this.selNode = null;
35579    
35580    
35581    
35582    this.addEvents({
35583        /**
35584         * @event selectionchange
35585         * Fires when the selected node changes
35586         * @param {DefaultSelectionModel} this
35587         * @param {TreeNode} node the new selection
35588         */
35589        "selectionchange" : true,
35590
35591        /**
35592         * @event beforeselect
35593         * Fires before the selected node changes, return false to cancel the change
35594         * @param {DefaultSelectionModel} this
35595         * @param {TreeNode} node the new selection
35596         * @param {TreeNode} node the old selection
35597         */
35598        "beforeselect" : true
35599    });
35600    
35601     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35602 };
35603
35604 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35605     init : function(tree){
35606         this.tree = tree;
35607         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35608         tree.on("click", this.onNodeClick, this);
35609     },
35610     
35611     onNodeClick : function(node, e){
35612         if (e.ctrlKey && this.selNode == node)  {
35613             this.unselect(node);
35614             return;
35615         }
35616         this.select(node);
35617     },
35618     
35619     /**
35620      * Select a node.
35621      * @param {TreeNode} node The node to select
35622      * @return {TreeNode} The selected node
35623      */
35624     select : function(node){
35625         var last = this.selNode;
35626         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35627             if(last){
35628                 last.ui.onSelectedChange(false);
35629             }
35630             this.selNode = node;
35631             node.ui.onSelectedChange(true);
35632             this.fireEvent("selectionchange", this, node, last);
35633         }
35634         return node;
35635     },
35636     
35637     /**
35638      * Deselect a node.
35639      * @param {TreeNode} node The node to unselect
35640      */
35641     unselect : function(node){
35642         if(this.selNode == node){
35643             this.clearSelections();
35644         }    
35645     },
35646     
35647     /**
35648      * Clear all selections
35649      */
35650     clearSelections : function(){
35651         var n = this.selNode;
35652         if(n){
35653             n.ui.onSelectedChange(false);
35654             this.selNode = null;
35655             this.fireEvent("selectionchange", this, null);
35656         }
35657         return n;
35658     },
35659     
35660     /**
35661      * Get the selected node
35662      * @return {TreeNode} The selected node
35663      */
35664     getSelectedNode : function(){
35665         return this.selNode;    
35666     },
35667     
35668     /**
35669      * Returns true if the node is selected
35670      * @param {TreeNode} node The node to check
35671      * @return {Boolean}
35672      */
35673     isSelected : function(node){
35674         return this.selNode == node;  
35675     },
35676
35677     /**
35678      * Selects the node above the selected node in the tree, intelligently walking the nodes
35679      * @return TreeNode The new selection
35680      */
35681     selectPrevious : function(){
35682         var s = this.selNode || this.lastSelNode;
35683         if(!s){
35684             return null;
35685         }
35686         var ps = s.previousSibling;
35687         if(ps){
35688             if(!ps.isExpanded() || ps.childNodes.length < 1){
35689                 return this.select(ps);
35690             } else{
35691                 var lc = ps.lastChild;
35692                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35693                     lc = lc.lastChild;
35694                 }
35695                 return this.select(lc);
35696             }
35697         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35698             return this.select(s.parentNode);
35699         }
35700         return null;
35701     },
35702
35703     /**
35704      * Selects the node above the selected node in the tree, intelligently walking the nodes
35705      * @return TreeNode The new selection
35706      */
35707     selectNext : function(){
35708         var s = this.selNode || this.lastSelNode;
35709         if(!s){
35710             return null;
35711         }
35712         if(s.firstChild && s.isExpanded()){
35713              return this.select(s.firstChild);
35714          }else if(s.nextSibling){
35715              return this.select(s.nextSibling);
35716          }else if(s.parentNode){
35717             var newS = null;
35718             s.parentNode.bubble(function(){
35719                 if(this.nextSibling){
35720                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35721                     return false;
35722                 }
35723             });
35724             return newS;
35725          }
35726         return null;
35727     },
35728
35729     onKeyDown : function(e){
35730         var s = this.selNode || this.lastSelNode;
35731         // undesirable, but required
35732         var sm = this;
35733         if(!s){
35734             return;
35735         }
35736         var k = e.getKey();
35737         switch(k){
35738              case e.DOWN:
35739                  e.stopEvent();
35740                  this.selectNext();
35741              break;
35742              case e.UP:
35743                  e.stopEvent();
35744                  this.selectPrevious();
35745              break;
35746              case e.RIGHT:
35747                  e.preventDefault();
35748                  if(s.hasChildNodes()){
35749                      if(!s.isExpanded()){
35750                          s.expand();
35751                      }else if(s.firstChild){
35752                          this.select(s.firstChild, e);
35753                      }
35754                  }
35755              break;
35756              case e.LEFT:
35757                  e.preventDefault();
35758                  if(s.hasChildNodes() && s.isExpanded()){
35759                      s.collapse();
35760                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35761                      this.select(s.parentNode, e);
35762                  }
35763              break;
35764         };
35765     }
35766 });
35767
35768 /**
35769  * @class Roo.tree.MultiSelectionModel
35770  * @extends Roo.util.Observable
35771  * Multi selection for a TreePanel.
35772  * @param {Object} cfg Configuration
35773  */
35774 Roo.tree.MultiSelectionModel = function(){
35775    this.selNodes = [];
35776    this.selMap = {};
35777    this.addEvents({
35778        /**
35779         * @event selectionchange
35780         * Fires when the selected nodes change
35781         * @param {MultiSelectionModel} this
35782         * @param {Array} nodes Array of the selected nodes
35783         */
35784        "selectionchange" : true
35785    });
35786    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35787    
35788 };
35789
35790 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35791     init : function(tree){
35792         this.tree = tree;
35793         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35794         tree.on("click", this.onNodeClick, this);
35795     },
35796     
35797     onNodeClick : function(node, e){
35798         this.select(node, e, e.ctrlKey);
35799     },
35800     
35801     /**
35802      * Select a node.
35803      * @param {TreeNode} node The node to select
35804      * @param {EventObject} e (optional) An event associated with the selection
35805      * @param {Boolean} keepExisting True to retain existing selections
35806      * @return {TreeNode} The selected node
35807      */
35808     select : function(node, e, keepExisting){
35809         if(keepExisting !== true){
35810             this.clearSelections(true);
35811         }
35812         if(this.isSelected(node)){
35813             this.lastSelNode = node;
35814             return node;
35815         }
35816         this.selNodes.push(node);
35817         this.selMap[node.id] = node;
35818         this.lastSelNode = node;
35819         node.ui.onSelectedChange(true);
35820         this.fireEvent("selectionchange", this, this.selNodes);
35821         return node;
35822     },
35823     
35824     /**
35825      * Deselect a node.
35826      * @param {TreeNode} node The node to unselect
35827      */
35828     unselect : function(node){
35829         if(this.selMap[node.id]){
35830             node.ui.onSelectedChange(false);
35831             var sn = this.selNodes;
35832             var index = -1;
35833             if(sn.indexOf){
35834                 index = sn.indexOf(node);
35835             }else{
35836                 for(var i = 0, len = sn.length; i < len; i++){
35837                     if(sn[i] == node){
35838                         index = i;
35839                         break;
35840                     }
35841                 }
35842             }
35843             if(index != -1){
35844                 this.selNodes.splice(index, 1);
35845             }
35846             delete this.selMap[node.id];
35847             this.fireEvent("selectionchange", this, this.selNodes);
35848         }
35849     },
35850     
35851     /**
35852      * Clear all selections
35853      */
35854     clearSelections : function(suppressEvent){
35855         var sn = this.selNodes;
35856         if(sn.length > 0){
35857             for(var i = 0, len = sn.length; i < len; i++){
35858                 sn[i].ui.onSelectedChange(false);
35859             }
35860             this.selNodes = [];
35861             this.selMap = {};
35862             if(suppressEvent !== true){
35863                 this.fireEvent("selectionchange", this, this.selNodes);
35864             }
35865         }
35866     },
35867     
35868     /**
35869      * Returns true if the node is selected
35870      * @param {TreeNode} node The node to check
35871      * @return {Boolean}
35872      */
35873     isSelected : function(node){
35874         return this.selMap[node.id] ? true : false;  
35875     },
35876     
35877     /**
35878      * Returns an array of the selected nodes
35879      * @return {Array}
35880      */
35881     getSelectedNodes : function(){
35882         return this.selNodes;    
35883     },
35884
35885     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35886
35887     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35888
35889     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35890 });/*
35891  * Based on:
35892  * Ext JS Library 1.1.1
35893  * Copyright(c) 2006-2007, Ext JS, LLC.
35894  *
35895  * Originally Released Under LGPL - original licence link has changed is not relivant.
35896  *
35897  * Fork - LGPL
35898  * <script type="text/javascript">
35899  */
35900  
35901 /**
35902  * @class Roo.tree.TreeNode
35903  * @extends Roo.data.Node
35904  * @cfg {String} text The text for this node
35905  * @cfg {Boolean} expanded true to start the node expanded
35906  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35907  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35908  * @cfg {Boolean} disabled true to start the node disabled
35909  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35910  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35911  * @cfg {String} cls A css class to be added to the node
35912  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35913  * @cfg {String} href URL of the link used for the node (defaults to #)
35914  * @cfg {String} hrefTarget target frame for the link
35915  * @cfg {String} qtip An Ext QuickTip for the node
35916  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35917  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35918  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35919  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35920  * (defaults to undefined with no checkbox rendered)
35921  * @constructor
35922  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35923  */
35924 Roo.tree.TreeNode = function(attributes){
35925     attributes = attributes || {};
35926     if(typeof attributes == "string"){
35927         attributes = {text: attributes};
35928     }
35929     this.childrenRendered = false;
35930     this.rendered = false;
35931     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35932     this.expanded = attributes.expanded === true;
35933     this.isTarget = attributes.isTarget !== false;
35934     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35935     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35936
35937     /**
35938      * Read-only. The text for this node. To change it use setText().
35939      * @type String
35940      */
35941     this.text = attributes.text;
35942     /**
35943      * True if this node is disabled.
35944      * @type Boolean
35945      */
35946     this.disabled = attributes.disabled === true;
35947
35948     this.addEvents({
35949         /**
35950         * @event textchange
35951         * Fires when the text for this node is changed
35952         * @param {Node} this This node
35953         * @param {String} text The new text
35954         * @param {String} oldText The old text
35955         */
35956         "textchange" : true,
35957         /**
35958         * @event beforeexpand
35959         * Fires before this node is expanded, return false to cancel.
35960         * @param {Node} this This node
35961         * @param {Boolean} deep
35962         * @param {Boolean} anim
35963         */
35964         "beforeexpand" : true,
35965         /**
35966         * @event beforecollapse
35967         * Fires before this node is collapsed, return false to cancel.
35968         * @param {Node} this This node
35969         * @param {Boolean} deep
35970         * @param {Boolean} anim
35971         */
35972         "beforecollapse" : true,
35973         /**
35974         * @event expand
35975         * Fires when this node is expanded
35976         * @param {Node} this This node
35977         */
35978         "expand" : true,
35979         /**
35980         * @event disabledchange
35981         * Fires when the disabled status of this node changes
35982         * @param {Node} this This node
35983         * @param {Boolean} disabled
35984         */
35985         "disabledchange" : true,
35986         /**
35987         * @event collapse
35988         * Fires when this node is collapsed
35989         * @param {Node} this This node
35990         */
35991         "collapse" : true,
35992         /**
35993         * @event beforeclick
35994         * Fires before click processing. Return false to cancel the default action.
35995         * @param {Node} this This node
35996         * @param {Roo.EventObject} e The event object
35997         */
35998         "beforeclick":true,
35999         /**
36000         * @event checkchange
36001         * Fires when a node with a checkbox's checked property changes
36002         * @param {Node} this This node
36003         * @param {Boolean} checked
36004         */
36005         "checkchange":true,
36006         /**
36007         * @event click
36008         * Fires when this node is clicked
36009         * @param {Node} this This node
36010         * @param {Roo.EventObject} e The event object
36011         */
36012         "click":true,
36013         /**
36014         * @event dblclick
36015         * Fires when this node is double clicked
36016         * @param {Node} this This node
36017         * @param {Roo.EventObject} e The event object
36018         */
36019         "dblclick":true,
36020         /**
36021         * @event contextmenu
36022         * Fires when this node is right clicked
36023         * @param {Node} this This node
36024         * @param {Roo.EventObject} e The event object
36025         */
36026         "contextmenu":true,
36027         /**
36028         * @event beforechildrenrendered
36029         * Fires right before the child nodes for this node are rendered
36030         * @param {Node} this This node
36031         */
36032         "beforechildrenrendered":true
36033     });
36034
36035     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36036
36037     /**
36038      * Read-only. The UI for this node
36039      * @type TreeNodeUI
36040      */
36041     this.ui = new uiClass(this);
36042     
36043     // finally support items[]
36044     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36045         return;
36046     }
36047     
36048     
36049     Roo.each(this.attributes.items, function(c) {
36050         this.appendChild(Roo.factory(c,Roo.Tree));
36051     }, this);
36052     delete this.attributes.items;
36053     
36054     
36055     
36056 };
36057 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36058     preventHScroll: true,
36059     /**
36060      * Returns true if this node is expanded
36061      * @return {Boolean}
36062      */
36063     isExpanded : function(){
36064         return this.expanded;
36065     },
36066
36067     /**
36068      * Returns the UI object for this node
36069      * @return {TreeNodeUI}
36070      */
36071     getUI : function(){
36072         return this.ui;
36073     },
36074
36075     // private override
36076     setFirstChild : function(node){
36077         var of = this.firstChild;
36078         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36079         if(this.childrenRendered && of && node != of){
36080             of.renderIndent(true, true);
36081         }
36082         if(this.rendered){
36083             this.renderIndent(true, true);
36084         }
36085     },
36086
36087     // private override
36088     setLastChild : function(node){
36089         var ol = this.lastChild;
36090         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36091         if(this.childrenRendered && ol && node != ol){
36092             ol.renderIndent(true, true);
36093         }
36094         if(this.rendered){
36095             this.renderIndent(true, true);
36096         }
36097     },
36098
36099     // these methods are overridden to provide lazy rendering support
36100     // private override
36101     appendChild : function()
36102     {
36103         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36104         if(node && this.childrenRendered){
36105             node.render();
36106         }
36107         this.ui.updateExpandIcon();
36108         return node;
36109     },
36110
36111     // private override
36112     removeChild : function(node){
36113         this.ownerTree.getSelectionModel().unselect(node);
36114         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36115         // if it's been rendered remove dom node
36116         if(this.childrenRendered){
36117             node.ui.remove();
36118         }
36119         if(this.childNodes.length < 1){
36120             this.collapse(false, false);
36121         }else{
36122             this.ui.updateExpandIcon();
36123         }
36124         if(!this.firstChild) {
36125             this.childrenRendered = false;
36126         }
36127         return node;
36128     },
36129
36130     // private override
36131     insertBefore : function(node, refNode){
36132         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36133         if(newNode && refNode && this.childrenRendered){
36134             node.render();
36135         }
36136         this.ui.updateExpandIcon();
36137         return newNode;
36138     },
36139
36140     /**
36141      * Sets the text for this node
36142      * @param {String} text
36143      */
36144     setText : function(text){
36145         var oldText = this.text;
36146         this.text = text;
36147         this.attributes.text = text;
36148         if(this.rendered){ // event without subscribing
36149             this.ui.onTextChange(this, text, oldText);
36150         }
36151         this.fireEvent("textchange", this, text, oldText);
36152     },
36153
36154     /**
36155      * Triggers selection of this node
36156      */
36157     select : function(){
36158         this.getOwnerTree().getSelectionModel().select(this);
36159     },
36160
36161     /**
36162      * Triggers deselection of this node
36163      */
36164     unselect : function(){
36165         this.getOwnerTree().getSelectionModel().unselect(this);
36166     },
36167
36168     /**
36169      * Returns true if this node is selected
36170      * @return {Boolean}
36171      */
36172     isSelected : function(){
36173         return this.getOwnerTree().getSelectionModel().isSelected(this);
36174     },
36175
36176     /**
36177      * Expand this node.
36178      * @param {Boolean} deep (optional) True to expand all children as well
36179      * @param {Boolean} anim (optional) false to cancel the default animation
36180      * @param {Function} callback (optional) A callback to be called when
36181      * expanding this node completes (does not wait for deep expand to complete).
36182      * Called with 1 parameter, this node.
36183      */
36184     expand : function(deep, anim, callback){
36185         if(!this.expanded){
36186             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36187                 return;
36188             }
36189             if(!this.childrenRendered){
36190                 this.renderChildren();
36191             }
36192             this.expanded = true;
36193             
36194             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36195                 this.ui.animExpand(function(){
36196                     this.fireEvent("expand", this);
36197                     if(typeof callback == "function"){
36198                         callback(this);
36199                     }
36200                     if(deep === true){
36201                         this.expandChildNodes(true);
36202                     }
36203                 }.createDelegate(this));
36204                 return;
36205             }else{
36206                 this.ui.expand();
36207                 this.fireEvent("expand", this);
36208                 if(typeof callback == "function"){
36209                     callback(this);
36210                 }
36211             }
36212         }else{
36213            if(typeof callback == "function"){
36214                callback(this);
36215            }
36216         }
36217         if(deep === true){
36218             this.expandChildNodes(true);
36219         }
36220     },
36221
36222     isHiddenRoot : function(){
36223         return this.isRoot && !this.getOwnerTree().rootVisible;
36224     },
36225
36226     /**
36227      * Collapse this node.
36228      * @param {Boolean} deep (optional) True to collapse all children as well
36229      * @param {Boolean} anim (optional) false to cancel the default animation
36230      */
36231     collapse : function(deep, anim){
36232         if(this.expanded && !this.isHiddenRoot()){
36233             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36234                 return;
36235             }
36236             this.expanded = false;
36237             if((this.getOwnerTree().animate && anim !== false) || anim){
36238                 this.ui.animCollapse(function(){
36239                     this.fireEvent("collapse", this);
36240                     if(deep === true){
36241                         this.collapseChildNodes(true);
36242                     }
36243                 }.createDelegate(this));
36244                 return;
36245             }else{
36246                 this.ui.collapse();
36247                 this.fireEvent("collapse", this);
36248             }
36249         }
36250         if(deep === true){
36251             var cs = this.childNodes;
36252             for(var i = 0, len = cs.length; i < len; i++) {
36253                 cs[i].collapse(true, false);
36254             }
36255         }
36256     },
36257
36258     // private
36259     delayedExpand : function(delay){
36260         if(!this.expandProcId){
36261             this.expandProcId = this.expand.defer(delay, this);
36262         }
36263     },
36264
36265     // private
36266     cancelExpand : function(){
36267         if(this.expandProcId){
36268             clearTimeout(this.expandProcId);
36269         }
36270         this.expandProcId = false;
36271     },
36272
36273     /**
36274      * Toggles expanded/collapsed state of the node
36275      */
36276     toggle : function(){
36277         if(this.expanded){
36278             this.collapse();
36279         }else{
36280             this.expand();
36281         }
36282     },
36283
36284     /**
36285      * Ensures all parent nodes are expanded
36286      */
36287     ensureVisible : function(callback){
36288         var tree = this.getOwnerTree();
36289         tree.expandPath(this.parentNode.getPath(), false, function(){
36290             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36291             Roo.callback(callback);
36292         }.createDelegate(this));
36293     },
36294
36295     /**
36296      * Expand all child nodes
36297      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36298      */
36299     expandChildNodes : function(deep){
36300         var cs = this.childNodes;
36301         for(var i = 0, len = cs.length; i < len; i++) {
36302                 cs[i].expand(deep);
36303         }
36304     },
36305
36306     /**
36307      * Collapse all child nodes
36308      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36309      */
36310     collapseChildNodes : function(deep){
36311         var cs = this.childNodes;
36312         for(var i = 0, len = cs.length; i < len; i++) {
36313                 cs[i].collapse(deep);
36314         }
36315     },
36316
36317     /**
36318      * Disables this node
36319      */
36320     disable : function(){
36321         this.disabled = true;
36322         this.unselect();
36323         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36324             this.ui.onDisableChange(this, true);
36325         }
36326         this.fireEvent("disabledchange", this, true);
36327     },
36328
36329     /**
36330      * Enables this node
36331      */
36332     enable : function(){
36333         this.disabled = false;
36334         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36335             this.ui.onDisableChange(this, false);
36336         }
36337         this.fireEvent("disabledchange", this, false);
36338     },
36339
36340     // private
36341     renderChildren : function(suppressEvent){
36342         if(suppressEvent !== false){
36343             this.fireEvent("beforechildrenrendered", this);
36344         }
36345         var cs = this.childNodes;
36346         for(var i = 0, len = cs.length; i < len; i++){
36347             cs[i].render(true);
36348         }
36349         this.childrenRendered = true;
36350     },
36351
36352     // private
36353     sort : function(fn, scope){
36354         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36355         if(this.childrenRendered){
36356             var cs = this.childNodes;
36357             for(var i = 0, len = cs.length; i < len; i++){
36358                 cs[i].render(true);
36359             }
36360         }
36361     },
36362
36363     // private
36364     render : function(bulkRender){
36365         this.ui.render(bulkRender);
36366         if(!this.rendered){
36367             this.rendered = true;
36368             if(this.expanded){
36369                 this.expanded = false;
36370                 this.expand(false, false);
36371             }
36372         }
36373     },
36374
36375     // private
36376     renderIndent : function(deep, refresh){
36377         if(refresh){
36378             this.ui.childIndent = null;
36379         }
36380         this.ui.renderIndent();
36381         if(deep === true && this.childrenRendered){
36382             var cs = this.childNodes;
36383             for(var i = 0, len = cs.length; i < len; i++){
36384                 cs[i].renderIndent(true, refresh);
36385             }
36386         }
36387     }
36388 });/*
36389  * Based on:
36390  * Ext JS Library 1.1.1
36391  * Copyright(c) 2006-2007, Ext JS, LLC.
36392  *
36393  * Originally Released Under LGPL - original licence link has changed is not relivant.
36394  *
36395  * Fork - LGPL
36396  * <script type="text/javascript">
36397  */
36398  
36399 /**
36400  * @class Roo.tree.AsyncTreeNode
36401  * @extends Roo.tree.TreeNode
36402  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36403  * @constructor
36404  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36405  */
36406  Roo.tree.AsyncTreeNode = function(config){
36407     this.loaded = false;
36408     this.loading = false;
36409     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36410     /**
36411     * @event beforeload
36412     * Fires before this node is loaded, return false to cancel
36413     * @param {Node} this This node
36414     */
36415     this.addEvents({'beforeload':true, 'load': true});
36416     /**
36417     * @event load
36418     * Fires when this node is loaded
36419     * @param {Node} this This node
36420     */
36421     /**
36422      * The loader used by this node (defaults to using the tree's defined loader)
36423      * @type TreeLoader
36424      * @property loader
36425      */
36426 };
36427 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36428     expand : function(deep, anim, callback){
36429         if(this.loading){ // if an async load is already running, waiting til it's done
36430             var timer;
36431             var f = function(){
36432                 if(!this.loading){ // done loading
36433                     clearInterval(timer);
36434                     this.expand(deep, anim, callback);
36435                 }
36436             }.createDelegate(this);
36437             timer = setInterval(f, 200);
36438             return;
36439         }
36440         if(!this.loaded){
36441             if(this.fireEvent("beforeload", this) === false){
36442                 return;
36443             }
36444             this.loading = true;
36445             this.ui.beforeLoad(this);
36446             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36447             if(loader){
36448                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36449                 return;
36450             }
36451         }
36452         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36453     },
36454     
36455     /**
36456      * Returns true if this node is currently loading
36457      * @return {Boolean}
36458      */
36459     isLoading : function(){
36460         return this.loading;  
36461     },
36462     
36463     loadComplete : function(deep, anim, callback){
36464         this.loading = false;
36465         this.loaded = true;
36466         this.ui.afterLoad(this);
36467         this.fireEvent("load", this);
36468         this.expand(deep, anim, callback);
36469     },
36470     
36471     /**
36472      * Returns true if this node has been loaded
36473      * @return {Boolean}
36474      */
36475     isLoaded : function(){
36476         return this.loaded;
36477     },
36478     
36479     hasChildNodes : function(){
36480         if(!this.isLeaf() && !this.loaded){
36481             return true;
36482         }else{
36483             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36484         }
36485     },
36486
36487     /**
36488      * Trigger a reload for this node
36489      * @param {Function} callback
36490      */
36491     reload : function(callback){
36492         this.collapse(false, false);
36493         while(this.firstChild){
36494             this.removeChild(this.firstChild);
36495         }
36496         this.childrenRendered = false;
36497         this.loaded = false;
36498         if(this.isHiddenRoot()){
36499             this.expanded = false;
36500         }
36501         this.expand(false, false, callback);
36502     }
36503 });/*
36504  * Based on:
36505  * Ext JS Library 1.1.1
36506  * Copyright(c) 2006-2007, Ext JS, LLC.
36507  *
36508  * Originally Released Under LGPL - original licence link has changed is not relivant.
36509  *
36510  * Fork - LGPL
36511  * <script type="text/javascript">
36512  */
36513  
36514 /**
36515  * @class Roo.tree.TreeNodeUI
36516  * @constructor
36517  * @param {Object} node The node to render
36518  * The TreeNode UI implementation is separate from the
36519  * tree implementation. Unless you are customizing the tree UI,
36520  * you should never have to use this directly.
36521  */
36522 Roo.tree.TreeNodeUI = function(node){
36523     this.node = node;
36524     this.rendered = false;
36525     this.animating = false;
36526     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36527 };
36528
36529 Roo.tree.TreeNodeUI.prototype = {
36530     removeChild : function(node){
36531         if(this.rendered){
36532             this.ctNode.removeChild(node.ui.getEl());
36533         }
36534     },
36535
36536     beforeLoad : function(){
36537          this.addClass("x-tree-node-loading");
36538     },
36539
36540     afterLoad : function(){
36541          this.removeClass("x-tree-node-loading");
36542     },
36543
36544     onTextChange : function(node, text, oldText){
36545         if(this.rendered){
36546             this.textNode.innerHTML = text;
36547         }
36548     },
36549
36550     onDisableChange : function(node, state){
36551         this.disabled = state;
36552         if(state){
36553             this.addClass("x-tree-node-disabled");
36554         }else{
36555             this.removeClass("x-tree-node-disabled");
36556         }
36557     },
36558
36559     onSelectedChange : function(state){
36560         if(state){
36561             this.focus();
36562             this.addClass("x-tree-selected");
36563         }else{
36564             //this.blur();
36565             this.removeClass("x-tree-selected");
36566         }
36567     },
36568
36569     onMove : function(tree, node, oldParent, newParent, index, refNode){
36570         this.childIndent = null;
36571         if(this.rendered){
36572             var targetNode = newParent.ui.getContainer();
36573             if(!targetNode){//target not rendered
36574                 this.holder = document.createElement("div");
36575                 this.holder.appendChild(this.wrap);
36576                 return;
36577             }
36578             var insertBefore = refNode ? refNode.ui.getEl() : null;
36579             if(insertBefore){
36580                 targetNode.insertBefore(this.wrap, insertBefore);
36581             }else{
36582                 targetNode.appendChild(this.wrap);
36583             }
36584             this.node.renderIndent(true);
36585         }
36586     },
36587
36588     addClass : function(cls){
36589         if(this.elNode){
36590             Roo.fly(this.elNode).addClass(cls);
36591         }
36592     },
36593
36594     removeClass : function(cls){
36595         if(this.elNode){
36596             Roo.fly(this.elNode).removeClass(cls);
36597         }
36598     },
36599
36600     remove : function(){
36601         if(this.rendered){
36602             this.holder = document.createElement("div");
36603             this.holder.appendChild(this.wrap);
36604         }
36605     },
36606
36607     fireEvent : function(){
36608         return this.node.fireEvent.apply(this.node, arguments);
36609     },
36610
36611     initEvents : function(){
36612         this.node.on("move", this.onMove, this);
36613         var E = Roo.EventManager;
36614         var a = this.anchor;
36615
36616         var el = Roo.fly(a, '_treeui');
36617
36618         if(Roo.isOpera){ // opera render bug ignores the CSS
36619             el.setStyle("text-decoration", "none");
36620         }
36621
36622         el.on("click", this.onClick, this);
36623         el.on("dblclick", this.onDblClick, this);
36624
36625         if(this.checkbox){
36626             Roo.EventManager.on(this.checkbox,
36627                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36628         }
36629
36630         el.on("contextmenu", this.onContextMenu, this);
36631
36632         var icon = Roo.fly(this.iconNode);
36633         icon.on("click", this.onClick, this);
36634         icon.on("dblclick", this.onDblClick, this);
36635         icon.on("contextmenu", this.onContextMenu, this);
36636         E.on(this.ecNode, "click", this.ecClick, this, true);
36637
36638         if(this.node.disabled){
36639             this.addClass("x-tree-node-disabled");
36640         }
36641         if(this.node.hidden){
36642             this.addClass("x-tree-node-disabled");
36643         }
36644         var ot = this.node.getOwnerTree();
36645         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36646         if(dd && (!this.node.isRoot || ot.rootVisible)){
36647             Roo.dd.Registry.register(this.elNode, {
36648                 node: this.node,
36649                 handles: this.getDDHandles(),
36650                 isHandle: false
36651             });
36652         }
36653     },
36654
36655     getDDHandles : function(){
36656         return [this.iconNode, this.textNode];
36657     },
36658
36659     hide : function(){
36660         if(this.rendered){
36661             this.wrap.style.display = "none";
36662         }
36663     },
36664
36665     show : function(){
36666         if(this.rendered){
36667             this.wrap.style.display = "";
36668         }
36669     },
36670
36671     onContextMenu : function(e){
36672         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36673             e.preventDefault();
36674             this.focus();
36675             this.fireEvent("contextmenu", this.node, e);
36676         }
36677     },
36678
36679     onClick : function(e){
36680         if(this.dropping){
36681             e.stopEvent();
36682             return;
36683         }
36684         if(this.fireEvent("beforeclick", this.node, e) !== false){
36685             if(!this.disabled && this.node.attributes.href){
36686                 this.fireEvent("click", this.node, e);
36687                 return;
36688             }
36689             e.preventDefault();
36690             if(this.disabled){
36691                 return;
36692             }
36693
36694             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36695                 this.node.toggle();
36696             }
36697
36698             this.fireEvent("click", this.node, e);
36699         }else{
36700             e.stopEvent();
36701         }
36702     },
36703
36704     onDblClick : function(e){
36705         e.preventDefault();
36706         if(this.disabled){
36707             return;
36708         }
36709         if(this.checkbox){
36710             this.toggleCheck();
36711         }
36712         if(!this.animating && this.node.hasChildNodes()){
36713             this.node.toggle();
36714         }
36715         this.fireEvent("dblclick", this.node, e);
36716     },
36717
36718     onCheckChange : function(){
36719         var checked = this.checkbox.checked;
36720         this.node.attributes.checked = checked;
36721         this.fireEvent('checkchange', this.node, checked);
36722     },
36723
36724     ecClick : function(e){
36725         if(!this.animating && this.node.hasChildNodes()){
36726             this.node.toggle();
36727         }
36728     },
36729
36730     startDrop : function(){
36731         this.dropping = true;
36732     },
36733
36734     // delayed drop so the click event doesn't get fired on a drop
36735     endDrop : function(){
36736        setTimeout(function(){
36737            this.dropping = false;
36738        }.createDelegate(this), 50);
36739     },
36740
36741     expand : function(){
36742         this.updateExpandIcon();
36743         this.ctNode.style.display = "";
36744     },
36745
36746     focus : function(){
36747         if(!this.node.preventHScroll){
36748             try{this.anchor.focus();
36749             }catch(e){}
36750         }else if(!Roo.isIE){
36751             try{
36752                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36753                 var l = noscroll.scrollLeft;
36754                 this.anchor.focus();
36755                 noscroll.scrollLeft = l;
36756             }catch(e){}
36757         }
36758     },
36759
36760     toggleCheck : function(value){
36761         var cb = this.checkbox;
36762         if(cb){
36763             cb.checked = (value === undefined ? !cb.checked : value);
36764         }
36765     },
36766
36767     blur : function(){
36768         try{
36769             this.anchor.blur();
36770         }catch(e){}
36771     },
36772
36773     animExpand : function(callback){
36774         var ct = Roo.get(this.ctNode);
36775         ct.stopFx();
36776         if(!this.node.hasChildNodes()){
36777             this.updateExpandIcon();
36778             this.ctNode.style.display = "";
36779             Roo.callback(callback);
36780             return;
36781         }
36782         this.animating = true;
36783         this.updateExpandIcon();
36784
36785         ct.slideIn('t', {
36786            callback : function(){
36787                this.animating = false;
36788                Roo.callback(callback);
36789             },
36790             scope: this,
36791             duration: this.node.ownerTree.duration || .25
36792         });
36793     },
36794
36795     highlight : function(){
36796         var tree = this.node.getOwnerTree();
36797         Roo.fly(this.wrap).highlight(
36798             tree.hlColor || "C3DAF9",
36799             {endColor: tree.hlBaseColor}
36800         );
36801     },
36802
36803     collapse : function(){
36804         this.updateExpandIcon();
36805         this.ctNode.style.display = "none";
36806     },
36807
36808     animCollapse : function(callback){
36809         var ct = Roo.get(this.ctNode);
36810         ct.enableDisplayMode('block');
36811         ct.stopFx();
36812
36813         this.animating = true;
36814         this.updateExpandIcon();
36815
36816         ct.slideOut('t', {
36817             callback : function(){
36818                this.animating = false;
36819                Roo.callback(callback);
36820             },
36821             scope: this,
36822             duration: this.node.ownerTree.duration || .25
36823         });
36824     },
36825
36826     getContainer : function(){
36827         return this.ctNode;
36828     },
36829
36830     getEl : function(){
36831         return this.wrap;
36832     },
36833
36834     appendDDGhost : function(ghostNode){
36835         ghostNode.appendChild(this.elNode.cloneNode(true));
36836     },
36837
36838     getDDRepairXY : function(){
36839         return Roo.lib.Dom.getXY(this.iconNode);
36840     },
36841
36842     onRender : function(){
36843         this.render();
36844     },
36845
36846     render : function(bulkRender){
36847         var n = this.node, a = n.attributes;
36848         var targetNode = n.parentNode ?
36849               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36850
36851         if(!this.rendered){
36852             this.rendered = true;
36853
36854             this.renderElements(n, a, targetNode, bulkRender);
36855
36856             if(a.qtip){
36857                if(this.textNode.setAttributeNS){
36858                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36859                    if(a.qtipTitle){
36860                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36861                    }
36862                }else{
36863                    this.textNode.setAttribute("ext:qtip", a.qtip);
36864                    if(a.qtipTitle){
36865                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36866                    }
36867                }
36868             }else if(a.qtipCfg){
36869                 a.qtipCfg.target = Roo.id(this.textNode);
36870                 Roo.QuickTips.register(a.qtipCfg);
36871             }
36872             this.initEvents();
36873             if(!this.node.expanded){
36874                 this.updateExpandIcon();
36875             }
36876         }else{
36877             if(bulkRender === true) {
36878                 targetNode.appendChild(this.wrap);
36879             }
36880         }
36881     },
36882
36883     renderElements : function(n, a, targetNode, bulkRender)
36884     {
36885         // add some indent caching, this helps performance when rendering a large tree
36886         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36887         var t = n.getOwnerTree();
36888         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36889         if (typeof(n.attributes.html) != 'undefined') {
36890             txt = n.attributes.html;
36891         }
36892         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36893         var cb = typeof a.checked == 'boolean';
36894         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36895         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36896             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36897             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36898             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36899             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36900             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36901              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36902                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36903             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36904             "</li>"];
36905
36906         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36907             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36908                                 n.nextSibling.ui.getEl(), buf.join(""));
36909         }else{
36910             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36911         }
36912
36913         this.elNode = this.wrap.childNodes[0];
36914         this.ctNode = this.wrap.childNodes[1];
36915         var cs = this.elNode.childNodes;
36916         this.indentNode = cs[0];
36917         this.ecNode = cs[1];
36918         this.iconNode = cs[2];
36919         var index = 3;
36920         if(cb){
36921             this.checkbox = cs[3];
36922             index++;
36923         }
36924         this.anchor = cs[index];
36925         this.textNode = cs[index].firstChild;
36926     },
36927
36928     getAnchor : function(){
36929         return this.anchor;
36930     },
36931
36932     getTextEl : function(){
36933         return this.textNode;
36934     },
36935
36936     getIconEl : function(){
36937         return this.iconNode;
36938     },
36939
36940     isChecked : function(){
36941         return this.checkbox ? this.checkbox.checked : false;
36942     },
36943
36944     updateExpandIcon : function(){
36945         if(this.rendered){
36946             var n = this.node, c1, c2;
36947             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36948             var hasChild = n.hasChildNodes();
36949             if(hasChild){
36950                 if(n.expanded){
36951                     cls += "-minus";
36952                     c1 = "x-tree-node-collapsed";
36953                     c2 = "x-tree-node-expanded";
36954                 }else{
36955                     cls += "-plus";
36956                     c1 = "x-tree-node-expanded";
36957                     c2 = "x-tree-node-collapsed";
36958                 }
36959                 if(this.wasLeaf){
36960                     this.removeClass("x-tree-node-leaf");
36961                     this.wasLeaf = false;
36962                 }
36963                 if(this.c1 != c1 || this.c2 != c2){
36964                     Roo.fly(this.elNode).replaceClass(c1, c2);
36965                     this.c1 = c1; this.c2 = c2;
36966                 }
36967             }else{
36968                 // this changes non-leafs into leafs if they have no children.
36969                 // it's not very rational behaviour..
36970                 
36971                 if(!this.wasLeaf && this.node.leaf){
36972                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36973                     delete this.c1;
36974                     delete this.c2;
36975                     this.wasLeaf = true;
36976                 }
36977             }
36978             var ecc = "x-tree-ec-icon "+cls;
36979             if(this.ecc != ecc){
36980                 this.ecNode.className = ecc;
36981                 this.ecc = ecc;
36982             }
36983         }
36984     },
36985
36986     getChildIndent : function(){
36987         if(!this.childIndent){
36988             var buf = [];
36989             var p = this.node;
36990             while(p){
36991                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36992                     if(!p.isLast()) {
36993                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36994                     } else {
36995                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36996                     }
36997                 }
36998                 p = p.parentNode;
36999             }
37000             this.childIndent = buf.join("");
37001         }
37002         return this.childIndent;
37003     },
37004
37005     renderIndent : function(){
37006         if(this.rendered){
37007             var indent = "";
37008             var p = this.node.parentNode;
37009             if(p){
37010                 indent = p.ui.getChildIndent();
37011             }
37012             if(this.indentMarkup != indent){ // don't rerender if not required
37013                 this.indentNode.innerHTML = indent;
37014                 this.indentMarkup = indent;
37015             }
37016             this.updateExpandIcon();
37017         }
37018     }
37019 };
37020
37021 Roo.tree.RootTreeNodeUI = function(){
37022     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37023 };
37024 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37025     render : function(){
37026         if(!this.rendered){
37027             var targetNode = this.node.ownerTree.innerCt.dom;
37028             this.node.expanded = true;
37029             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37030             this.wrap = this.ctNode = targetNode.firstChild;
37031         }
37032     },
37033     collapse : function(){
37034     },
37035     expand : function(){
37036     }
37037 });/*
37038  * Based on:
37039  * Ext JS Library 1.1.1
37040  * Copyright(c) 2006-2007, Ext JS, LLC.
37041  *
37042  * Originally Released Under LGPL - original licence link has changed is not relivant.
37043  *
37044  * Fork - LGPL
37045  * <script type="text/javascript">
37046  */
37047 /**
37048  * @class Roo.tree.TreeLoader
37049  * @extends Roo.util.Observable
37050  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37051  * nodes from a specified URL. The response must be a javascript Array definition
37052  * who's elements are node definition objects. eg:
37053  * <pre><code>
37054 {  success : true,
37055    data :      [
37056    
37057     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37058     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37059     ]
37060 }
37061
37062
37063 </code></pre>
37064  * <br><br>
37065  * The old style respose with just an array is still supported, but not recommended.
37066  * <br><br>
37067  *
37068  * A server request is sent, and child nodes are loaded only when a node is expanded.
37069  * The loading node's id is passed to the server under the parameter name "node" to
37070  * enable the server to produce the correct child nodes.
37071  * <br><br>
37072  * To pass extra parameters, an event handler may be attached to the "beforeload"
37073  * event, and the parameters specified in the TreeLoader's baseParams property:
37074  * <pre><code>
37075     myTreeLoader.on("beforeload", function(treeLoader, node) {
37076         this.baseParams.category = node.attributes.category;
37077     }, this);
37078     
37079 </code></pre>
37080  *
37081  * This would pass an HTTP parameter called "category" to the server containing
37082  * the value of the Node's "category" attribute.
37083  * @constructor
37084  * Creates a new Treeloader.
37085  * @param {Object} config A config object containing config properties.
37086  */
37087 Roo.tree.TreeLoader = function(config){
37088     this.baseParams = {};
37089     this.requestMethod = "POST";
37090     Roo.apply(this, config);
37091
37092     this.addEvents({
37093     
37094         /**
37095          * @event beforeload
37096          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37097          * @param {Object} This TreeLoader object.
37098          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37099          * @param {Object} callback The callback function specified in the {@link #load} call.
37100          */
37101         beforeload : true,
37102         /**
37103          * @event load
37104          * Fires when the node has been successfuly loaded.
37105          * @param {Object} This TreeLoader object.
37106          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37107          * @param {Object} response The response object containing the data from the server.
37108          */
37109         load : true,
37110         /**
37111          * @event loadexception
37112          * Fires if the network request failed.
37113          * @param {Object} This TreeLoader object.
37114          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37115          * @param {Object} response The response object containing the data from the server.
37116          */
37117         loadexception : true,
37118         /**
37119          * @event create
37120          * Fires before a node is created, enabling you to return custom Node types 
37121          * @param {Object} This TreeLoader object.
37122          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37123          */
37124         create : true
37125     });
37126
37127     Roo.tree.TreeLoader.superclass.constructor.call(this);
37128 };
37129
37130 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37131     /**
37132     * @cfg {String} dataUrl The URL from which to request a Json string which
37133     * specifies an array of node definition object representing the child nodes
37134     * to be loaded.
37135     */
37136     /**
37137     * @cfg {String} requestMethod either GET or POST
37138     * defaults to POST (due to BC)
37139     * to be loaded.
37140     */
37141     /**
37142     * @cfg {Object} baseParams (optional) An object containing properties which
37143     * specify HTTP parameters to be passed to each request for child nodes.
37144     */
37145     /**
37146     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37147     * created by this loader. If the attributes sent by the server have an attribute in this object,
37148     * they take priority.
37149     */
37150     /**
37151     * @cfg {Object} uiProviders (optional) An object containing properties which
37152     * 
37153     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37154     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37155     * <i>uiProvider</i> attribute of a returned child node is a string rather
37156     * than a reference to a TreeNodeUI implementation, this that string value
37157     * is used as a property name in the uiProviders object. You can define the provider named
37158     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37159     */
37160     uiProviders : {},
37161
37162     /**
37163     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37164     * child nodes before loading.
37165     */
37166     clearOnLoad : true,
37167
37168     /**
37169     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37170     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37171     * Grid query { data : [ .....] }
37172     */
37173     
37174     root : false,
37175      /**
37176     * @cfg {String} queryParam (optional) 
37177     * Name of the query as it will be passed on the querystring (defaults to 'node')
37178     * eg. the request will be ?node=[id]
37179     */
37180     
37181     
37182     queryParam: false,
37183     
37184     /**
37185      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37186      * This is called automatically when a node is expanded, but may be used to reload
37187      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37188      * @param {Roo.tree.TreeNode} node
37189      * @param {Function} callback
37190      */
37191     load : function(node, callback){
37192         if(this.clearOnLoad){
37193             while(node.firstChild){
37194                 node.removeChild(node.firstChild);
37195             }
37196         }
37197         if(node.attributes.children){ // preloaded json children
37198             var cs = node.attributes.children;
37199             for(var i = 0, len = cs.length; i < len; i++){
37200                 node.appendChild(this.createNode(cs[i]));
37201             }
37202             if(typeof callback == "function"){
37203                 callback();
37204             }
37205         }else if(this.dataUrl){
37206             this.requestData(node, callback);
37207         }
37208     },
37209
37210     getParams: function(node){
37211         var buf = [], bp = this.baseParams;
37212         for(var key in bp){
37213             if(typeof bp[key] != "function"){
37214                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37215             }
37216         }
37217         var n = this.queryParam === false ? 'node' : this.queryParam;
37218         buf.push(n + "=", encodeURIComponent(node.id));
37219         return buf.join("");
37220     },
37221
37222     requestData : function(node, callback){
37223         if(this.fireEvent("beforeload", this, node, callback) !== false){
37224             this.transId = Roo.Ajax.request({
37225                 method:this.requestMethod,
37226                 url: this.dataUrl||this.url,
37227                 success: this.handleResponse,
37228                 failure: this.handleFailure,
37229                 scope: this,
37230                 argument: {callback: callback, node: node},
37231                 params: this.getParams(node)
37232             });
37233         }else{
37234             // if the load is cancelled, make sure we notify
37235             // the node that we are done
37236             if(typeof callback == "function"){
37237                 callback();
37238             }
37239         }
37240     },
37241
37242     isLoading : function(){
37243         return this.transId ? true : false;
37244     },
37245
37246     abort : function(){
37247         if(this.isLoading()){
37248             Roo.Ajax.abort(this.transId);
37249         }
37250     },
37251
37252     // private
37253     createNode : function(attr)
37254     {
37255         // apply baseAttrs, nice idea Corey!
37256         if(this.baseAttrs){
37257             Roo.applyIf(attr, this.baseAttrs);
37258         }
37259         if(this.applyLoader !== false){
37260             attr.loader = this;
37261         }
37262         // uiProvider = depreciated..
37263         
37264         if(typeof(attr.uiProvider) == 'string'){
37265            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37266                 /**  eval:var:attr */ eval(attr.uiProvider);
37267         }
37268         if(typeof(this.uiProviders['default']) != 'undefined') {
37269             attr.uiProvider = this.uiProviders['default'];
37270         }
37271         
37272         this.fireEvent('create', this, attr);
37273         
37274         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37275         return(attr.leaf ?
37276                         new Roo.tree.TreeNode(attr) :
37277                         new Roo.tree.AsyncTreeNode(attr));
37278     },
37279
37280     processResponse : function(response, node, callback)
37281     {
37282         var json = response.responseText;
37283         try {
37284             
37285             var o = Roo.decode(json);
37286             
37287             if (this.root === false && typeof(o.success) != undefined) {
37288                 this.root = 'data'; // the default behaviour for list like data..
37289                 }
37290                 
37291             if (this.root !== false &&  !o.success) {
37292                 // it's a failure condition.
37293                 var a = response.argument;
37294                 this.fireEvent("loadexception", this, a.node, response);
37295                 Roo.log("Load failed - should have a handler really");
37296                 return;
37297             }
37298             
37299             
37300             
37301             if (this.root !== false) {
37302                  o = o[this.root];
37303             }
37304             
37305             for(var i = 0, len = o.length; i < len; i++){
37306                 var n = this.createNode(o[i]);
37307                 if(n){
37308                     node.appendChild(n);
37309                 }
37310             }
37311             if(typeof callback == "function"){
37312                 callback(this, node);
37313             }
37314         }catch(e){
37315             this.handleFailure(response);
37316         }
37317     },
37318
37319     handleResponse : function(response){
37320         this.transId = false;
37321         var a = response.argument;
37322         this.processResponse(response, a.node, a.callback);
37323         this.fireEvent("load", this, a.node, response);
37324     },
37325
37326     handleFailure : function(response)
37327     {
37328         // should handle failure better..
37329         this.transId = false;
37330         var a = response.argument;
37331         this.fireEvent("loadexception", this, a.node, response);
37332         if(typeof a.callback == "function"){
37333             a.callback(this, a.node);
37334         }
37335     }
37336 });/*
37337  * Based on:
37338  * Ext JS Library 1.1.1
37339  * Copyright(c) 2006-2007, Ext JS, LLC.
37340  *
37341  * Originally Released Under LGPL - original licence link has changed is not relivant.
37342  *
37343  * Fork - LGPL
37344  * <script type="text/javascript">
37345  */
37346
37347 /**
37348 * @class Roo.tree.TreeFilter
37349 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37350 * @param {TreePanel} tree
37351 * @param {Object} config (optional)
37352  */
37353 Roo.tree.TreeFilter = function(tree, config){
37354     this.tree = tree;
37355     this.filtered = {};
37356     Roo.apply(this, config);
37357 };
37358
37359 Roo.tree.TreeFilter.prototype = {
37360     clearBlank:false,
37361     reverse:false,
37362     autoClear:false,
37363     remove:false,
37364
37365      /**
37366      * Filter the data by a specific attribute.
37367      * @param {String/RegExp} value Either string that the attribute value
37368      * should start with or a RegExp to test against the attribute
37369      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37370      * @param {TreeNode} startNode (optional) The node to start the filter at.
37371      */
37372     filter : function(value, attr, startNode){
37373         attr = attr || "text";
37374         var f;
37375         if(typeof value == "string"){
37376             var vlen = value.length;
37377             // auto clear empty filter
37378             if(vlen == 0 && this.clearBlank){
37379                 this.clear();
37380                 return;
37381             }
37382             value = value.toLowerCase();
37383             f = function(n){
37384                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37385             };
37386         }else if(value.exec){ // regex?
37387             f = function(n){
37388                 return value.test(n.attributes[attr]);
37389             };
37390         }else{
37391             throw 'Illegal filter type, must be string or regex';
37392         }
37393         this.filterBy(f, null, startNode);
37394         },
37395
37396     /**
37397      * Filter by a function. The passed function will be called with each
37398      * node in the tree (or from the startNode). If the function returns true, the node is kept
37399      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37400      * @param {Function} fn The filter function
37401      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37402      */
37403     filterBy : function(fn, scope, startNode){
37404         startNode = startNode || this.tree.root;
37405         if(this.autoClear){
37406             this.clear();
37407         }
37408         var af = this.filtered, rv = this.reverse;
37409         var f = function(n){
37410             if(n == startNode){
37411                 return true;
37412             }
37413             if(af[n.id]){
37414                 return false;
37415             }
37416             var m = fn.call(scope || n, n);
37417             if(!m || rv){
37418                 af[n.id] = n;
37419                 n.ui.hide();
37420                 return false;
37421             }
37422             return true;
37423         };
37424         startNode.cascade(f);
37425         if(this.remove){
37426            for(var id in af){
37427                if(typeof id != "function"){
37428                    var n = af[id];
37429                    if(n && n.parentNode){
37430                        n.parentNode.removeChild(n);
37431                    }
37432                }
37433            }
37434         }
37435     },
37436
37437     /**
37438      * Clears the current filter. Note: with the "remove" option
37439      * set a filter cannot be cleared.
37440      */
37441     clear : function(){
37442         var t = this.tree;
37443         var af = this.filtered;
37444         for(var id in af){
37445             if(typeof id != "function"){
37446                 var n = af[id];
37447                 if(n){
37448                     n.ui.show();
37449                 }
37450             }
37451         }
37452         this.filtered = {};
37453     }
37454 };
37455 /*
37456  * Based on:
37457  * Ext JS Library 1.1.1
37458  * Copyright(c) 2006-2007, Ext JS, LLC.
37459  *
37460  * Originally Released Under LGPL - original licence link has changed is not relivant.
37461  *
37462  * Fork - LGPL
37463  * <script type="text/javascript">
37464  */
37465  
37466
37467 /**
37468  * @class Roo.tree.TreeSorter
37469  * Provides sorting of nodes in a TreePanel
37470  * 
37471  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37472  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37473  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37474  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37475  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37476  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37477  * @constructor
37478  * @param {TreePanel} tree
37479  * @param {Object} config
37480  */
37481 Roo.tree.TreeSorter = function(tree, config){
37482     Roo.apply(this, config);
37483     tree.on("beforechildrenrendered", this.doSort, this);
37484     tree.on("append", this.updateSort, this);
37485     tree.on("insert", this.updateSort, this);
37486     
37487     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37488     var p = this.property || "text";
37489     var sortType = this.sortType;
37490     var fs = this.folderSort;
37491     var cs = this.caseSensitive === true;
37492     var leafAttr = this.leafAttr || 'leaf';
37493
37494     this.sortFn = function(n1, n2){
37495         if(fs){
37496             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37497                 return 1;
37498             }
37499             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37500                 return -1;
37501             }
37502         }
37503         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37504         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37505         if(v1 < v2){
37506                         return dsc ? +1 : -1;
37507                 }else if(v1 > v2){
37508                         return dsc ? -1 : +1;
37509         }else{
37510                 return 0;
37511         }
37512     };
37513 };
37514
37515 Roo.tree.TreeSorter.prototype = {
37516     doSort : function(node){
37517         node.sort(this.sortFn);
37518     },
37519     
37520     compareNodes : function(n1, n2){
37521         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37522     },
37523     
37524     updateSort : function(tree, node){
37525         if(node.childrenRendered){
37526             this.doSort.defer(1, this, [node]);
37527         }
37528     }
37529 };/*
37530  * Based on:
37531  * Ext JS Library 1.1.1
37532  * Copyright(c) 2006-2007, Ext JS, LLC.
37533  *
37534  * Originally Released Under LGPL - original licence link has changed is not relivant.
37535  *
37536  * Fork - LGPL
37537  * <script type="text/javascript">
37538  */
37539
37540 if(Roo.dd.DropZone){
37541     
37542 Roo.tree.TreeDropZone = function(tree, config){
37543     this.allowParentInsert = false;
37544     this.allowContainerDrop = false;
37545     this.appendOnly = false;
37546     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37547     this.tree = tree;
37548     this.lastInsertClass = "x-tree-no-status";
37549     this.dragOverData = {};
37550 };
37551
37552 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37553     ddGroup : "TreeDD",
37554     scroll:  true,
37555     
37556     expandDelay : 1000,
37557     
37558     expandNode : function(node){
37559         if(node.hasChildNodes() && !node.isExpanded()){
37560             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37561         }
37562     },
37563     
37564     queueExpand : function(node){
37565         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37566     },
37567     
37568     cancelExpand : function(){
37569         if(this.expandProcId){
37570             clearTimeout(this.expandProcId);
37571             this.expandProcId = false;
37572         }
37573     },
37574     
37575     isValidDropPoint : function(n, pt, dd, e, data){
37576         if(!n || !data){ return false; }
37577         var targetNode = n.node;
37578         var dropNode = data.node;
37579         // default drop rules
37580         if(!(targetNode && targetNode.isTarget && pt)){
37581             return false;
37582         }
37583         if(pt == "append" && targetNode.allowChildren === false){
37584             return false;
37585         }
37586         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37587             return false;
37588         }
37589         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37590             return false;
37591         }
37592         // reuse the object
37593         var overEvent = this.dragOverData;
37594         overEvent.tree = this.tree;
37595         overEvent.target = targetNode;
37596         overEvent.data = data;
37597         overEvent.point = pt;
37598         overEvent.source = dd;
37599         overEvent.rawEvent = e;
37600         overEvent.dropNode = dropNode;
37601         overEvent.cancel = false;  
37602         var result = this.tree.fireEvent("nodedragover", overEvent);
37603         return overEvent.cancel === false && result !== false;
37604     },
37605     
37606     getDropPoint : function(e, n, dd)
37607     {
37608         var tn = n.node;
37609         if(tn.isRoot){
37610             return tn.allowChildren !== false ? "append" : false; // always append for root
37611         }
37612         var dragEl = n.ddel;
37613         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37614         var y = Roo.lib.Event.getPageY(e);
37615         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37616         
37617         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37618         var noAppend = tn.allowChildren === false;
37619         if(this.appendOnly || tn.parentNode.allowChildren === false){
37620             return noAppend ? false : "append";
37621         }
37622         var noBelow = false;
37623         if(!this.allowParentInsert){
37624             noBelow = tn.hasChildNodes() && tn.isExpanded();
37625         }
37626         var q = (b - t) / (noAppend ? 2 : 3);
37627         if(y >= t && y < (t + q)){
37628             return "above";
37629         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37630             return "below";
37631         }else{
37632             return "append";
37633         }
37634     },
37635     
37636     onNodeEnter : function(n, dd, e, data)
37637     {
37638         this.cancelExpand();
37639     },
37640     
37641     onNodeOver : function(n, dd, e, data)
37642     {
37643        
37644         var pt = this.getDropPoint(e, n, dd);
37645         var node = n.node;
37646         
37647         // auto node expand check
37648         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37649             this.queueExpand(node);
37650         }else if(pt != "append"){
37651             this.cancelExpand();
37652         }
37653         
37654         // set the insert point style on the target node
37655         var returnCls = this.dropNotAllowed;
37656         if(this.isValidDropPoint(n, pt, dd, e, data)){
37657            if(pt){
37658                var el = n.ddel;
37659                var cls;
37660                if(pt == "above"){
37661                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37662                    cls = "x-tree-drag-insert-above";
37663                }else if(pt == "below"){
37664                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37665                    cls = "x-tree-drag-insert-below";
37666                }else{
37667                    returnCls = "x-tree-drop-ok-append";
37668                    cls = "x-tree-drag-append";
37669                }
37670                if(this.lastInsertClass != cls){
37671                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37672                    this.lastInsertClass = cls;
37673                }
37674            }
37675        }
37676        return returnCls;
37677     },
37678     
37679     onNodeOut : function(n, dd, e, data){
37680         
37681         this.cancelExpand();
37682         this.removeDropIndicators(n);
37683     },
37684     
37685     onNodeDrop : function(n, dd, e, data){
37686         var point = this.getDropPoint(e, n, dd);
37687         var targetNode = n.node;
37688         targetNode.ui.startDrop();
37689         if(!this.isValidDropPoint(n, point, dd, e, data)){
37690             targetNode.ui.endDrop();
37691             return false;
37692         }
37693         // first try to find the drop node
37694         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37695         var dropEvent = {
37696             tree : this.tree,
37697             target: targetNode,
37698             data: data,
37699             point: point,
37700             source: dd,
37701             rawEvent: e,
37702             dropNode: dropNode,
37703             cancel: !dropNode   
37704         };
37705         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37706         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37707             targetNode.ui.endDrop();
37708             return false;
37709         }
37710         // allow target changing
37711         targetNode = dropEvent.target;
37712         if(point == "append" && !targetNode.isExpanded()){
37713             targetNode.expand(false, null, function(){
37714                 this.completeDrop(dropEvent);
37715             }.createDelegate(this));
37716         }else{
37717             this.completeDrop(dropEvent);
37718         }
37719         return true;
37720     },
37721     
37722     completeDrop : function(de){
37723         var ns = de.dropNode, p = de.point, t = de.target;
37724         if(!(ns instanceof Array)){
37725             ns = [ns];
37726         }
37727         var n;
37728         for(var i = 0, len = ns.length; i < len; i++){
37729             n = ns[i];
37730             if(p == "above"){
37731                 t.parentNode.insertBefore(n, t);
37732             }else if(p == "below"){
37733                 t.parentNode.insertBefore(n, t.nextSibling);
37734             }else{
37735                 t.appendChild(n);
37736             }
37737         }
37738         n.ui.focus();
37739         if(this.tree.hlDrop){
37740             n.ui.highlight();
37741         }
37742         t.ui.endDrop();
37743         this.tree.fireEvent("nodedrop", de);
37744     },
37745     
37746     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37747         if(this.tree.hlDrop){
37748             dropNode.ui.focus();
37749             dropNode.ui.highlight();
37750         }
37751         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37752     },
37753     
37754     getTree : function(){
37755         return this.tree;
37756     },
37757     
37758     removeDropIndicators : function(n){
37759         if(n && n.ddel){
37760             var el = n.ddel;
37761             Roo.fly(el).removeClass([
37762                     "x-tree-drag-insert-above",
37763                     "x-tree-drag-insert-below",
37764                     "x-tree-drag-append"]);
37765             this.lastInsertClass = "_noclass";
37766         }
37767     },
37768     
37769     beforeDragDrop : function(target, e, id){
37770         this.cancelExpand();
37771         return true;
37772     },
37773     
37774     afterRepair : function(data){
37775         if(data && Roo.enableFx){
37776             data.node.ui.highlight();
37777         }
37778         this.hideProxy();
37779     } 
37780     
37781 });
37782
37783 }
37784 /*
37785  * Based on:
37786  * Ext JS Library 1.1.1
37787  * Copyright(c) 2006-2007, Ext JS, LLC.
37788  *
37789  * Originally Released Under LGPL - original licence link has changed is not relivant.
37790  *
37791  * Fork - LGPL
37792  * <script type="text/javascript">
37793  */
37794  
37795
37796 if(Roo.dd.DragZone){
37797 Roo.tree.TreeDragZone = function(tree, config){
37798     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37799     this.tree = tree;
37800 };
37801
37802 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37803     ddGroup : "TreeDD",
37804    
37805     onBeforeDrag : function(data, e){
37806         var n = data.node;
37807         return n && n.draggable && !n.disabled;
37808     },
37809      
37810     
37811     onInitDrag : function(e){
37812         var data = this.dragData;
37813         this.tree.getSelectionModel().select(data.node);
37814         this.proxy.update("");
37815         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37816         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37817     },
37818     
37819     getRepairXY : function(e, data){
37820         return data.node.ui.getDDRepairXY();
37821     },
37822     
37823     onEndDrag : function(data, e){
37824         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37825         
37826         
37827     },
37828     
37829     onValidDrop : function(dd, e, id){
37830         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37831         this.hideProxy();
37832     },
37833     
37834     beforeInvalidDrop : function(e, id){
37835         // this scrolls the original position back into view
37836         var sm = this.tree.getSelectionModel();
37837         sm.clearSelections();
37838         sm.select(this.dragData.node);
37839     }
37840 });
37841 }/*
37842  * Based on:
37843  * Ext JS Library 1.1.1
37844  * Copyright(c) 2006-2007, Ext JS, LLC.
37845  *
37846  * Originally Released Under LGPL - original licence link has changed is not relivant.
37847  *
37848  * Fork - LGPL
37849  * <script type="text/javascript">
37850  */
37851 /**
37852  * @class Roo.tree.TreeEditor
37853  * @extends Roo.Editor
37854  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37855  * as the editor field.
37856  * @constructor
37857  * @param {Object} config (used to be the tree panel.)
37858  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37859  * 
37860  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37861  * @cfg {Roo.form.TextField} field [required] The field configuration
37862  *
37863  * 
37864  */
37865 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37866     var tree = config;
37867     var field;
37868     if (oldconfig) { // old style..
37869         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37870     } else {
37871         // new style..
37872         tree = config.tree;
37873         config.field = config.field  || {};
37874         config.field.xtype = 'TextField';
37875         field = Roo.factory(config.field, Roo.form);
37876     }
37877     config = config || {};
37878     
37879     
37880     this.addEvents({
37881         /**
37882          * @event beforenodeedit
37883          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37884          * false from the handler of this event.
37885          * @param {Editor} this
37886          * @param {Roo.tree.Node} node 
37887          */
37888         "beforenodeedit" : true
37889     });
37890     
37891     //Roo.log(config);
37892     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37893
37894     this.tree = tree;
37895
37896     tree.on('beforeclick', this.beforeNodeClick, this);
37897     tree.getTreeEl().on('mousedown', this.hide, this);
37898     this.on('complete', this.updateNode, this);
37899     this.on('beforestartedit', this.fitToTree, this);
37900     this.on('startedit', this.bindScroll, this, {delay:10});
37901     this.on('specialkey', this.onSpecialKey, this);
37902 };
37903
37904 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37905     /**
37906      * @cfg {String} alignment
37907      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37908      */
37909     alignment: "l-l",
37910     // inherit
37911     autoSize: false,
37912     /**
37913      * @cfg {Boolean} hideEl
37914      * True to hide the bound element while the editor is displayed (defaults to false)
37915      */
37916     hideEl : false,
37917     /**
37918      * @cfg {String} cls
37919      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37920      */
37921     cls: "x-small-editor x-tree-editor",
37922     /**
37923      * @cfg {Boolean} shim
37924      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37925      */
37926     shim:false,
37927     // inherit
37928     shadow:"frame",
37929     /**
37930      * @cfg {Number} maxWidth
37931      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37932      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37933      * scroll and client offsets into account prior to each edit.
37934      */
37935     maxWidth: 250,
37936
37937     editDelay : 350,
37938
37939     // private
37940     fitToTree : function(ed, el){
37941         var td = this.tree.getTreeEl().dom, nd = el.dom;
37942         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37943             td.scrollLeft = nd.offsetLeft;
37944         }
37945         var w = Math.min(
37946                 this.maxWidth,
37947                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37948         this.setSize(w, '');
37949         
37950         return this.fireEvent('beforenodeedit', this, this.editNode);
37951         
37952     },
37953
37954     // private
37955     triggerEdit : function(node){
37956         this.completeEdit();
37957         this.editNode = node;
37958         this.startEdit(node.ui.textNode, node.text);
37959     },
37960
37961     // private
37962     bindScroll : function(){
37963         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37964     },
37965
37966     // private
37967     beforeNodeClick : function(node, e){
37968         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37969         this.lastClick = new Date();
37970         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37971             e.stopEvent();
37972             this.triggerEdit(node);
37973             return false;
37974         }
37975         return true;
37976     },
37977
37978     // private
37979     updateNode : function(ed, value){
37980         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37981         this.editNode.setText(value);
37982     },
37983
37984     // private
37985     onHide : function(){
37986         Roo.tree.TreeEditor.superclass.onHide.call(this);
37987         if(this.editNode){
37988             this.editNode.ui.focus();
37989         }
37990     },
37991
37992     // private
37993     onSpecialKey : function(field, e){
37994         var k = e.getKey();
37995         if(k == e.ESC){
37996             e.stopEvent();
37997             this.cancelEdit();
37998         }else if(k == e.ENTER && !e.hasModifier()){
37999             e.stopEvent();
38000             this.completeEdit();
38001         }
38002     }
38003 });//<Script type="text/javascript">
38004 /*
38005  * Based on:
38006  * Ext JS Library 1.1.1
38007  * Copyright(c) 2006-2007, Ext JS, LLC.
38008  *
38009  * Originally Released Under LGPL - original licence link has changed is not relivant.
38010  *
38011  * Fork - LGPL
38012  * <script type="text/javascript">
38013  */
38014  
38015 /**
38016  * Not documented??? - probably should be...
38017  */
38018
38019 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38020     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38021     
38022     renderElements : function(n, a, targetNode, bulkRender){
38023         //consel.log("renderElements?");
38024         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38025
38026         var t = n.getOwnerTree();
38027         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38028         
38029         var cols = t.columns;
38030         var bw = t.borderWidth;
38031         var c = cols[0];
38032         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38033          var cb = typeof a.checked == "boolean";
38034         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38035         var colcls = 'x-t-' + tid + '-c0';
38036         var buf = [
38037             '<li class="x-tree-node">',
38038             
38039                 
38040                 '<div class="x-tree-node-el ', a.cls,'">',
38041                     // extran...
38042                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38043                 
38044                 
38045                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38046                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38047                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38048                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38049                            (a.iconCls ? ' '+a.iconCls : ''),
38050                            '" unselectable="on" />',
38051                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38052                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38053                              
38054                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38055                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38056                             '<span unselectable="on" qtip="' + tx + '">',
38057                              tx,
38058                              '</span></a>' ,
38059                     '</div>',
38060                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38061                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38062                  ];
38063         for(var i = 1, len = cols.length; i < len; i++){
38064             c = cols[i];
38065             colcls = 'x-t-' + tid + '-c' +i;
38066             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38067             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38068                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38069                       "</div>");
38070          }
38071          
38072          buf.push(
38073             '</a>',
38074             '<div class="x-clear"></div></div>',
38075             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38076             "</li>");
38077         
38078         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38079             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38080                                 n.nextSibling.ui.getEl(), buf.join(""));
38081         }else{
38082             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38083         }
38084         var el = this.wrap.firstChild;
38085         this.elRow = el;
38086         this.elNode = el.firstChild;
38087         this.ranchor = el.childNodes[1];
38088         this.ctNode = this.wrap.childNodes[1];
38089         var cs = el.firstChild.childNodes;
38090         this.indentNode = cs[0];
38091         this.ecNode = cs[1];
38092         this.iconNode = cs[2];
38093         var index = 3;
38094         if(cb){
38095             this.checkbox = cs[3];
38096             index++;
38097         }
38098         this.anchor = cs[index];
38099         
38100         this.textNode = cs[index].firstChild;
38101         
38102         //el.on("click", this.onClick, this);
38103         //el.on("dblclick", this.onDblClick, this);
38104         
38105         
38106        // console.log(this);
38107     },
38108     initEvents : function(){
38109         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38110         
38111             
38112         var a = this.ranchor;
38113
38114         var el = Roo.get(a);
38115
38116         if(Roo.isOpera){ // opera render bug ignores the CSS
38117             el.setStyle("text-decoration", "none");
38118         }
38119
38120         el.on("click", this.onClick, this);
38121         el.on("dblclick", this.onDblClick, this);
38122         el.on("contextmenu", this.onContextMenu, this);
38123         
38124     },
38125     
38126     /*onSelectedChange : function(state){
38127         if(state){
38128             this.focus();
38129             this.addClass("x-tree-selected");
38130         }else{
38131             //this.blur();
38132             this.removeClass("x-tree-selected");
38133         }
38134     },*/
38135     addClass : function(cls){
38136         if(this.elRow){
38137             Roo.fly(this.elRow).addClass(cls);
38138         }
38139         
38140     },
38141     
38142     
38143     removeClass : function(cls){
38144         if(this.elRow){
38145             Roo.fly(this.elRow).removeClass(cls);
38146         }
38147     }
38148
38149     
38150     
38151 });//<Script type="text/javascript">
38152
38153 /*
38154  * Based on:
38155  * Ext JS Library 1.1.1
38156  * Copyright(c) 2006-2007, Ext JS, LLC.
38157  *
38158  * Originally Released Under LGPL - original licence link has changed is not relivant.
38159  *
38160  * Fork - LGPL
38161  * <script type="text/javascript">
38162  */
38163  
38164
38165 /**
38166  * @class Roo.tree.ColumnTree
38167  * @extends Roo.tree.TreePanel
38168  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38169  * @cfg {int} borderWidth  compined right/left border allowance
38170  * @constructor
38171  * @param {String/HTMLElement/Element} el The container element
38172  * @param {Object} config
38173  */
38174 Roo.tree.ColumnTree =  function(el, config)
38175 {
38176    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38177    this.addEvents({
38178         /**
38179         * @event resize
38180         * Fire this event on a container when it resizes
38181         * @param {int} w Width
38182         * @param {int} h Height
38183         */
38184        "resize" : true
38185     });
38186     this.on('resize', this.onResize, this);
38187 };
38188
38189 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38190     //lines:false,
38191     
38192     
38193     borderWidth: Roo.isBorderBox ? 0 : 2, 
38194     headEls : false,
38195     
38196     render : function(){
38197         // add the header.....
38198        
38199         Roo.tree.ColumnTree.superclass.render.apply(this);
38200         
38201         this.el.addClass('x-column-tree');
38202         
38203         this.headers = this.el.createChild(
38204             {cls:'x-tree-headers'},this.innerCt.dom);
38205    
38206         var cols = this.columns, c;
38207         var totalWidth = 0;
38208         this.headEls = [];
38209         var  len = cols.length;
38210         for(var i = 0; i < len; i++){
38211              c = cols[i];
38212              totalWidth += c.width;
38213             this.headEls.push(this.headers.createChild({
38214                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38215                  cn: {
38216                      cls:'x-tree-hd-text',
38217                      html: c.header
38218                  },
38219                  style:'width:'+(c.width-this.borderWidth)+'px;'
38220              }));
38221         }
38222         this.headers.createChild({cls:'x-clear'});
38223         // prevent floats from wrapping when clipped
38224         this.headers.setWidth(totalWidth);
38225         //this.innerCt.setWidth(totalWidth);
38226         this.innerCt.setStyle({ overflow: 'auto' });
38227         this.onResize(this.width, this.height);
38228              
38229         
38230     },
38231     onResize : function(w,h)
38232     {
38233         this.height = h;
38234         this.width = w;
38235         // resize cols..
38236         this.innerCt.setWidth(this.width);
38237         this.innerCt.setHeight(this.height-20);
38238         
38239         // headers...
38240         var cols = this.columns, c;
38241         var totalWidth = 0;
38242         var expEl = false;
38243         var len = cols.length;
38244         for(var i = 0; i < len; i++){
38245             c = cols[i];
38246             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38247                 // it's the expander..
38248                 expEl  = this.headEls[i];
38249                 continue;
38250             }
38251             totalWidth += c.width;
38252             
38253         }
38254         if (expEl) {
38255             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38256         }
38257         this.headers.setWidth(w-20);
38258
38259         
38260         
38261         
38262     }
38263 });
38264 /*
38265  * Based on:
38266  * Ext JS Library 1.1.1
38267  * Copyright(c) 2006-2007, Ext JS, LLC.
38268  *
38269  * Originally Released Under LGPL - original licence link has changed is not relivant.
38270  *
38271  * Fork - LGPL
38272  * <script type="text/javascript">
38273  */
38274  
38275 /**
38276  * @class Roo.menu.Menu
38277  * @extends Roo.util.Observable
38278  * @children Roo.menu.BaseItem
38279  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38280  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38281  * @constructor
38282  * Creates a new Menu
38283  * @param {Object} config Configuration options
38284  */
38285 Roo.menu.Menu = function(config){
38286     
38287     Roo.menu.Menu.superclass.constructor.call(this, config);
38288     
38289     this.id = this.id || Roo.id();
38290     this.addEvents({
38291         /**
38292          * @event beforeshow
38293          * Fires before this menu is displayed
38294          * @param {Roo.menu.Menu} this
38295          */
38296         beforeshow : true,
38297         /**
38298          * @event beforehide
38299          * Fires before this menu is hidden
38300          * @param {Roo.menu.Menu} this
38301          */
38302         beforehide : true,
38303         /**
38304          * @event show
38305          * Fires after this menu is displayed
38306          * @param {Roo.menu.Menu} this
38307          */
38308         show : true,
38309         /**
38310          * @event hide
38311          * Fires after this menu is hidden
38312          * @param {Roo.menu.Menu} this
38313          */
38314         hide : true,
38315         /**
38316          * @event click
38317          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38318          * @param {Roo.menu.Menu} this
38319          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38320          * @param {Roo.EventObject} e
38321          */
38322         click : true,
38323         /**
38324          * @event mouseover
38325          * Fires when the mouse is hovering over this menu
38326          * @param {Roo.menu.Menu} this
38327          * @param {Roo.EventObject} e
38328          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38329          */
38330         mouseover : true,
38331         /**
38332          * @event mouseout
38333          * Fires when the mouse exits this menu
38334          * @param {Roo.menu.Menu} this
38335          * @param {Roo.EventObject} e
38336          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38337          */
38338         mouseout : true,
38339         /**
38340          * @event itemclick
38341          * Fires when a menu item contained in this menu is clicked
38342          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38343          * @param {Roo.EventObject} e
38344          */
38345         itemclick: true
38346     });
38347     if (this.registerMenu) {
38348         Roo.menu.MenuMgr.register(this);
38349     }
38350     
38351     var mis = this.items;
38352     this.items = new Roo.util.MixedCollection();
38353     if(mis){
38354         this.add.apply(this, mis);
38355     }
38356 };
38357
38358 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38359     /**
38360      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38361      */
38362     minWidth : 120,
38363     /**
38364      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38365      * for bottom-right shadow (defaults to "sides")
38366      */
38367     shadow : "sides",
38368     /**
38369      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38370      * this menu (defaults to "tl-tr?")
38371      */
38372     subMenuAlign : "tl-tr?",
38373     /**
38374      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38375      * relative to its element of origin (defaults to "tl-bl?")
38376      */
38377     defaultAlign : "tl-bl?",
38378     /**
38379      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38380      */
38381     allowOtherMenus : false,
38382     /**
38383      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38384      */
38385     registerMenu : true,
38386
38387     hidden:true,
38388
38389     // private
38390     render : function(){
38391         if(this.el){
38392             return;
38393         }
38394         var el = this.el = new Roo.Layer({
38395             cls: "x-menu",
38396             shadow:this.shadow,
38397             constrain: false,
38398             parentEl: this.parentEl || document.body,
38399             zindex:15000
38400         });
38401
38402         this.keyNav = new Roo.menu.MenuNav(this);
38403
38404         if(this.plain){
38405             el.addClass("x-menu-plain");
38406         }
38407         if(this.cls){
38408             el.addClass(this.cls);
38409         }
38410         // generic focus element
38411         this.focusEl = el.createChild({
38412             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38413         });
38414         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38415         //disabling touch- as it's causing issues ..
38416         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38417         ul.on('click'   , this.onClick, this);
38418         
38419         
38420         ul.on("mouseover", this.onMouseOver, this);
38421         ul.on("mouseout", this.onMouseOut, this);
38422         this.items.each(function(item){
38423             if (item.hidden) {
38424                 return;
38425             }
38426             
38427             var li = document.createElement("li");
38428             li.className = "x-menu-list-item";
38429             ul.dom.appendChild(li);
38430             item.render(li, this);
38431         }, this);
38432         this.ul = ul;
38433         this.autoWidth();
38434     },
38435
38436     // private
38437     autoWidth : function(){
38438         var el = this.el, ul = this.ul;
38439         if(!el){
38440             return;
38441         }
38442         var w = this.width;
38443         if(w){
38444             el.setWidth(w);
38445         }else if(Roo.isIE){
38446             el.setWidth(this.minWidth);
38447             var t = el.dom.offsetWidth; // force recalc
38448             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38449         }
38450     },
38451
38452     // private
38453     delayAutoWidth : function(){
38454         if(this.rendered){
38455             if(!this.awTask){
38456                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38457             }
38458             this.awTask.delay(20);
38459         }
38460     },
38461
38462     // private
38463     findTargetItem : function(e){
38464         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38465         if(t && t.menuItemId){
38466             return this.items.get(t.menuItemId);
38467         }
38468     },
38469
38470     // private
38471     onClick : function(e){
38472         Roo.log("menu.onClick");
38473         var t = this.findTargetItem(e);
38474         if(!t){
38475             return;
38476         }
38477         Roo.log(e);
38478         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38479             if(t == this.activeItem && t.shouldDeactivate(e)){
38480                 this.activeItem.deactivate();
38481                 delete this.activeItem;
38482                 return;
38483             }
38484             if(t.canActivate){
38485                 this.setActiveItem(t, true);
38486             }
38487             return;
38488             
38489             
38490         }
38491         
38492         t.onClick(e);
38493         this.fireEvent("click", this, t, e);
38494     },
38495
38496     // private
38497     setActiveItem : function(item, autoExpand){
38498         if(item != this.activeItem){
38499             if(this.activeItem){
38500                 this.activeItem.deactivate();
38501             }
38502             this.activeItem = item;
38503             item.activate(autoExpand);
38504         }else if(autoExpand){
38505             item.expandMenu();
38506         }
38507     },
38508
38509     // private
38510     tryActivate : function(start, step){
38511         var items = this.items;
38512         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38513             var item = items.get(i);
38514             if(!item.disabled && item.canActivate){
38515                 this.setActiveItem(item, false);
38516                 return item;
38517             }
38518         }
38519         return false;
38520     },
38521
38522     // private
38523     onMouseOver : function(e){
38524         var t;
38525         if(t = this.findTargetItem(e)){
38526             if(t.canActivate && !t.disabled){
38527                 this.setActiveItem(t, true);
38528             }
38529         }
38530         this.fireEvent("mouseover", this, e, t);
38531     },
38532
38533     // private
38534     onMouseOut : function(e){
38535         var t;
38536         if(t = this.findTargetItem(e)){
38537             if(t == this.activeItem && t.shouldDeactivate(e)){
38538                 this.activeItem.deactivate();
38539                 delete this.activeItem;
38540             }
38541         }
38542         this.fireEvent("mouseout", this, e, t);
38543     },
38544
38545     /**
38546      * Read-only.  Returns true if the menu is currently displayed, else false.
38547      * @type Boolean
38548      */
38549     isVisible : function(){
38550         return this.el && !this.hidden;
38551     },
38552
38553     /**
38554      * Displays this menu relative to another element
38555      * @param {String/HTMLElement/Roo.Element} element The element to align to
38556      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38557      * the element (defaults to this.defaultAlign)
38558      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38559      */
38560     show : function(el, pos, parentMenu){
38561         this.parentMenu = parentMenu;
38562         if(!this.el){
38563             this.render();
38564         }
38565         this.fireEvent("beforeshow", this);
38566         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38567     },
38568
38569     /**
38570      * Displays this menu at a specific xy position
38571      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38572      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38573      */
38574     showAt : function(xy, parentMenu, /* private: */_e){
38575         this.parentMenu = parentMenu;
38576         if(!this.el){
38577             this.render();
38578         }
38579         if(_e !== false){
38580             this.fireEvent("beforeshow", this);
38581             xy = this.el.adjustForConstraints(xy);
38582         }
38583         this.el.setXY(xy);
38584         this.el.show();
38585         this.hidden = false;
38586         this.focus();
38587         this.fireEvent("show", this);
38588     },
38589
38590     focus : function(){
38591         if(!this.hidden){
38592             this.doFocus.defer(50, this);
38593         }
38594     },
38595
38596     doFocus : function(){
38597         if(!this.hidden){
38598             this.focusEl.focus();
38599         }
38600     },
38601
38602     /**
38603      * Hides this menu and optionally all parent menus
38604      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38605      */
38606     hide : function(deep){
38607         if(this.el && this.isVisible()){
38608             this.fireEvent("beforehide", this);
38609             if(this.activeItem){
38610                 this.activeItem.deactivate();
38611                 this.activeItem = null;
38612             }
38613             this.el.hide();
38614             this.hidden = true;
38615             this.fireEvent("hide", this);
38616         }
38617         if(deep === true && this.parentMenu){
38618             this.parentMenu.hide(true);
38619         }
38620     },
38621
38622     /**
38623      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38624      * Any of the following are valid:
38625      * <ul>
38626      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38627      * <li>An HTMLElement object which will be converted to a menu item</li>
38628      * <li>A menu item config object that will be created as a new menu item</li>
38629      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38630      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38631      * </ul>
38632      * Usage:
38633      * <pre><code>
38634 // Create the menu
38635 var menu = new Roo.menu.Menu();
38636
38637 // Create a menu item to add by reference
38638 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38639
38640 // Add a bunch of items at once using different methods.
38641 // Only the last item added will be returned.
38642 var item = menu.add(
38643     menuItem,                // add existing item by ref
38644     'Dynamic Item',          // new TextItem
38645     '-',                     // new separator
38646     { text: 'Config Item' }  // new item by config
38647 );
38648 </code></pre>
38649      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38650      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38651      */
38652     add : function(){
38653         var a = arguments, l = a.length, item;
38654         for(var i = 0; i < l; i++){
38655             var el = a[i];
38656             if ((typeof(el) == "object") && el.xtype && el.xns) {
38657                 el = Roo.factory(el, Roo.menu);
38658             }
38659             
38660             if(el.render){ // some kind of Item
38661                 item = this.addItem(el);
38662             }else if(typeof el == "string"){ // string
38663                 if(el == "separator" || el == "-"){
38664                     item = this.addSeparator();
38665                 }else{
38666                     item = this.addText(el);
38667                 }
38668             }else if(el.tagName || el.el){ // element
38669                 item = this.addElement(el);
38670             }else if(typeof el == "object"){ // must be menu item config?
38671                 item = this.addMenuItem(el);
38672             }
38673         }
38674         return item;
38675     },
38676
38677     /**
38678      * Returns this menu's underlying {@link Roo.Element} object
38679      * @return {Roo.Element} The element
38680      */
38681     getEl : function(){
38682         if(!this.el){
38683             this.render();
38684         }
38685         return this.el;
38686     },
38687
38688     /**
38689      * Adds a separator bar to the menu
38690      * @return {Roo.menu.Item} The menu item that was added
38691      */
38692     addSeparator : function(){
38693         return this.addItem(new Roo.menu.Separator());
38694     },
38695
38696     /**
38697      * Adds an {@link Roo.Element} object to the menu
38698      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38699      * @return {Roo.menu.Item} The menu item that was added
38700      */
38701     addElement : function(el){
38702         return this.addItem(new Roo.menu.BaseItem(el));
38703     },
38704
38705     /**
38706      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38707      * @param {Roo.menu.Item} item The menu item to add
38708      * @return {Roo.menu.Item} The menu item that was added
38709      */
38710     addItem : function(item){
38711         this.items.add(item);
38712         if(this.ul){
38713             var li = document.createElement("li");
38714             li.className = "x-menu-list-item";
38715             this.ul.dom.appendChild(li);
38716             item.render(li, this);
38717             this.delayAutoWidth();
38718         }
38719         return item;
38720     },
38721
38722     /**
38723      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38724      * @param {Object} config A MenuItem config object
38725      * @return {Roo.menu.Item} The menu item that was added
38726      */
38727     addMenuItem : function(config){
38728         if(!(config instanceof Roo.menu.Item)){
38729             if(typeof config.checked == "boolean"){ // must be check menu item config?
38730                 config = new Roo.menu.CheckItem(config);
38731             }else{
38732                 config = new Roo.menu.Item(config);
38733             }
38734         }
38735         return this.addItem(config);
38736     },
38737
38738     /**
38739      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38740      * @param {String} text The text to display in the menu item
38741      * @return {Roo.menu.Item} The menu item that was added
38742      */
38743     addText : function(text){
38744         return this.addItem(new Roo.menu.TextItem({ text : text }));
38745     },
38746
38747     /**
38748      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38749      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38750      * @param {Roo.menu.Item} item The menu item to add
38751      * @return {Roo.menu.Item} The menu item that was added
38752      */
38753     insert : function(index, item){
38754         this.items.insert(index, item);
38755         if(this.ul){
38756             var li = document.createElement("li");
38757             li.className = "x-menu-list-item";
38758             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38759             item.render(li, this);
38760             this.delayAutoWidth();
38761         }
38762         return item;
38763     },
38764
38765     /**
38766      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38767      * @param {Roo.menu.Item} item The menu item to remove
38768      */
38769     remove : function(item){
38770         this.items.removeKey(item.id);
38771         item.destroy();
38772     },
38773
38774     /**
38775      * Removes and destroys all items in the menu
38776      */
38777     removeAll : function(){
38778         var f;
38779         while(f = this.items.first()){
38780             this.remove(f);
38781         }
38782     }
38783 });
38784
38785 // MenuNav is a private utility class used internally by the Menu
38786 Roo.menu.MenuNav = function(menu){
38787     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38788     this.scope = this.menu = menu;
38789 };
38790
38791 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38792     doRelay : function(e, h){
38793         var k = e.getKey();
38794         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38795             this.menu.tryActivate(0, 1);
38796             return false;
38797         }
38798         return h.call(this.scope || this, e, this.menu);
38799     },
38800
38801     up : function(e, m){
38802         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38803             m.tryActivate(m.items.length-1, -1);
38804         }
38805     },
38806
38807     down : function(e, m){
38808         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38809             m.tryActivate(0, 1);
38810         }
38811     },
38812
38813     right : function(e, m){
38814         if(m.activeItem){
38815             m.activeItem.expandMenu(true);
38816         }
38817     },
38818
38819     left : function(e, m){
38820         m.hide();
38821         if(m.parentMenu && m.parentMenu.activeItem){
38822             m.parentMenu.activeItem.activate();
38823         }
38824     },
38825
38826     enter : function(e, m){
38827         if(m.activeItem){
38828             e.stopPropagation();
38829             m.activeItem.onClick(e);
38830             m.fireEvent("click", this, m.activeItem);
38831             return true;
38832         }
38833     }
38834 });/*
38835  * Based on:
38836  * Ext JS Library 1.1.1
38837  * Copyright(c) 2006-2007, Ext JS, LLC.
38838  *
38839  * Originally Released Under LGPL - original licence link has changed is not relivant.
38840  *
38841  * Fork - LGPL
38842  * <script type="text/javascript">
38843  */
38844  
38845 /**
38846  * @class Roo.menu.MenuMgr
38847  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38848  * @static
38849  */
38850 Roo.menu.MenuMgr = function(){
38851    var menus, active, groups = {}, attached = false, lastShow = new Date();
38852
38853    // private - called when first menu is created
38854    function init(){
38855        menus = {};
38856        active = new Roo.util.MixedCollection();
38857        Roo.get(document).addKeyListener(27, function(){
38858            if(active.length > 0){
38859                hideAll();
38860            }
38861        });
38862    }
38863
38864    // private
38865    function hideAll(){
38866        if(active && active.length > 0){
38867            var c = active.clone();
38868            c.each(function(m){
38869                m.hide();
38870            });
38871        }
38872    }
38873
38874    // private
38875    function onHide(m){
38876        active.remove(m);
38877        if(active.length < 1){
38878            Roo.get(document).un("mousedown", onMouseDown);
38879            attached = false;
38880        }
38881    }
38882
38883    // private
38884    function onShow(m){
38885        var last = active.last();
38886        lastShow = new Date();
38887        active.add(m);
38888        if(!attached){
38889            Roo.get(document).on("mousedown", onMouseDown);
38890            attached = true;
38891        }
38892        if(m.parentMenu){
38893           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38894           m.parentMenu.activeChild = m;
38895        }else if(last && last.isVisible()){
38896           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38897        }
38898    }
38899
38900    // private
38901    function onBeforeHide(m){
38902        if(m.activeChild){
38903            m.activeChild.hide();
38904        }
38905        if(m.autoHideTimer){
38906            clearTimeout(m.autoHideTimer);
38907            delete m.autoHideTimer;
38908        }
38909    }
38910
38911    // private
38912    function onBeforeShow(m){
38913        var pm = m.parentMenu;
38914        if(!pm && !m.allowOtherMenus){
38915            hideAll();
38916        }else if(pm && pm.activeChild && active != m){
38917            pm.activeChild.hide();
38918        }
38919    }
38920
38921    // private
38922    function onMouseDown(e){
38923        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38924            hideAll();
38925        }
38926    }
38927
38928    // private
38929    function onBeforeCheck(mi, state){
38930        if(state){
38931            var g = groups[mi.group];
38932            for(var i = 0, l = g.length; i < l; i++){
38933                if(g[i] != mi){
38934                    g[i].setChecked(false);
38935                }
38936            }
38937        }
38938    }
38939
38940    return {
38941
38942        /**
38943         * Hides all menus that are currently visible
38944         */
38945        hideAll : function(){
38946             hideAll();  
38947        },
38948
38949        // private
38950        register : function(menu){
38951            if(!menus){
38952                init();
38953            }
38954            menus[menu.id] = menu;
38955            menu.on("beforehide", onBeforeHide);
38956            menu.on("hide", onHide);
38957            menu.on("beforeshow", onBeforeShow);
38958            menu.on("show", onShow);
38959            var g = menu.group;
38960            if(g && menu.events["checkchange"]){
38961                if(!groups[g]){
38962                    groups[g] = [];
38963                }
38964                groups[g].push(menu);
38965                menu.on("checkchange", onCheck);
38966            }
38967        },
38968
38969         /**
38970          * Returns a {@link Roo.menu.Menu} object
38971          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38972          * be used to generate and return a new Menu instance.
38973          */
38974        get : function(menu){
38975            if(typeof menu == "string"){ // menu id
38976                return menus[menu];
38977            }else if(menu.events){  // menu instance
38978                return menu;
38979            }else if(typeof menu.length == 'number'){ // array of menu items?
38980                return new Roo.menu.Menu({items:menu});
38981            }else{ // otherwise, must be a config
38982                return new Roo.menu.Menu(menu);
38983            }
38984        },
38985
38986        // private
38987        unregister : function(menu){
38988            delete menus[menu.id];
38989            menu.un("beforehide", onBeforeHide);
38990            menu.un("hide", onHide);
38991            menu.un("beforeshow", onBeforeShow);
38992            menu.un("show", onShow);
38993            var g = menu.group;
38994            if(g && menu.events["checkchange"]){
38995                groups[g].remove(menu);
38996                menu.un("checkchange", onCheck);
38997            }
38998        },
38999
39000        // private
39001        registerCheckable : function(menuItem){
39002            var g = menuItem.group;
39003            if(g){
39004                if(!groups[g]){
39005                    groups[g] = [];
39006                }
39007                groups[g].push(menuItem);
39008                menuItem.on("beforecheckchange", onBeforeCheck);
39009            }
39010        },
39011
39012        // private
39013        unregisterCheckable : function(menuItem){
39014            var g = menuItem.group;
39015            if(g){
39016                groups[g].remove(menuItem);
39017                menuItem.un("beforecheckchange", onBeforeCheck);
39018            }
39019        }
39020    };
39021 }();/*
39022  * Based on:
39023  * Ext JS Library 1.1.1
39024  * Copyright(c) 2006-2007, Ext JS, LLC.
39025  *
39026  * Originally Released Under LGPL - original licence link has changed is not relivant.
39027  *
39028  * Fork - LGPL
39029  * <script type="text/javascript">
39030  */
39031  
39032
39033 /**
39034  * @class Roo.menu.BaseItem
39035  * @extends Roo.Component
39036  * @abstract
39037  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39038  * management and base configuration options shared by all menu components.
39039  * @constructor
39040  * Creates a new BaseItem
39041  * @param {Object} config Configuration options
39042  */
39043 Roo.menu.BaseItem = function(config){
39044     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39045
39046     this.addEvents({
39047         /**
39048          * @event click
39049          * Fires when this item is clicked
39050          * @param {Roo.menu.BaseItem} this
39051          * @param {Roo.EventObject} e
39052          */
39053         click: true,
39054         /**
39055          * @event activate
39056          * Fires when this item is activated
39057          * @param {Roo.menu.BaseItem} this
39058          */
39059         activate : true,
39060         /**
39061          * @event deactivate
39062          * Fires when this item is deactivated
39063          * @param {Roo.menu.BaseItem} this
39064          */
39065         deactivate : true
39066     });
39067
39068     if(this.handler){
39069         this.on("click", this.handler, this.scope, true);
39070     }
39071 };
39072
39073 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39074     /**
39075      * @cfg {Function} handler
39076      * A function that will handle the click event of this menu item (defaults to undefined)
39077      */
39078     /**
39079      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39080      */
39081     canActivate : false,
39082     
39083      /**
39084      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39085      */
39086     hidden: false,
39087     
39088     /**
39089      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39090      */
39091     activeClass : "x-menu-item-active",
39092     /**
39093      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39094      */
39095     hideOnClick : true,
39096     /**
39097      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39098      */
39099     hideDelay : 100,
39100
39101     // private
39102     ctype: "Roo.menu.BaseItem",
39103
39104     // private
39105     actionMode : "container",
39106
39107     // private
39108     render : function(container, parentMenu){
39109         this.parentMenu = parentMenu;
39110         Roo.menu.BaseItem.superclass.render.call(this, container);
39111         this.container.menuItemId = this.id;
39112     },
39113
39114     // private
39115     onRender : function(container, position){
39116         this.el = Roo.get(this.el);
39117         container.dom.appendChild(this.el.dom);
39118     },
39119
39120     // private
39121     onClick : function(e){
39122         if(!this.disabled && this.fireEvent("click", this, e) !== false
39123                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39124             this.handleClick(e);
39125         }else{
39126             e.stopEvent();
39127         }
39128     },
39129
39130     // private
39131     activate : function(){
39132         if(this.disabled){
39133             return false;
39134         }
39135         var li = this.container;
39136         li.addClass(this.activeClass);
39137         this.region = li.getRegion().adjust(2, 2, -2, -2);
39138         this.fireEvent("activate", this);
39139         return true;
39140     },
39141
39142     // private
39143     deactivate : function(){
39144         this.container.removeClass(this.activeClass);
39145         this.fireEvent("deactivate", this);
39146     },
39147
39148     // private
39149     shouldDeactivate : function(e){
39150         return !this.region || !this.region.contains(e.getPoint());
39151     },
39152
39153     // private
39154     handleClick : function(e){
39155         if(this.hideOnClick){
39156             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39157         }
39158     },
39159
39160     // private
39161     expandMenu : function(autoActivate){
39162         // do nothing
39163     },
39164
39165     // private
39166     hideMenu : function(){
39167         // do nothing
39168     }
39169 });/*
39170  * Based on:
39171  * Ext JS Library 1.1.1
39172  * Copyright(c) 2006-2007, Ext JS, LLC.
39173  *
39174  * Originally Released Under LGPL - original licence link has changed is not relivant.
39175  *
39176  * Fork - LGPL
39177  * <script type="text/javascript">
39178  */
39179  
39180 /**
39181  * @class Roo.menu.Adapter
39182  * @extends Roo.menu.BaseItem
39183  * @abstract
39184  * 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.
39185  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39186  * @constructor
39187  * Creates a new Adapter
39188  * @param {Object} config Configuration options
39189  */
39190 Roo.menu.Adapter = function(component, config){
39191     Roo.menu.Adapter.superclass.constructor.call(this, config);
39192     this.component = component;
39193 };
39194 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39195     // private
39196     canActivate : true,
39197
39198     // private
39199     onRender : function(container, position){
39200         this.component.render(container);
39201         this.el = this.component.getEl();
39202     },
39203
39204     // private
39205     activate : function(){
39206         if(this.disabled){
39207             return false;
39208         }
39209         this.component.focus();
39210         this.fireEvent("activate", this);
39211         return true;
39212     },
39213
39214     // private
39215     deactivate : function(){
39216         this.fireEvent("deactivate", this);
39217     },
39218
39219     // private
39220     disable : function(){
39221         this.component.disable();
39222         Roo.menu.Adapter.superclass.disable.call(this);
39223     },
39224
39225     // private
39226     enable : function(){
39227         this.component.enable();
39228         Roo.menu.Adapter.superclass.enable.call(this);
39229     }
39230 });/*
39231  * Based on:
39232  * Ext JS Library 1.1.1
39233  * Copyright(c) 2006-2007, Ext JS, LLC.
39234  *
39235  * Originally Released Under LGPL - original licence link has changed is not relivant.
39236  *
39237  * Fork - LGPL
39238  * <script type="text/javascript">
39239  */
39240
39241 /**
39242  * @class Roo.menu.TextItem
39243  * @extends Roo.menu.BaseItem
39244  * Adds a static text string to a menu, usually used as either a heading or group separator.
39245  * Note: old style constructor with text is still supported.
39246  * 
39247  * @constructor
39248  * Creates a new TextItem
39249  * @param {Object} cfg Configuration
39250  */
39251 Roo.menu.TextItem = function(cfg){
39252     if (typeof(cfg) == 'string') {
39253         this.text = cfg;
39254     } else {
39255         Roo.apply(this,cfg);
39256     }
39257     
39258     Roo.menu.TextItem.superclass.constructor.call(this);
39259 };
39260
39261 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39262     /**
39263      * @cfg {String} text Text to show on item.
39264      */
39265     text : '',
39266     
39267     /**
39268      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39269      */
39270     hideOnClick : false,
39271     /**
39272      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39273      */
39274     itemCls : "x-menu-text",
39275
39276     // private
39277     onRender : function(){
39278         var s = document.createElement("span");
39279         s.className = this.itemCls;
39280         s.innerHTML = this.text;
39281         this.el = s;
39282         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39283     }
39284 });/*
39285  * Based on:
39286  * Ext JS Library 1.1.1
39287  * Copyright(c) 2006-2007, Ext JS, LLC.
39288  *
39289  * Originally Released Under LGPL - original licence link has changed is not relivant.
39290  *
39291  * Fork - LGPL
39292  * <script type="text/javascript">
39293  */
39294
39295 /**
39296  * @class Roo.menu.Separator
39297  * @extends Roo.menu.BaseItem
39298  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39299  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39300  * @constructor
39301  * @param {Object} config Configuration options
39302  */
39303 Roo.menu.Separator = function(config){
39304     Roo.menu.Separator.superclass.constructor.call(this, config);
39305 };
39306
39307 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39308     /**
39309      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39310      */
39311     itemCls : "x-menu-sep",
39312     /**
39313      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39314      */
39315     hideOnClick : false,
39316
39317     // private
39318     onRender : function(li){
39319         var s = document.createElement("span");
39320         s.className = this.itemCls;
39321         s.innerHTML = "&#160;";
39322         this.el = s;
39323         li.addClass("x-menu-sep-li");
39324         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39325     }
39326 });/*
39327  * Based on:
39328  * Ext JS Library 1.1.1
39329  * Copyright(c) 2006-2007, Ext JS, LLC.
39330  *
39331  * Originally Released Under LGPL - original licence link has changed is not relivant.
39332  *
39333  * Fork - LGPL
39334  * <script type="text/javascript">
39335  */
39336 /**
39337  * @class Roo.menu.Item
39338  * @extends Roo.menu.BaseItem
39339  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39340  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39341  * activation and click handling.
39342  * @constructor
39343  * Creates a new Item
39344  * @param {Object} config Configuration options
39345  */
39346 Roo.menu.Item = function(config){
39347     Roo.menu.Item.superclass.constructor.call(this, config);
39348     if(this.menu){
39349         this.menu = Roo.menu.MenuMgr.get(this.menu);
39350     }
39351 };
39352 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39353     /**
39354      * @cfg {Roo.menu.Menu} menu
39355      * A Sub menu
39356      */
39357     /**
39358      * @cfg {String} text
39359      * The text to show on the menu item.
39360      */
39361     text: '',
39362      /**
39363      * @cfg {String} HTML to render in menu
39364      * The text to show on the menu item (HTML version).
39365      */
39366     html: '',
39367     /**
39368      * @cfg {String} icon
39369      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39370      */
39371     icon: undefined,
39372     /**
39373      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39374      */
39375     itemCls : "x-menu-item",
39376     /**
39377      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39378      */
39379     canActivate : true,
39380     /**
39381      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39382      */
39383     showDelay: 200,
39384     // doc'd in BaseItem
39385     hideDelay: 200,
39386
39387     // private
39388     ctype: "Roo.menu.Item",
39389     
39390     // private
39391     onRender : function(container, position){
39392         var el = document.createElement("a");
39393         el.hideFocus = true;
39394         el.unselectable = "on";
39395         el.href = this.href || "#";
39396         if(this.hrefTarget){
39397             el.target = this.hrefTarget;
39398         }
39399         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39400         
39401         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39402         
39403         el.innerHTML = String.format(
39404                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39405                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39406         this.el = el;
39407         Roo.menu.Item.superclass.onRender.call(this, container, position);
39408     },
39409
39410     /**
39411      * Sets the text to display in this menu item
39412      * @param {String} text The text to display
39413      * @param {Boolean} isHTML true to indicate text is pure html.
39414      */
39415     setText : function(text, isHTML){
39416         if (isHTML) {
39417             this.html = text;
39418         } else {
39419             this.text = text;
39420             this.html = '';
39421         }
39422         if(this.rendered){
39423             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39424      
39425             this.el.update(String.format(
39426                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39427                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39428             this.parentMenu.autoWidth();
39429         }
39430     },
39431
39432     // private
39433     handleClick : function(e){
39434         if(!this.href){ // if no link defined, stop the event automatically
39435             e.stopEvent();
39436         }
39437         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39438     },
39439
39440     // private
39441     activate : function(autoExpand){
39442         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39443             this.focus();
39444             if(autoExpand){
39445                 this.expandMenu();
39446             }
39447         }
39448         return true;
39449     },
39450
39451     // private
39452     shouldDeactivate : function(e){
39453         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39454             if(this.menu && this.menu.isVisible()){
39455                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39456             }
39457             return true;
39458         }
39459         return false;
39460     },
39461
39462     // private
39463     deactivate : function(){
39464         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39465         this.hideMenu();
39466     },
39467
39468     // private
39469     expandMenu : function(autoActivate){
39470         if(!this.disabled && this.menu){
39471             clearTimeout(this.hideTimer);
39472             delete this.hideTimer;
39473             if(!this.menu.isVisible() && !this.showTimer){
39474                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39475             }else if (this.menu.isVisible() && autoActivate){
39476                 this.menu.tryActivate(0, 1);
39477             }
39478         }
39479     },
39480
39481     // private
39482     deferExpand : function(autoActivate){
39483         delete this.showTimer;
39484         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39485         if(autoActivate){
39486             this.menu.tryActivate(0, 1);
39487         }
39488     },
39489
39490     // private
39491     hideMenu : function(){
39492         clearTimeout(this.showTimer);
39493         delete this.showTimer;
39494         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39495             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39496         }
39497     },
39498
39499     // private
39500     deferHide : function(){
39501         delete this.hideTimer;
39502         this.menu.hide();
39503     }
39504 });/*
39505  * Based on:
39506  * Ext JS Library 1.1.1
39507  * Copyright(c) 2006-2007, Ext JS, LLC.
39508  *
39509  * Originally Released Under LGPL - original licence link has changed is not relivant.
39510  *
39511  * Fork - LGPL
39512  * <script type="text/javascript">
39513  */
39514  
39515 /**
39516  * @class Roo.menu.CheckItem
39517  * @extends Roo.menu.Item
39518  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39519  * @constructor
39520  * Creates a new CheckItem
39521  * @param {Object} config Configuration options
39522  */
39523 Roo.menu.CheckItem = function(config){
39524     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39525     this.addEvents({
39526         /**
39527          * @event beforecheckchange
39528          * Fires before the checked value is set, providing an opportunity to cancel if needed
39529          * @param {Roo.menu.CheckItem} this
39530          * @param {Boolean} checked The new checked value that will be set
39531          */
39532         "beforecheckchange" : true,
39533         /**
39534          * @event checkchange
39535          * Fires after the checked value has been set
39536          * @param {Roo.menu.CheckItem} this
39537          * @param {Boolean} checked The checked value that was set
39538          */
39539         "checkchange" : true
39540     });
39541     if(this.checkHandler){
39542         this.on('checkchange', this.checkHandler, this.scope);
39543     }
39544 };
39545 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39546     /**
39547      * @cfg {String} group
39548      * All check items with the same group name will automatically be grouped into a single-select
39549      * radio button group (defaults to '')
39550      */
39551     /**
39552      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39553      */
39554     itemCls : "x-menu-item x-menu-check-item",
39555     /**
39556      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39557      */
39558     groupClass : "x-menu-group-item",
39559
39560     /**
39561      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39562      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39563      * initialized with checked = true will be rendered as checked.
39564      */
39565     checked: false,
39566
39567     // private
39568     ctype: "Roo.menu.CheckItem",
39569
39570     // private
39571     onRender : function(c){
39572         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39573         if(this.group){
39574             this.el.addClass(this.groupClass);
39575         }
39576         Roo.menu.MenuMgr.registerCheckable(this);
39577         if(this.checked){
39578             this.checked = false;
39579             this.setChecked(true, true);
39580         }
39581     },
39582
39583     // private
39584     destroy : function(){
39585         if(this.rendered){
39586             Roo.menu.MenuMgr.unregisterCheckable(this);
39587         }
39588         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39589     },
39590
39591     /**
39592      * Set the checked state of this item
39593      * @param {Boolean} checked The new checked value
39594      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39595      */
39596     setChecked : function(state, suppressEvent){
39597         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39598             if(this.container){
39599                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39600             }
39601             this.checked = state;
39602             if(suppressEvent !== true){
39603                 this.fireEvent("checkchange", this, state);
39604             }
39605         }
39606     },
39607
39608     // private
39609     handleClick : function(e){
39610        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39611            this.setChecked(!this.checked);
39612        }
39613        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39614     }
39615 });/*
39616  * Based on:
39617  * Ext JS Library 1.1.1
39618  * Copyright(c) 2006-2007, Ext JS, LLC.
39619  *
39620  * Originally Released Under LGPL - original licence link has changed is not relivant.
39621  *
39622  * Fork - LGPL
39623  * <script type="text/javascript">
39624  */
39625  
39626 /**
39627  * @class Roo.menu.DateItem
39628  * @extends Roo.menu.Adapter
39629  * A menu item that wraps the {@link Roo.DatPicker} component.
39630  * @constructor
39631  * Creates a new DateItem
39632  * @param {Object} config Configuration options
39633  */
39634 Roo.menu.DateItem = function(config){
39635     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39636     /** The Roo.DatePicker object @type Roo.DatePicker */
39637     this.picker = this.component;
39638     this.addEvents({select: true});
39639     
39640     this.picker.on("render", function(picker){
39641         picker.getEl().swallowEvent("click");
39642         picker.container.addClass("x-menu-date-item");
39643     });
39644
39645     this.picker.on("select", this.onSelect, this);
39646 };
39647
39648 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39649     // private
39650     onSelect : function(picker, date){
39651         this.fireEvent("select", this, date, picker);
39652         Roo.menu.DateItem.superclass.handleClick.call(this);
39653     }
39654 });/*
39655  * Based on:
39656  * Ext JS Library 1.1.1
39657  * Copyright(c) 2006-2007, Ext JS, LLC.
39658  *
39659  * Originally Released Under LGPL - original licence link has changed is not relivant.
39660  *
39661  * Fork - LGPL
39662  * <script type="text/javascript">
39663  */
39664  
39665 /**
39666  * @class Roo.menu.ColorItem
39667  * @extends Roo.menu.Adapter
39668  * A menu item that wraps the {@link Roo.ColorPalette} component.
39669  * @constructor
39670  * Creates a new ColorItem
39671  * @param {Object} config Configuration options
39672  */
39673 Roo.menu.ColorItem = function(config){
39674     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39675     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39676     this.palette = this.component;
39677     this.relayEvents(this.palette, ["select"]);
39678     if(this.selectHandler){
39679         this.on('select', this.selectHandler, this.scope);
39680     }
39681 };
39682 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39683  * Based on:
39684  * Ext JS Library 1.1.1
39685  * Copyright(c) 2006-2007, Ext JS, LLC.
39686  *
39687  * Originally Released Under LGPL - original licence link has changed is not relivant.
39688  *
39689  * Fork - LGPL
39690  * <script type="text/javascript">
39691  */
39692  
39693
39694 /**
39695  * @class Roo.menu.DateMenu
39696  * @extends Roo.menu.Menu
39697  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39698  * @constructor
39699  * Creates a new DateMenu
39700  * @param {Object} config Configuration options
39701  */
39702 Roo.menu.DateMenu = function(config){
39703     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39704     this.plain = true;
39705     var di = new Roo.menu.DateItem(config);
39706     this.add(di);
39707     /**
39708      * The {@link Roo.DatePicker} instance for this DateMenu
39709      * @type DatePicker
39710      */
39711     this.picker = di.picker;
39712     /**
39713      * @event select
39714      * @param {DatePicker} picker
39715      * @param {Date} date
39716      */
39717     this.relayEvents(di, ["select"]);
39718     this.on('beforeshow', function(){
39719         if(this.picker){
39720             this.picker.hideMonthPicker(false);
39721         }
39722     }, this);
39723 };
39724 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39725     cls:'x-date-menu'
39726 });/*
39727  * Based on:
39728  * Ext JS Library 1.1.1
39729  * Copyright(c) 2006-2007, Ext JS, LLC.
39730  *
39731  * Originally Released Under LGPL - original licence link has changed is not relivant.
39732  *
39733  * Fork - LGPL
39734  * <script type="text/javascript">
39735  */
39736  
39737
39738 /**
39739  * @class Roo.menu.ColorMenu
39740  * @extends Roo.menu.Menu
39741  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39742  * @constructor
39743  * Creates a new ColorMenu
39744  * @param {Object} config Configuration options
39745  */
39746 Roo.menu.ColorMenu = function(config){
39747     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39748     this.plain = true;
39749     var ci = new Roo.menu.ColorItem(config);
39750     this.add(ci);
39751     /**
39752      * The {@link Roo.ColorPalette} instance for this ColorMenu
39753      * @type ColorPalette
39754      */
39755     this.palette = ci.palette;
39756     /**
39757      * @event select
39758      * @param {ColorPalette} palette
39759      * @param {String} color
39760      */
39761     this.relayEvents(ci, ["select"]);
39762 };
39763 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39764  * Based on:
39765  * Ext JS Library 1.1.1
39766  * Copyright(c) 2006-2007, Ext JS, LLC.
39767  *
39768  * Originally Released Under LGPL - original licence link has changed is not relivant.
39769  *
39770  * Fork - LGPL
39771  * <script type="text/javascript">
39772  */
39773  
39774 /**
39775  * @class Roo.form.TextItem
39776  * @extends Roo.BoxComponent
39777  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39778  * @constructor
39779  * Creates a new TextItem
39780  * @param {Object} config Configuration options
39781  */
39782 Roo.form.TextItem = function(config){
39783     Roo.form.TextItem.superclass.constructor.call(this, config);
39784 };
39785
39786 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39787     
39788     /**
39789      * @cfg {String} tag the tag for this item (default div)
39790      */
39791     tag : 'div',
39792     /**
39793      * @cfg {String} html the content for this item
39794      */
39795     html : '',
39796     
39797     getAutoCreate : function()
39798     {
39799         var cfg = {
39800             id: this.id,
39801             tag: this.tag,
39802             html: this.html,
39803             cls: 'x-form-item'
39804         };
39805         
39806         return cfg;
39807         
39808     },
39809     
39810     onRender : function(ct, position)
39811     {
39812         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39813         
39814         if(!this.el){
39815             var cfg = this.getAutoCreate();
39816             if(!cfg.name){
39817                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39818             }
39819             if (!cfg.name.length) {
39820                 delete cfg.name;
39821             }
39822             this.el = ct.createChild(cfg, position);
39823         }
39824     },
39825     /*
39826      * setHTML
39827      * @param {String} html update the Contents of the element.
39828      */
39829     setHTML : function(html)
39830     {
39831         this.fieldEl.dom.innerHTML = html;
39832     }
39833     
39834 });/*
39835  * Based on:
39836  * Ext JS Library 1.1.1
39837  * Copyright(c) 2006-2007, Ext JS, LLC.
39838  *
39839  * Originally Released Under LGPL - original licence link has changed is not relivant.
39840  *
39841  * Fork - LGPL
39842  * <script type="text/javascript">
39843  */
39844  
39845 /**
39846  * @class Roo.form.Field
39847  * @extends Roo.BoxComponent
39848  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39849  * @constructor
39850  * Creates a new Field
39851  * @param {Object} config Configuration options
39852  */
39853 Roo.form.Field = function(config){
39854     Roo.form.Field.superclass.constructor.call(this, config);
39855 };
39856
39857 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39858     /**
39859      * @cfg {String} fieldLabel Label to use when rendering a form.
39860      */
39861        /**
39862      * @cfg {String} qtip Mouse over tip
39863      */
39864      
39865     /**
39866      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39867      */
39868     invalidClass : "x-form-invalid",
39869     /**
39870      * @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")
39871      */
39872     invalidText : "The value in this field is invalid",
39873     /**
39874      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39875      */
39876     focusClass : "x-form-focus",
39877     /**
39878      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39879       automatic validation (defaults to "keyup").
39880      */
39881     validationEvent : "keyup",
39882     /**
39883      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39884      */
39885     validateOnBlur : true,
39886     /**
39887      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39888      */
39889     validationDelay : 250,
39890     /**
39891      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39892      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39893      */
39894     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39895     /**
39896      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39897      */
39898     fieldClass : "x-form-field",
39899     /**
39900      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39901      *<pre>
39902 Value         Description
39903 -----------   ----------------------------------------------------------------------
39904 qtip          Display a quick tip when the user hovers over the field
39905 title         Display a default browser title attribute popup
39906 under         Add a block div beneath the field containing the error text
39907 side          Add an error icon to the right of the field with a popup on hover
39908 [element id]  Add the error text directly to the innerHTML of the specified element
39909 </pre>
39910      */
39911     msgTarget : 'qtip',
39912     /**
39913      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39914      */
39915     msgFx : 'normal',
39916
39917     /**
39918      * @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.
39919      */
39920     readOnly : false,
39921
39922     /**
39923      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39924      */
39925     disabled : false,
39926
39927     /**
39928      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39929      */
39930     inputType : undefined,
39931     
39932     /**
39933      * @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).
39934          */
39935         tabIndex : undefined,
39936         
39937     // private
39938     isFormField : true,
39939
39940     // private
39941     hasFocus : false,
39942     /**
39943      * @property {Roo.Element} fieldEl
39944      * Element Containing the rendered Field (with label etc.)
39945      */
39946     /**
39947      * @cfg {Mixed} value A value to initialize this field with.
39948      */
39949     value : undefined,
39950
39951     /**
39952      * @cfg {String} name The field's HTML name attribute.
39953      */
39954     /**
39955      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39956      */
39957     // private
39958     loadedValue : false,
39959      
39960      
39961         // private ??
39962         initComponent : function(){
39963         Roo.form.Field.superclass.initComponent.call(this);
39964         this.addEvents({
39965             /**
39966              * @event focus
39967              * Fires when this field receives input focus.
39968              * @param {Roo.form.Field} this
39969              */
39970             focus : true,
39971             /**
39972              * @event blur
39973              * Fires when this field loses input focus.
39974              * @param {Roo.form.Field} this
39975              */
39976             blur : true,
39977             /**
39978              * @event specialkey
39979              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39980              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39981              * @param {Roo.form.Field} this
39982              * @param {Roo.EventObject} e The event object
39983              */
39984             specialkey : true,
39985             /**
39986              * @event change
39987              * Fires just before the field blurs if the field value has changed.
39988              * @param {Roo.form.Field} this
39989              * @param {Mixed} newValue The new value
39990              * @param {Mixed} oldValue The original value
39991              */
39992             change : true,
39993             /**
39994              * @event invalid
39995              * Fires after the field has been marked as invalid.
39996              * @param {Roo.form.Field} this
39997              * @param {String} msg The validation message
39998              */
39999             invalid : true,
40000             /**
40001              * @event valid
40002              * Fires after the field has been validated with no errors.
40003              * @param {Roo.form.Field} this
40004              */
40005             valid : true,
40006              /**
40007              * @event keyup
40008              * Fires after the key up
40009              * @param {Roo.form.Field} this
40010              * @param {Roo.EventObject}  e The event Object
40011              */
40012             keyup : true
40013         });
40014     },
40015
40016     /**
40017      * Returns the name attribute of the field if available
40018      * @return {String} name The field name
40019      */
40020     getName: function(){
40021          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40022     },
40023
40024     // private
40025     onRender : function(ct, position){
40026         Roo.form.Field.superclass.onRender.call(this, ct, position);
40027         if(!this.el){
40028             var cfg = this.getAutoCreate();
40029             if(!cfg.name){
40030                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40031             }
40032             if (!cfg.name.length) {
40033                 delete cfg.name;
40034             }
40035             if(this.inputType){
40036                 cfg.type = this.inputType;
40037             }
40038             this.el = ct.createChild(cfg, position);
40039         }
40040         var type = this.el.dom.type;
40041         if(type){
40042             if(type == 'password'){
40043                 type = 'text';
40044             }
40045             this.el.addClass('x-form-'+type);
40046         }
40047         if(this.readOnly){
40048             this.el.dom.readOnly = true;
40049         }
40050         if(this.tabIndex !== undefined){
40051             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40052         }
40053
40054         this.el.addClass([this.fieldClass, this.cls]);
40055         this.initValue();
40056     },
40057
40058     /**
40059      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40060      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40061      * @return {Roo.form.Field} this
40062      */
40063     applyTo : function(target){
40064         this.allowDomMove = false;
40065         this.el = Roo.get(target);
40066         this.render(this.el.dom.parentNode);
40067         return this;
40068     },
40069
40070     // private
40071     initValue : function(){
40072         if(this.value !== undefined){
40073             this.setValue(this.value);
40074         }else if(this.el.dom.value.length > 0){
40075             this.setValue(this.el.dom.value);
40076         }
40077     },
40078
40079     /**
40080      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40081      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40082      */
40083     isDirty : function() {
40084         if(this.disabled) {
40085             return false;
40086         }
40087         return String(this.getValue()) !== String(this.originalValue);
40088     },
40089
40090     /**
40091      * stores the current value in loadedValue
40092      */
40093     resetHasChanged : function()
40094     {
40095         this.loadedValue = String(this.getValue());
40096     },
40097     /**
40098      * checks the current value against the 'loaded' value.
40099      * Note - will return false if 'resetHasChanged' has not been called first.
40100      */
40101     hasChanged : function()
40102     {
40103         if(this.disabled || this.readOnly) {
40104             return false;
40105         }
40106         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40107     },
40108     
40109     
40110     
40111     // private
40112     afterRender : function(){
40113         Roo.form.Field.superclass.afterRender.call(this);
40114         this.initEvents();
40115     },
40116
40117     // private
40118     fireKey : function(e){
40119         //Roo.log('field ' + e.getKey());
40120         if(e.isNavKeyPress()){
40121             this.fireEvent("specialkey", this, e);
40122         }
40123     },
40124
40125     /**
40126      * Resets the current field value to the originally loaded value and clears any validation messages
40127      */
40128     reset : function(){
40129         this.setValue(this.resetValue);
40130         this.originalValue = this.getValue();
40131         this.clearInvalid();
40132     },
40133
40134     // private
40135     initEvents : function(){
40136         // safari killled keypress - so keydown is now used..
40137         this.el.on("keydown" , this.fireKey,  this);
40138         this.el.on("focus", this.onFocus,  this);
40139         this.el.on("blur", this.onBlur,  this);
40140         this.el.relayEvent('keyup', this);
40141
40142         // reference to original value for reset
40143         this.originalValue = this.getValue();
40144         this.resetValue =  this.getValue();
40145     },
40146
40147     // private
40148     onFocus : function(){
40149         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40150             this.el.addClass(this.focusClass);
40151         }
40152         if(!this.hasFocus){
40153             this.hasFocus = true;
40154             this.startValue = this.getValue();
40155             this.fireEvent("focus", this);
40156         }
40157     },
40158
40159     beforeBlur : Roo.emptyFn,
40160
40161     // private
40162     onBlur : function(){
40163         this.beforeBlur();
40164         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40165             this.el.removeClass(this.focusClass);
40166         }
40167         this.hasFocus = false;
40168         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40169             this.validate();
40170         }
40171         var v = this.getValue();
40172         if(String(v) !== String(this.startValue)){
40173             this.fireEvent('change', this, v, this.startValue);
40174         }
40175         this.fireEvent("blur", this);
40176     },
40177
40178     /**
40179      * Returns whether or not the field value is currently valid
40180      * @param {Boolean} preventMark True to disable marking the field invalid
40181      * @return {Boolean} True if the value is valid, else false
40182      */
40183     isValid : function(preventMark){
40184         if(this.disabled){
40185             return true;
40186         }
40187         var restore = this.preventMark;
40188         this.preventMark = preventMark === true;
40189         var v = this.validateValue(this.processValue(this.getRawValue()));
40190         this.preventMark = restore;
40191         return v;
40192     },
40193
40194     /**
40195      * Validates the field value
40196      * @return {Boolean} True if the value is valid, else false
40197      */
40198     validate : function(){
40199         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40200             this.clearInvalid();
40201             return true;
40202         }
40203         return false;
40204     },
40205
40206     processValue : function(value){
40207         return value;
40208     },
40209
40210     // private
40211     // Subclasses should provide the validation implementation by overriding this
40212     validateValue : function(value){
40213         return true;
40214     },
40215
40216     /**
40217      * Mark this field as invalid
40218      * @param {String} msg The validation message
40219      */
40220     markInvalid : function(msg){
40221         if(!this.rendered || this.preventMark){ // not rendered
40222             return;
40223         }
40224         
40225         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40226         
40227         obj.el.addClass(this.invalidClass);
40228         msg = msg || this.invalidText;
40229         switch(this.msgTarget){
40230             case 'qtip':
40231                 obj.el.dom.qtip = msg;
40232                 obj.el.dom.qclass = 'x-form-invalid-tip';
40233                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40234                     Roo.QuickTips.enable();
40235                 }
40236                 break;
40237             case 'title':
40238                 this.el.dom.title = msg;
40239                 break;
40240             case 'under':
40241                 if(!this.errorEl){
40242                     var elp = this.el.findParent('.x-form-element', 5, true);
40243                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40244                     this.errorEl.setWidth(elp.getWidth(true)-20);
40245                 }
40246                 this.errorEl.update(msg);
40247                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40248                 break;
40249             case 'side':
40250                 if(!this.errorIcon){
40251                     var elp = this.el.findParent('.x-form-element', 5, true);
40252                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40253                 }
40254                 this.alignErrorIcon();
40255                 this.errorIcon.dom.qtip = msg;
40256                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40257                 this.errorIcon.show();
40258                 this.on('resize', this.alignErrorIcon, this);
40259                 break;
40260             default:
40261                 var t = Roo.getDom(this.msgTarget);
40262                 t.innerHTML = msg;
40263                 t.style.display = this.msgDisplay;
40264                 break;
40265         }
40266         this.fireEvent('invalid', this, msg);
40267     },
40268
40269     // private
40270     alignErrorIcon : function(){
40271         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40272     },
40273
40274     /**
40275      * Clear any invalid styles/messages for this field
40276      */
40277     clearInvalid : function(){
40278         if(!this.rendered || this.preventMark){ // not rendered
40279             return;
40280         }
40281         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40282         
40283         obj.el.removeClass(this.invalidClass);
40284         switch(this.msgTarget){
40285             case 'qtip':
40286                 obj.el.dom.qtip = '';
40287                 break;
40288             case 'title':
40289                 this.el.dom.title = '';
40290                 break;
40291             case 'under':
40292                 if(this.errorEl){
40293                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40294                 }
40295                 break;
40296             case 'side':
40297                 if(this.errorIcon){
40298                     this.errorIcon.dom.qtip = '';
40299                     this.errorIcon.hide();
40300                     this.un('resize', this.alignErrorIcon, this);
40301                 }
40302                 break;
40303             default:
40304                 var t = Roo.getDom(this.msgTarget);
40305                 t.innerHTML = '';
40306                 t.style.display = 'none';
40307                 break;
40308         }
40309         this.fireEvent('valid', this);
40310     },
40311
40312     /**
40313      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40314      * @return {Mixed} value The field value
40315      */
40316     getRawValue : function(){
40317         var v = this.el.getValue();
40318         
40319         return v;
40320     },
40321
40322     /**
40323      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40324      * @return {Mixed} value The field value
40325      */
40326     getValue : function(){
40327         var v = this.el.getValue();
40328          
40329         return v;
40330     },
40331
40332     /**
40333      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40334      * @param {Mixed} value The value to set
40335      */
40336     setRawValue : function(v){
40337         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40338     },
40339
40340     /**
40341      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40342      * @param {Mixed} value The value to set
40343      */
40344     setValue : function(v){
40345         this.value = v;
40346         if(this.rendered){
40347             this.el.dom.value = (v === null || v === undefined ? '' : v);
40348              this.validate();
40349         }
40350     },
40351
40352     adjustSize : function(w, h){
40353         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40354         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40355         return s;
40356     },
40357
40358     adjustWidth : function(tag, w){
40359         tag = tag.toLowerCase();
40360         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40361             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40362                 if(tag == 'input'){
40363                     return w + 2;
40364                 }
40365                 if(tag == 'textarea'){
40366                     return w-2;
40367                 }
40368             }else if(Roo.isOpera){
40369                 if(tag == 'input'){
40370                     return w + 2;
40371                 }
40372                 if(tag == 'textarea'){
40373                     return w-2;
40374                 }
40375             }
40376         }
40377         return w;
40378     }
40379 });
40380
40381
40382 // anything other than normal should be considered experimental
40383 Roo.form.Field.msgFx = {
40384     normal : {
40385         show: function(msgEl, f){
40386             msgEl.setDisplayed('block');
40387         },
40388
40389         hide : function(msgEl, f){
40390             msgEl.setDisplayed(false).update('');
40391         }
40392     },
40393
40394     slide : {
40395         show: function(msgEl, f){
40396             msgEl.slideIn('t', {stopFx:true});
40397         },
40398
40399         hide : function(msgEl, f){
40400             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40401         }
40402     },
40403
40404     slideRight : {
40405         show: function(msgEl, f){
40406             msgEl.fixDisplay();
40407             msgEl.alignTo(f.el, 'tl-tr');
40408             msgEl.slideIn('l', {stopFx:true});
40409         },
40410
40411         hide : function(msgEl, f){
40412             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40413         }
40414     }
40415 };/*
40416  * Based on:
40417  * Ext JS Library 1.1.1
40418  * Copyright(c) 2006-2007, Ext JS, LLC.
40419  *
40420  * Originally Released Under LGPL - original licence link has changed is not relivant.
40421  *
40422  * Fork - LGPL
40423  * <script type="text/javascript">
40424  */
40425  
40426
40427 /**
40428  * @class Roo.form.TextField
40429  * @extends Roo.form.Field
40430  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40431  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40432  * @constructor
40433  * Creates a new TextField
40434  * @param {Object} config Configuration options
40435  */
40436 Roo.form.TextField = function(config){
40437     Roo.form.TextField.superclass.constructor.call(this, config);
40438     this.addEvents({
40439         /**
40440          * @event autosize
40441          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40442          * according to the default logic, but this event provides a hook for the developer to apply additional
40443          * logic at runtime to resize the field if needed.
40444              * @param {Roo.form.Field} this This text field
40445              * @param {Number} width The new field width
40446              */
40447         autosize : true
40448     });
40449 };
40450
40451 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40452     /**
40453      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40454      */
40455     grow : false,
40456     /**
40457      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40458      */
40459     growMin : 30,
40460     /**
40461      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40462      */
40463     growMax : 800,
40464     /**
40465      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40466      */
40467     vtype : null,
40468     /**
40469      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40470      */
40471     maskRe : null,
40472     /**
40473      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40474      */
40475     disableKeyFilter : false,
40476     /**
40477      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40478      */
40479     allowBlank : true,
40480     /**
40481      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40482      */
40483     minLength : 0,
40484     /**
40485      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40486      */
40487     maxLength : Number.MAX_VALUE,
40488     /**
40489      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40490      */
40491     minLengthText : "The minimum length for this field is {0}",
40492     /**
40493      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40494      */
40495     maxLengthText : "The maximum length for this field is {0}",
40496     /**
40497      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40498      */
40499     selectOnFocus : false,
40500     /**
40501      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40502      */    
40503     allowLeadingSpace : false,
40504     /**
40505      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40506      */
40507     blankText : "This field is required",
40508     /**
40509      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40510      * If available, this function will be called only after the basic validators all return true, and will be passed the
40511      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40512      */
40513     validator : null,
40514     /**
40515      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40516      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40517      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40518      */
40519     regex : null,
40520     /**
40521      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40522      */
40523     regexText : "",
40524     /**
40525      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40526      */
40527     emptyText : null,
40528    
40529
40530     // private
40531     initEvents : function()
40532     {
40533         if (this.emptyText) {
40534             this.el.attr('placeholder', this.emptyText);
40535         }
40536         
40537         Roo.form.TextField.superclass.initEvents.call(this);
40538         if(this.validationEvent == 'keyup'){
40539             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40540             this.el.on('keyup', this.filterValidation, this);
40541         }
40542         else if(this.validationEvent !== false){
40543             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40544         }
40545         
40546         if(this.selectOnFocus){
40547             this.on("focus", this.preFocus, this);
40548         }
40549         if (!this.allowLeadingSpace) {
40550             this.on('blur', this.cleanLeadingSpace, this);
40551         }
40552         
40553         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40554             this.el.on("keypress", this.filterKeys, this);
40555         }
40556         if(this.grow){
40557             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40558             this.el.on("click", this.autoSize,  this);
40559         }
40560         if(this.el.is('input[type=password]') && Roo.isSafari){
40561             this.el.on('keydown', this.SafariOnKeyDown, this);
40562         }
40563     },
40564
40565     processValue : function(value){
40566         if(this.stripCharsRe){
40567             var newValue = value.replace(this.stripCharsRe, '');
40568             if(newValue !== value){
40569                 this.setRawValue(newValue);
40570                 return newValue;
40571             }
40572         }
40573         return value;
40574     },
40575
40576     filterValidation : function(e){
40577         if(!e.isNavKeyPress()){
40578             this.validationTask.delay(this.validationDelay);
40579         }
40580     },
40581
40582     // private
40583     onKeyUp : function(e){
40584         if(!e.isNavKeyPress()){
40585             this.autoSize();
40586         }
40587     },
40588     // private - clean the leading white space
40589     cleanLeadingSpace : function(e)
40590     {
40591         if ( this.inputType == 'file') {
40592             return;
40593         }
40594         
40595         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40596     },
40597     /**
40598      * Resets the current field value to the originally-loaded value and clears any validation messages.
40599      *  
40600      */
40601     reset : function(){
40602         Roo.form.TextField.superclass.reset.call(this);
40603        
40604     }, 
40605     // private
40606     preFocus : function(){
40607         
40608         if(this.selectOnFocus){
40609             this.el.dom.select();
40610         }
40611     },
40612
40613     
40614     // private
40615     filterKeys : function(e){
40616         var k = e.getKey();
40617         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40618             return;
40619         }
40620         var c = e.getCharCode(), cc = String.fromCharCode(c);
40621         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40622             return;
40623         }
40624         if(!this.maskRe.test(cc)){
40625             e.stopEvent();
40626         }
40627     },
40628
40629     setValue : function(v){
40630         
40631         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40632         
40633         this.autoSize();
40634     },
40635
40636     /**
40637      * Validates a value according to the field's validation rules and marks the field as invalid
40638      * if the validation fails
40639      * @param {Mixed} value The value to validate
40640      * @return {Boolean} True if the value is valid, else false
40641      */
40642     validateValue : function(value){
40643         if(value.length < 1)  { // if it's blank
40644              if(this.allowBlank){
40645                 this.clearInvalid();
40646                 return true;
40647              }else{
40648                 this.markInvalid(this.blankText);
40649                 return false;
40650              }
40651         }
40652         if(value.length < this.minLength){
40653             this.markInvalid(String.format(this.minLengthText, this.minLength));
40654             return false;
40655         }
40656         if(value.length > this.maxLength){
40657             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40658             return false;
40659         }
40660         if(this.vtype){
40661             var vt = Roo.form.VTypes;
40662             if(!vt[this.vtype](value, this)){
40663                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40664                 return false;
40665             }
40666         }
40667         if(typeof this.validator == "function"){
40668             var msg = this.validator(value);
40669             if(msg !== true){
40670                 this.markInvalid(msg);
40671                 return false;
40672             }
40673         }
40674         if(this.regex && !this.regex.test(value)){
40675             this.markInvalid(this.regexText);
40676             return false;
40677         }
40678         return true;
40679     },
40680
40681     /**
40682      * Selects text in this field
40683      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40684      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40685      */
40686     selectText : function(start, end){
40687         var v = this.getRawValue();
40688         if(v.length > 0){
40689             start = start === undefined ? 0 : start;
40690             end = end === undefined ? v.length : end;
40691             var d = this.el.dom;
40692             if(d.setSelectionRange){
40693                 d.setSelectionRange(start, end);
40694             }else if(d.createTextRange){
40695                 var range = d.createTextRange();
40696                 range.moveStart("character", start);
40697                 range.moveEnd("character", v.length-end);
40698                 range.select();
40699             }
40700         }
40701     },
40702
40703     /**
40704      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40705      * This only takes effect if grow = true, and fires the autosize event.
40706      */
40707     autoSize : function(){
40708         if(!this.grow || !this.rendered){
40709             return;
40710         }
40711         if(!this.metrics){
40712             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40713         }
40714         var el = this.el;
40715         var v = el.dom.value;
40716         var d = document.createElement('div');
40717         d.appendChild(document.createTextNode(v));
40718         v = d.innerHTML;
40719         d = null;
40720         v += "&#160;";
40721         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40722         this.el.setWidth(w);
40723         this.fireEvent("autosize", this, w);
40724     },
40725     
40726     // private
40727     SafariOnKeyDown : function(event)
40728     {
40729         // this is a workaround for a password hang bug on chrome/ webkit.
40730         
40731         var isSelectAll = false;
40732         
40733         if(this.el.dom.selectionEnd > 0){
40734             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40735         }
40736         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40737             event.preventDefault();
40738             this.setValue('');
40739             return;
40740         }
40741         
40742         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40743             
40744             event.preventDefault();
40745             // this is very hacky as keydown always get's upper case.
40746             
40747             var cc = String.fromCharCode(event.getCharCode());
40748             
40749             
40750             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40751             
40752         }
40753         
40754         
40755     }
40756 });/*
40757  * Based on:
40758  * Ext JS Library 1.1.1
40759  * Copyright(c) 2006-2007, Ext JS, LLC.
40760  *
40761  * Originally Released Under LGPL - original licence link has changed is not relivant.
40762  *
40763  * Fork - LGPL
40764  * <script type="text/javascript">
40765  */
40766  
40767 /**
40768  * @class Roo.form.Hidden
40769  * @extends Roo.form.TextField
40770  * Simple Hidden element used on forms 
40771  * 
40772  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40773  * 
40774  * @constructor
40775  * Creates a new Hidden form element.
40776  * @param {Object} config Configuration options
40777  */
40778
40779
40780
40781 // easy hidden field...
40782 Roo.form.Hidden = function(config){
40783     Roo.form.Hidden.superclass.constructor.call(this, config);
40784 };
40785   
40786 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40787     fieldLabel:      '',
40788     inputType:      'hidden',
40789     width:          50,
40790     allowBlank:     true,
40791     labelSeparator: '',
40792     hidden:         true,
40793     itemCls :       'x-form-item-display-none'
40794
40795
40796 });
40797
40798
40799 /*
40800  * Based on:
40801  * Ext JS Library 1.1.1
40802  * Copyright(c) 2006-2007, Ext JS, LLC.
40803  *
40804  * Originally Released Under LGPL - original licence link has changed is not relivant.
40805  *
40806  * Fork - LGPL
40807  * <script type="text/javascript">
40808  */
40809  
40810 /**
40811  * @class Roo.form.TriggerField
40812  * @extends Roo.form.TextField
40813  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40814  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40815  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40816  * for which you can provide a custom implementation.  For example:
40817  * <pre><code>
40818 var trigger = new Roo.form.TriggerField();
40819 trigger.onTriggerClick = myTriggerFn;
40820 trigger.applyTo('my-field');
40821 </code></pre>
40822  *
40823  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40824  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40825  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40826  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40827  * @constructor
40828  * Create a new TriggerField.
40829  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40830  * to the base TextField)
40831  */
40832 Roo.form.TriggerField = function(config){
40833     this.mimicing = false;
40834     Roo.form.TriggerField.superclass.constructor.call(this, config);
40835 };
40836
40837 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40838     /**
40839      * @cfg {String} triggerClass A CSS class to apply to the trigger
40840      */
40841     /**
40842      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40843      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40844      */
40845     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40846     /**
40847      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40848      */
40849     hideTrigger:false,
40850
40851     /** @cfg {Boolean} grow @hide */
40852     /** @cfg {Number} growMin @hide */
40853     /** @cfg {Number} growMax @hide */
40854
40855     /**
40856      * @hide 
40857      * @method
40858      */
40859     autoSize: Roo.emptyFn,
40860     // private
40861     monitorTab : true,
40862     // private
40863     deferHeight : true,
40864
40865     
40866     actionMode : 'wrap',
40867     // private
40868     onResize : function(w, h){
40869         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40870         if(typeof w == 'number'){
40871             var x = w - this.trigger.getWidth();
40872             this.el.setWidth(this.adjustWidth('input', x));
40873             this.trigger.setStyle('left', x+'px');
40874         }
40875     },
40876
40877     // private
40878     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40879
40880     // private
40881     getResizeEl : function(){
40882         return this.wrap;
40883     },
40884
40885     // private
40886     getPositionEl : function(){
40887         return this.wrap;
40888     },
40889
40890     // private
40891     alignErrorIcon : function(){
40892         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40893     },
40894
40895     // private
40896     onRender : function(ct, position){
40897         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40898         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40899         this.trigger = this.wrap.createChild(this.triggerConfig ||
40900                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40901         if(this.hideTrigger){
40902             this.trigger.setDisplayed(false);
40903         }
40904         this.initTrigger();
40905         if(!this.width){
40906             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40907         }
40908     },
40909
40910     // private
40911     initTrigger : function(){
40912         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40913         this.trigger.addClassOnOver('x-form-trigger-over');
40914         this.trigger.addClassOnClick('x-form-trigger-click');
40915     },
40916
40917     // private
40918     onDestroy : function(){
40919         if(this.trigger){
40920             this.trigger.removeAllListeners();
40921             this.trigger.remove();
40922         }
40923         if(this.wrap){
40924             this.wrap.remove();
40925         }
40926         Roo.form.TriggerField.superclass.onDestroy.call(this);
40927     },
40928
40929     // private
40930     onFocus : function(){
40931         Roo.form.TriggerField.superclass.onFocus.call(this);
40932         if(!this.mimicing){
40933             this.wrap.addClass('x-trigger-wrap-focus');
40934             this.mimicing = true;
40935             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40936             if(this.monitorTab){
40937                 this.el.on("keydown", this.checkTab, this);
40938             }
40939         }
40940     },
40941
40942     // private
40943     checkTab : function(e){
40944         if(e.getKey() == e.TAB){
40945             this.triggerBlur();
40946         }
40947     },
40948
40949     // private
40950     onBlur : function(){
40951         // do nothing
40952     },
40953
40954     // private
40955     mimicBlur : function(e, t){
40956         if(!this.wrap.contains(t) && this.validateBlur()){
40957             this.triggerBlur();
40958         }
40959     },
40960
40961     // private
40962     triggerBlur : function(){
40963         this.mimicing = false;
40964         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40965         if(this.monitorTab){
40966             this.el.un("keydown", this.checkTab, this);
40967         }
40968         this.wrap.removeClass('x-trigger-wrap-focus');
40969         Roo.form.TriggerField.superclass.onBlur.call(this);
40970     },
40971
40972     // private
40973     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40974     validateBlur : function(e, t){
40975         return true;
40976     },
40977
40978     // private
40979     onDisable : function(){
40980         Roo.form.TriggerField.superclass.onDisable.call(this);
40981         if(this.wrap){
40982             this.wrap.addClass('x-item-disabled');
40983         }
40984     },
40985
40986     // private
40987     onEnable : function(){
40988         Roo.form.TriggerField.superclass.onEnable.call(this);
40989         if(this.wrap){
40990             this.wrap.removeClass('x-item-disabled');
40991         }
40992     },
40993
40994     // private
40995     onShow : function(){
40996         var ae = this.getActionEl();
40997         
40998         if(ae){
40999             ae.dom.style.display = '';
41000             ae.dom.style.visibility = 'visible';
41001         }
41002     },
41003
41004     // private
41005     
41006     onHide : function(){
41007         var ae = this.getActionEl();
41008         ae.dom.style.display = 'none';
41009     },
41010
41011     /**
41012      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41013      * by an implementing function.
41014      * @method
41015      * @param {EventObject} e
41016      */
41017     onTriggerClick : Roo.emptyFn
41018 });
41019
41020 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41021 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41022 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41023 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41024     initComponent : function(){
41025         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41026
41027         this.triggerConfig = {
41028             tag:'span', cls:'x-form-twin-triggers', cn:[
41029             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41030             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41031         ]};
41032     },
41033
41034     getTrigger : function(index){
41035         return this.triggers[index];
41036     },
41037
41038     initTrigger : function(){
41039         var ts = this.trigger.select('.x-form-trigger', true);
41040         this.wrap.setStyle('overflow', 'hidden');
41041         var triggerField = this;
41042         ts.each(function(t, all, index){
41043             t.hide = function(){
41044                 var w = triggerField.wrap.getWidth();
41045                 this.dom.style.display = 'none';
41046                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41047             };
41048             t.show = function(){
41049                 var w = triggerField.wrap.getWidth();
41050                 this.dom.style.display = '';
41051                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41052             };
41053             var triggerIndex = 'Trigger'+(index+1);
41054
41055             if(this['hide'+triggerIndex]){
41056                 t.dom.style.display = 'none';
41057             }
41058             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41059             t.addClassOnOver('x-form-trigger-over');
41060             t.addClassOnClick('x-form-trigger-click');
41061         }, this);
41062         this.triggers = ts.elements;
41063     },
41064
41065     onTrigger1Click : Roo.emptyFn,
41066     onTrigger2Click : Roo.emptyFn
41067 });/*
41068  * Based on:
41069  * Ext JS Library 1.1.1
41070  * Copyright(c) 2006-2007, Ext JS, LLC.
41071  *
41072  * Originally Released Under LGPL - original licence link has changed is not relivant.
41073  *
41074  * Fork - LGPL
41075  * <script type="text/javascript">
41076  */
41077  
41078 /**
41079  * @class Roo.form.TextArea
41080  * @extends Roo.form.TextField
41081  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41082  * support for auto-sizing.
41083  * @constructor
41084  * Creates a new TextArea
41085  * @param {Object} config Configuration options
41086  */
41087 Roo.form.TextArea = function(config){
41088     Roo.form.TextArea.superclass.constructor.call(this, config);
41089     // these are provided exchanges for backwards compat
41090     // minHeight/maxHeight were replaced by growMin/growMax to be
41091     // compatible with TextField growing config values
41092     if(this.minHeight !== undefined){
41093         this.growMin = this.minHeight;
41094     }
41095     if(this.maxHeight !== undefined){
41096         this.growMax = this.maxHeight;
41097     }
41098 };
41099
41100 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41101     /**
41102      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41103      */
41104     growMin : 60,
41105     /**
41106      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41107      */
41108     growMax: 1000,
41109     /**
41110      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41111      * in the field (equivalent to setting overflow: hidden, defaults to false)
41112      */
41113     preventScrollbars: false,
41114     /**
41115      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41116      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41117      */
41118
41119     // private
41120     onRender : function(ct, position){
41121         if(!this.el){
41122             this.defaultAutoCreate = {
41123                 tag: "textarea",
41124                 style:"width:300px;height:60px;",
41125                 autocomplete: "new-password"
41126             };
41127         }
41128         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41129         if(this.grow){
41130             this.textSizeEl = Roo.DomHelper.append(document.body, {
41131                 tag: "pre", cls: "x-form-grow-sizer"
41132             });
41133             if(this.preventScrollbars){
41134                 this.el.setStyle("overflow", "hidden");
41135             }
41136             this.el.setHeight(this.growMin);
41137         }
41138     },
41139
41140     onDestroy : function(){
41141         if(this.textSizeEl){
41142             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41143         }
41144         Roo.form.TextArea.superclass.onDestroy.call(this);
41145     },
41146
41147     // private
41148     onKeyUp : function(e){
41149         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41150             this.autoSize();
41151         }
41152     },
41153
41154     /**
41155      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41156      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41157      */
41158     autoSize : function(){
41159         if(!this.grow || !this.textSizeEl){
41160             return;
41161         }
41162         var el = this.el;
41163         var v = el.dom.value;
41164         var ts = this.textSizeEl;
41165
41166         ts.innerHTML = '';
41167         ts.appendChild(document.createTextNode(v));
41168         v = ts.innerHTML;
41169
41170         Roo.fly(ts).setWidth(this.el.getWidth());
41171         if(v.length < 1){
41172             v = "&#160;&#160;";
41173         }else{
41174             if(Roo.isIE){
41175                 v = v.replace(/\n/g, '<p>&#160;</p>');
41176             }
41177             v += "&#160;\n&#160;";
41178         }
41179         ts.innerHTML = v;
41180         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41181         if(h != this.lastHeight){
41182             this.lastHeight = h;
41183             this.el.setHeight(h);
41184             this.fireEvent("autosize", this, h);
41185         }
41186     }
41187 });/*
41188  * Based on:
41189  * Ext JS Library 1.1.1
41190  * Copyright(c) 2006-2007, Ext JS, LLC.
41191  *
41192  * Originally Released Under LGPL - original licence link has changed is not relivant.
41193  *
41194  * Fork - LGPL
41195  * <script type="text/javascript">
41196  */
41197  
41198
41199 /**
41200  * @class Roo.form.NumberField
41201  * @extends Roo.form.TextField
41202  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41203  * @constructor
41204  * Creates a new NumberField
41205  * @param {Object} config Configuration options
41206  */
41207 Roo.form.NumberField = function(config){
41208     Roo.form.NumberField.superclass.constructor.call(this, config);
41209 };
41210
41211 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41212     /**
41213      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41214      */
41215     fieldClass: "x-form-field x-form-num-field",
41216     /**
41217      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41218      */
41219     allowDecimals : true,
41220     /**
41221      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41222      */
41223     decimalSeparator : ".",
41224     /**
41225      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41226      */
41227     decimalPrecision : 2,
41228     /**
41229      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41230      */
41231     allowNegative : true,
41232     /**
41233      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41234      */
41235     minValue : Number.NEGATIVE_INFINITY,
41236     /**
41237      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41238      */
41239     maxValue : Number.MAX_VALUE,
41240     /**
41241      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41242      */
41243     minText : "The minimum value for this field is {0}",
41244     /**
41245      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41246      */
41247     maxText : "The maximum value for this field is {0}",
41248     /**
41249      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41250      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41251      */
41252     nanText : "{0} is not a valid number",
41253
41254     // private
41255     initEvents : function(){
41256         Roo.form.NumberField.superclass.initEvents.call(this);
41257         var allowed = "0123456789";
41258         if(this.allowDecimals){
41259             allowed += this.decimalSeparator;
41260         }
41261         if(this.allowNegative){
41262             allowed += "-";
41263         }
41264         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41265         var keyPress = function(e){
41266             var k = e.getKey();
41267             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41268                 return;
41269             }
41270             var c = e.getCharCode();
41271             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41272                 e.stopEvent();
41273             }
41274         };
41275         this.el.on("keypress", keyPress, this);
41276     },
41277
41278     // private
41279     validateValue : function(value){
41280         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41281             return false;
41282         }
41283         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41284              return true;
41285         }
41286         var num = this.parseValue(value);
41287         if(isNaN(num)){
41288             this.markInvalid(String.format(this.nanText, value));
41289             return false;
41290         }
41291         if(num < this.minValue){
41292             this.markInvalid(String.format(this.minText, this.minValue));
41293             return false;
41294         }
41295         if(num > this.maxValue){
41296             this.markInvalid(String.format(this.maxText, this.maxValue));
41297             return false;
41298         }
41299         return true;
41300     },
41301
41302     getValue : function(){
41303         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41304     },
41305
41306     // private
41307     parseValue : function(value){
41308         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41309         return isNaN(value) ? '' : value;
41310     },
41311
41312     // private
41313     fixPrecision : function(value){
41314         var nan = isNaN(value);
41315         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41316             return nan ? '' : value;
41317         }
41318         return parseFloat(value).toFixed(this.decimalPrecision);
41319     },
41320
41321     setValue : function(v){
41322         v = this.fixPrecision(v);
41323         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41324     },
41325
41326     // private
41327     decimalPrecisionFcn : function(v){
41328         return Math.floor(v);
41329     },
41330
41331     beforeBlur : function(){
41332         var v = this.parseValue(this.getRawValue());
41333         if(v){
41334             this.setValue(v);
41335         }
41336     }
41337 });/*
41338  * Based on:
41339  * Ext JS Library 1.1.1
41340  * Copyright(c) 2006-2007, Ext JS, LLC.
41341  *
41342  * Originally Released Under LGPL - original licence link has changed is not relivant.
41343  *
41344  * Fork - LGPL
41345  * <script type="text/javascript">
41346  */
41347  
41348 /**
41349  * @class Roo.form.DateField
41350  * @extends Roo.form.TriggerField
41351  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41352 * @constructor
41353 * Create a new DateField
41354 * @param {Object} config
41355  */
41356 Roo.form.DateField = function(config)
41357 {
41358     Roo.form.DateField.superclass.constructor.call(this, config);
41359     
41360       this.addEvents({
41361          
41362         /**
41363          * @event select
41364          * Fires when a date is selected
41365              * @param {Roo.form.DateField} combo This combo box
41366              * @param {Date} date The date selected
41367              */
41368         'select' : true
41369          
41370     });
41371     
41372     
41373     if(typeof this.minValue == "string") {
41374         this.minValue = this.parseDate(this.minValue);
41375     }
41376     if(typeof this.maxValue == "string") {
41377         this.maxValue = this.parseDate(this.maxValue);
41378     }
41379     this.ddMatch = null;
41380     if(this.disabledDates){
41381         var dd = this.disabledDates;
41382         var re = "(?:";
41383         for(var i = 0; i < dd.length; i++){
41384             re += dd[i];
41385             if(i != dd.length-1) {
41386                 re += "|";
41387             }
41388         }
41389         this.ddMatch = new RegExp(re + ")");
41390     }
41391 };
41392
41393 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41394     /**
41395      * @cfg {String} format
41396      * The default date format string which can be overriden for localization support.  The format must be
41397      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41398      */
41399     format : "m/d/y",
41400     /**
41401      * @cfg {String} altFormats
41402      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41403      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41404      */
41405     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41406     /**
41407      * @cfg {Array} disabledDays
41408      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41409      */
41410     disabledDays : null,
41411     /**
41412      * @cfg {String} disabledDaysText
41413      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41414      */
41415     disabledDaysText : "Disabled",
41416     /**
41417      * @cfg {Array} disabledDates
41418      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41419      * expression so they are very powerful. Some examples:
41420      * <ul>
41421      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41422      * <li>["03/08", "09/16"] would disable those days for every year</li>
41423      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41424      * <li>["03/../2006"] would disable every day in March 2006</li>
41425      * <li>["^03"] would disable every day in every March</li>
41426      * </ul>
41427      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41428      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41429      */
41430     disabledDates : null,
41431     /**
41432      * @cfg {String} disabledDatesText
41433      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41434      */
41435     disabledDatesText : "Disabled",
41436     /**
41437      * @cfg {Date/String} minValue
41438      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41439      * valid format (defaults to null).
41440      */
41441     minValue : null,
41442     /**
41443      * @cfg {Date/String} maxValue
41444      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41445      * valid format (defaults to null).
41446      */
41447     maxValue : null,
41448     /**
41449      * @cfg {String} minText
41450      * The error text to display when the date in the cell is before minValue (defaults to
41451      * 'The date in this field must be after {minValue}').
41452      */
41453     minText : "The date in this field must be equal to or after {0}",
41454     /**
41455      * @cfg {String} maxText
41456      * The error text to display when the date in the cell is after maxValue (defaults to
41457      * 'The date in this field must be before {maxValue}').
41458      */
41459     maxText : "The date in this field must be equal to or before {0}",
41460     /**
41461      * @cfg {String} invalidText
41462      * The error text to display when the date in the field is invalid (defaults to
41463      * '{value} is not a valid date - it must be in the format {format}').
41464      */
41465     invalidText : "{0} is not a valid date - it must be in the format {1}",
41466     /**
41467      * @cfg {String} triggerClass
41468      * An additional CSS class used to style the trigger button.  The trigger will always get the
41469      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41470      * which displays a calendar icon).
41471      */
41472     triggerClass : 'x-form-date-trigger',
41473     
41474
41475     /**
41476      * @cfg {Boolean} useIso
41477      * if enabled, then the date field will use a hidden field to store the 
41478      * real value as iso formated date. default (false)
41479      */ 
41480     useIso : false,
41481     /**
41482      * @cfg {String/Object} autoCreate
41483      * A DomHelper element spec, or true for a default element spec (defaults to
41484      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41485      */ 
41486     // private
41487     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41488     
41489     // private
41490     hiddenField: false,
41491     
41492     onRender : function(ct, position)
41493     {
41494         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41495         if (this.useIso) {
41496             //this.el.dom.removeAttribute('name'); 
41497             Roo.log("Changing name?");
41498             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41499             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41500                     'before', true);
41501             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41502             // prevent input submission
41503             this.hiddenName = this.name;
41504         }
41505             
41506             
41507     },
41508     
41509     // private
41510     validateValue : function(value)
41511     {
41512         value = this.formatDate(value);
41513         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41514             Roo.log('super failed');
41515             return false;
41516         }
41517         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41518              return true;
41519         }
41520         var svalue = value;
41521         value = this.parseDate(value);
41522         if(!value){
41523             Roo.log('parse date failed' + svalue);
41524             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41525             return false;
41526         }
41527         var time = value.getTime();
41528         if(this.minValue && time < this.minValue.getTime()){
41529             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41530             return false;
41531         }
41532         if(this.maxValue && time > this.maxValue.getTime()){
41533             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41534             return false;
41535         }
41536         if(this.disabledDays){
41537             var day = value.getDay();
41538             for(var i = 0; i < this.disabledDays.length; i++) {
41539                 if(day === this.disabledDays[i]){
41540                     this.markInvalid(this.disabledDaysText);
41541                     return false;
41542                 }
41543             }
41544         }
41545         var fvalue = this.formatDate(value);
41546         if(this.ddMatch && this.ddMatch.test(fvalue)){
41547             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41548             return false;
41549         }
41550         return true;
41551     },
41552
41553     // private
41554     // Provides logic to override the default TriggerField.validateBlur which just returns true
41555     validateBlur : function(){
41556         return !this.menu || !this.menu.isVisible();
41557     },
41558     
41559     getName: function()
41560     {
41561         // returns hidden if it's set..
41562         if (!this.rendered) {return ''};
41563         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41564         
41565     },
41566
41567     /**
41568      * Returns the current date value of the date field.
41569      * @return {Date} The date value
41570      */
41571     getValue : function(){
41572         
41573         return  this.hiddenField ?
41574                 this.hiddenField.value :
41575                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41576     },
41577
41578     /**
41579      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41580      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41581      * (the default format used is "m/d/y").
41582      * <br />Usage:
41583      * <pre><code>
41584 //All of these calls set the same date value (May 4, 2006)
41585
41586 //Pass a date object:
41587 var dt = new Date('5/4/06');
41588 dateField.setValue(dt);
41589
41590 //Pass a date string (default format):
41591 dateField.setValue('5/4/06');
41592
41593 //Pass a date string (custom format):
41594 dateField.format = 'Y-m-d';
41595 dateField.setValue('2006-5-4');
41596 </code></pre>
41597      * @param {String/Date} date The date or valid date string
41598      */
41599     setValue : function(date){
41600         if (this.hiddenField) {
41601             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41602         }
41603         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41604         // make sure the value field is always stored as a date..
41605         this.value = this.parseDate(date);
41606         
41607         
41608     },
41609
41610     // private
41611     parseDate : function(value){
41612         if(!value || value instanceof Date){
41613             return value;
41614         }
41615         var v = Date.parseDate(value, this.format);
41616          if (!v && this.useIso) {
41617             v = Date.parseDate(value, 'Y-m-d');
41618         }
41619         if(!v && this.altFormats){
41620             if(!this.altFormatsArray){
41621                 this.altFormatsArray = this.altFormats.split("|");
41622             }
41623             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41624                 v = Date.parseDate(value, this.altFormatsArray[i]);
41625             }
41626         }
41627         return v;
41628     },
41629
41630     // private
41631     formatDate : function(date, fmt){
41632         return (!date || !(date instanceof Date)) ?
41633                date : date.dateFormat(fmt || this.format);
41634     },
41635
41636     // private
41637     menuListeners : {
41638         select: function(m, d){
41639             
41640             this.setValue(d);
41641             this.fireEvent('select', this, d);
41642         },
41643         show : function(){ // retain focus styling
41644             this.onFocus();
41645         },
41646         hide : function(){
41647             this.focus.defer(10, this);
41648             var ml = this.menuListeners;
41649             this.menu.un("select", ml.select,  this);
41650             this.menu.un("show", ml.show,  this);
41651             this.menu.un("hide", ml.hide,  this);
41652         }
41653     },
41654
41655     // private
41656     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41657     onTriggerClick : function(){
41658         if(this.disabled){
41659             return;
41660         }
41661         if(this.menu == null){
41662             this.menu = new Roo.menu.DateMenu();
41663         }
41664         Roo.apply(this.menu.picker,  {
41665             showClear: this.allowBlank,
41666             minDate : this.minValue,
41667             maxDate : this.maxValue,
41668             disabledDatesRE : this.ddMatch,
41669             disabledDatesText : this.disabledDatesText,
41670             disabledDays : this.disabledDays,
41671             disabledDaysText : this.disabledDaysText,
41672             format : this.useIso ? 'Y-m-d' : this.format,
41673             minText : String.format(this.minText, this.formatDate(this.minValue)),
41674             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41675         });
41676         this.menu.on(Roo.apply({}, this.menuListeners, {
41677             scope:this
41678         }));
41679         this.menu.picker.setValue(this.getValue() || new Date());
41680         this.menu.show(this.el, "tl-bl?");
41681     },
41682
41683     beforeBlur : function(){
41684         var v = this.parseDate(this.getRawValue());
41685         if(v){
41686             this.setValue(v);
41687         }
41688     },
41689
41690     /*@
41691      * overide
41692      * 
41693      */
41694     isDirty : function() {
41695         if(this.disabled) {
41696             return false;
41697         }
41698         
41699         if(typeof(this.startValue) === 'undefined'){
41700             return false;
41701         }
41702         
41703         return String(this.getValue()) !== String(this.startValue);
41704         
41705     },
41706     // @overide
41707     cleanLeadingSpace : function(e)
41708     {
41709        return;
41710     }
41711     
41712 });/*
41713  * Based on:
41714  * Ext JS Library 1.1.1
41715  * Copyright(c) 2006-2007, Ext JS, LLC.
41716  *
41717  * Originally Released Under LGPL - original licence link has changed is not relivant.
41718  *
41719  * Fork - LGPL
41720  * <script type="text/javascript">
41721  */
41722  
41723 /**
41724  * @class Roo.form.MonthField
41725  * @extends Roo.form.TriggerField
41726  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41727 * @constructor
41728 * Create a new MonthField
41729 * @param {Object} config
41730  */
41731 Roo.form.MonthField = function(config){
41732     
41733     Roo.form.MonthField.superclass.constructor.call(this, config);
41734     
41735       this.addEvents({
41736          
41737         /**
41738          * @event select
41739          * Fires when a date is selected
41740              * @param {Roo.form.MonthFieeld} combo This combo box
41741              * @param {Date} date The date selected
41742              */
41743         'select' : true
41744          
41745     });
41746     
41747     
41748     if(typeof this.minValue == "string") {
41749         this.minValue = this.parseDate(this.minValue);
41750     }
41751     if(typeof this.maxValue == "string") {
41752         this.maxValue = this.parseDate(this.maxValue);
41753     }
41754     this.ddMatch = null;
41755     if(this.disabledDates){
41756         var dd = this.disabledDates;
41757         var re = "(?:";
41758         for(var i = 0; i < dd.length; i++){
41759             re += dd[i];
41760             if(i != dd.length-1) {
41761                 re += "|";
41762             }
41763         }
41764         this.ddMatch = new RegExp(re + ")");
41765     }
41766 };
41767
41768 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41769     /**
41770      * @cfg {String} format
41771      * The default date format string which can be overriden for localization support.  The format must be
41772      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41773      */
41774     format : "M Y",
41775     /**
41776      * @cfg {String} altFormats
41777      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41778      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41779      */
41780     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41781     /**
41782      * @cfg {Array} disabledDays
41783      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41784      */
41785     disabledDays : [0,1,2,3,4,5,6],
41786     /**
41787      * @cfg {String} disabledDaysText
41788      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41789      */
41790     disabledDaysText : "Disabled",
41791     /**
41792      * @cfg {Array} disabledDates
41793      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41794      * expression so they are very powerful. Some examples:
41795      * <ul>
41796      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41797      * <li>["03/08", "09/16"] would disable those days for every year</li>
41798      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41799      * <li>["03/../2006"] would disable every day in March 2006</li>
41800      * <li>["^03"] would disable every day in every March</li>
41801      * </ul>
41802      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41803      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41804      */
41805     disabledDates : null,
41806     /**
41807      * @cfg {String} disabledDatesText
41808      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41809      */
41810     disabledDatesText : "Disabled",
41811     /**
41812      * @cfg {Date/String} minValue
41813      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41814      * valid format (defaults to null).
41815      */
41816     minValue : null,
41817     /**
41818      * @cfg {Date/String} maxValue
41819      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41820      * valid format (defaults to null).
41821      */
41822     maxValue : null,
41823     /**
41824      * @cfg {String} minText
41825      * The error text to display when the date in the cell is before minValue (defaults to
41826      * 'The date in this field must be after {minValue}').
41827      */
41828     minText : "The date in this field must be equal to or after {0}",
41829     /**
41830      * @cfg {String} maxTextf
41831      * The error text to display when the date in the cell is after maxValue (defaults to
41832      * 'The date in this field must be before {maxValue}').
41833      */
41834     maxText : "The date in this field must be equal to or before {0}",
41835     /**
41836      * @cfg {String} invalidText
41837      * The error text to display when the date in the field is invalid (defaults to
41838      * '{value} is not a valid date - it must be in the format {format}').
41839      */
41840     invalidText : "{0} is not a valid date - it must be in the format {1}",
41841     /**
41842      * @cfg {String} triggerClass
41843      * An additional CSS class used to style the trigger button.  The trigger will always get the
41844      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41845      * which displays a calendar icon).
41846      */
41847     triggerClass : 'x-form-date-trigger',
41848     
41849
41850     /**
41851      * @cfg {Boolean} useIso
41852      * if enabled, then the date field will use a hidden field to store the 
41853      * real value as iso formated date. default (true)
41854      */ 
41855     useIso : true,
41856     /**
41857      * @cfg {String/Object} autoCreate
41858      * A DomHelper element spec, or true for a default element spec (defaults to
41859      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41860      */ 
41861     // private
41862     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41863     
41864     // private
41865     hiddenField: false,
41866     
41867     hideMonthPicker : false,
41868     
41869     onRender : function(ct, position)
41870     {
41871         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41872         if (this.useIso) {
41873             this.el.dom.removeAttribute('name'); 
41874             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41875                     'before', true);
41876             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41877             // prevent input submission
41878             this.hiddenName = this.name;
41879         }
41880             
41881             
41882     },
41883     
41884     // private
41885     validateValue : function(value)
41886     {
41887         value = this.formatDate(value);
41888         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41889             return false;
41890         }
41891         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41892              return true;
41893         }
41894         var svalue = value;
41895         value = this.parseDate(value);
41896         if(!value){
41897             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41898             return false;
41899         }
41900         var time = value.getTime();
41901         if(this.minValue && time < this.minValue.getTime()){
41902             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41903             return false;
41904         }
41905         if(this.maxValue && time > this.maxValue.getTime()){
41906             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41907             return false;
41908         }
41909         /*if(this.disabledDays){
41910             var day = value.getDay();
41911             for(var i = 0; i < this.disabledDays.length; i++) {
41912                 if(day === this.disabledDays[i]){
41913                     this.markInvalid(this.disabledDaysText);
41914                     return false;
41915                 }
41916             }
41917         }
41918         */
41919         var fvalue = this.formatDate(value);
41920         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41921             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41922             return false;
41923         }
41924         */
41925         return true;
41926     },
41927
41928     // private
41929     // Provides logic to override the default TriggerField.validateBlur which just returns true
41930     validateBlur : function(){
41931         return !this.menu || !this.menu.isVisible();
41932     },
41933
41934     /**
41935      * Returns the current date value of the date field.
41936      * @return {Date} The date value
41937      */
41938     getValue : function(){
41939         
41940         
41941         
41942         return  this.hiddenField ?
41943                 this.hiddenField.value :
41944                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41945     },
41946
41947     /**
41948      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41949      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41950      * (the default format used is "m/d/y").
41951      * <br />Usage:
41952      * <pre><code>
41953 //All of these calls set the same date value (May 4, 2006)
41954
41955 //Pass a date object:
41956 var dt = new Date('5/4/06');
41957 monthField.setValue(dt);
41958
41959 //Pass a date string (default format):
41960 monthField.setValue('5/4/06');
41961
41962 //Pass a date string (custom format):
41963 monthField.format = 'Y-m-d';
41964 monthField.setValue('2006-5-4');
41965 </code></pre>
41966      * @param {String/Date} date The date or valid date string
41967      */
41968     setValue : function(date){
41969         Roo.log('month setValue' + date);
41970         // can only be first of month..
41971         
41972         var val = this.parseDate(date);
41973         
41974         if (this.hiddenField) {
41975             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41976         }
41977         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41978         this.value = this.parseDate(date);
41979     },
41980
41981     // private
41982     parseDate : function(value){
41983         if(!value || value instanceof Date){
41984             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41985             return value;
41986         }
41987         var v = Date.parseDate(value, this.format);
41988         if (!v && this.useIso) {
41989             v = Date.parseDate(value, 'Y-m-d');
41990         }
41991         if (v) {
41992             // 
41993             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41994         }
41995         
41996         
41997         if(!v && this.altFormats){
41998             if(!this.altFormatsArray){
41999                 this.altFormatsArray = this.altFormats.split("|");
42000             }
42001             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42002                 v = Date.parseDate(value, this.altFormatsArray[i]);
42003             }
42004         }
42005         return v;
42006     },
42007
42008     // private
42009     formatDate : function(date, fmt){
42010         return (!date || !(date instanceof Date)) ?
42011                date : date.dateFormat(fmt || this.format);
42012     },
42013
42014     // private
42015     menuListeners : {
42016         select: function(m, d){
42017             this.setValue(d);
42018             this.fireEvent('select', this, d);
42019         },
42020         show : function(){ // retain focus styling
42021             this.onFocus();
42022         },
42023         hide : function(){
42024             this.focus.defer(10, this);
42025             var ml = this.menuListeners;
42026             this.menu.un("select", ml.select,  this);
42027             this.menu.un("show", ml.show,  this);
42028             this.menu.un("hide", ml.hide,  this);
42029         }
42030     },
42031     // private
42032     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42033     onTriggerClick : function(){
42034         if(this.disabled){
42035             return;
42036         }
42037         if(this.menu == null){
42038             this.menu = new Roo.menu.DateMenu();
42039            
42040         }
42041         
42042         Roo.apply(this.menu.picker,  {
42043             
42044             showClear: this.allowBlank,
42045             minDate : this.minValue,
42046             maxDate : this.maxValue,
42047             disabledDatesRE : this.ddMatch,
42048             disabledDatesText : this.disabledDatesText,
42049             
42050             format : this.useIso ? 'Y-m-d' : this.format,
42051             minText : String.format(this.minText, this.formatDate(this.minValue)),
42052             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42053             
42054         });
42055          this.menu.on(Roo.apply({}, this.menuListeners, {
42056             scope:this
42057         }));
42058        
42059         
42060         var m = this.menu;
42061         var p = m.picker;
42062         
42063         // hide month picker get's called when we called by 'before hide';
42064         
42065         var ignorehide = true;
42066         p.hideMonthPicker  = function(disableAnim){
42067             if (ignorehide) {
42068                 return;
42069             }
42070              if(this.monthPicker){
42071                 Roo.log("hideMonthPicker called");
42072                 if(disableAnim === true){
42073                     this.monthPicker.hide();
42074                 }else{
42075                     this.monthPicker.slideOut('t', {duration:.2});
42076                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42077                     p.fireEvent("select", this, this.value);
42078                     m.hide();
42079                 }
42080             }
42081         }
42082         
42083         Roo.log('picker set value');
42084         Roo.log(this.getValue());
42085         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42086         m.show(this.el, 'tl-bl?');
42087         ignorehide  = false;
42088         // this will trigger hideMonthPicker..
42089         
42090         
42091         // hidden the day picker
42092         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42093         
42094         
42095         
42096       
42097         
42098         p.showMonthPicker.defer(100, p);
42099     
42100         
42101        
42102     },
42103
42104     beforeBlur : function(){
42105         var v = this.parseDate(this.getRawValue());
42106         if(v){
42107             this.setValue(v);
42108         }
42109     }
42110
42111     /** @cfg {Boolean} grow @hide */
42112     /** @cfg {Number} growMin @hide */
42113     /** @cfg {Number} growMax @hide */
42114     /**
42115      * @hide
42116      * @method autoSize
42117      */
42118 });/*
42119  * Based on:
42120  * Ext JS Library 1.1.1
42121  * Copyright(c) 2006-2007, Ext JS, LLC.
42122  *
42123  * Originally Released Under LGPL - original licence link has changed is not relivant.
42124  *
42125  * Fork - LGPL
42126  * <script type="text/javascript">
42127  */
42128  
42129
42130 /**
42131  * @class Roo.form.ComboBox
42132  * @extends Roo.form.TriggerField
42133  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42134  * @constructor
42135  * Create a new ComboBox.
42136  * @param {Object} config Configuration options
42137  */
42138 Roo.form.ComboBox = function(config){
42139     Roo.form.ComboBox.superclass.constructor.call(this, config);
42140     this.addEvents({
42141         /**
42142          * @event expand
42143          * Fires when the dropdown list is expanded
42144              * @param {Roo.form.ComboBox} combo This combo box
42145              */
42146         'expand' : true,
42147         /**
42148          * @event collapse
42149          * Fires when the dropdown list is collapsed
42150              * @param {Roo.form.ComboBox} combo This combo box
42151              */
42152         'collapse' : true,
42153         /**
42154          * @event beforeselect
42155          * Fires before a list item is selected. Return false to cancel the selection.
42156              * @param {Roo.form.ComboBox} combo This combo box
42157              * @param {Roo.data.Record} record The data record returned from the underlying store
42158              * @param {Number} index The index of the selected item in the dropdown list
42159              */
42160         'beforeselect' : true,
42161         /**
42162          * @event select
42163          * Fires when a list item is selected
42164              * @param {Roo.form.ComboBox} combo This combo box
42165              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42166              * @param {Number} index The index of the selected item in the dropdown list
42167              */
42168         'select' : true,
42169         /**
42170          * @event beforequery
42171          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42172          * The event object passed has these properties:
42173              * @param {Roo.form.ComboBox} combo This combo box
42174              * @param {String} query The query
42175              * @param {Boolean} forceAll true to force "all" query
42176              * @param {Boolean} cancel true to cancel the query
42177              * @param {Object} e The query event object
42178              */
42179         'beforequery': true,
42180          /**
42181          * @event add
42182          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42183              * @param {Roo.form.ComboBox} combo This combo box
42184              */
42185         'add' : true,
42186         /**
42187          * @event edit
42188          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42189              * @param {Roo.form.ComboBox} combo This combo box
42190              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42191              */
42192         'edit' : true
42193         
42194         
42195     });
42196     if(this.transform){
42197         this.allowDomMove = false;
42198         var s = Roo.getDom(this.transform);
42199         if(!this.hiddenName){
42200             this.hiddenName = s.name;
42201         }
42202         if(!this.store){
42203             this.mode = 'local';
42204             var d = [], opts = s.options;
42205             for(var i = 0, len = opts.length;i < len; i++){
42206                 var o = opts[i];
42207                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42208                 if(o.selected) {
42209                     this.value = value;
42210                 }
42211                 d.push([value, o.text]);
42212             }
42213             this.store = new Roo.data.SimpleStore({
42214                 'id': 0,
42215                 fields: ['value', 'text'],
42216                 data : d
42217             });
42218             this.valueField = 'value';
42219             this.displayField = 'text';
42220         }
42221         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42222         if(!this.lazyRender){
42223             this.target = true;
42224             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42225             s.parentNode.removeChild(s); // remove it
42226             this.render(this.el.parentNode);
42227         }else{
42228             s.parentNode.removeChild(s); // remove it
42229         }
42230
42231     }
42232     if (this.store) {
42233         this.store = Roo.factory(this.store, Roo.data);
42234     }
42235     
42236     this.selectedIndex = -1;
42237     if(this.mode == 'local'){
42238         if(config.queryDelay === undefined){
42239             this.queryDelay = 10;
42240         }
42241         if(config.minChars === undefined){
42242             this.minChars = 0;
42243         }
42244     }
42245 };
42246
42247 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42248     /**
42249      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42250      */
42251     /**
42252      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42253      * rendering into an Roo.Editor, defaults to false)
42254      */
42255     /**
42256      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42257      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42258      */
42259     /**
42260      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42261      */
42262     /**
42263      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42264      * the dropdown list (defaults to undefined, with no header element)
42265      */
42266
42267      /**
42268      * @cfg {String/Roo.Template} tpl The template to use to render the output
42269      */
42270      
42271     // private
42272     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42273     /**
42274      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42275      */
42276     listWidth: undefined,
42277     /**
42278      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42279      * mode = 'remote' or 'text' if mode = 'local')
42280      */
42281     displayField: undefined,
42282     /**
42283      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42284      * mode = 'remote' or 'value' if mode = 'local'). 
42285      * Note: use of a valueField requires the user make a selection
42286      * in order for a value to be mapped.
42287      */
42288     valueField: undefined,
42289     
42290     
42291     /**
42292      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42293      * field's data value (defaults to the underlying DOM element's name)
42294      */
42295     hiddenName: undefined,
42296     /**
42297      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42298      */
42299     listClass: '',
42300     /**
42301      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42302      */
42303     selectedClass: 'x-combo-selected',
42304     /**
42305      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42306      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42307      * which displays a downward arrow icon).
42308      */
42309     triggerClass : 'x-form-arrow-trigger',
42310     /**
42311      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42312      */
42313     shadow:'sides',
42314     /**
42315      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42316      * anchor positions (defaults to 'tl-bl')
42317      */
42318     listAlign: 'tl-bl?',
42319     /**
42320      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42321      */
42322     maxHeight: 300,
42323     /**
42324      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42325      * query specified by the allQuery config option (defaults to 'query')
42326      */
42327     triggerAction: 'query',
42328     /**
42329      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42330      * (defaults to 4, does not apply if editable = false)
42331      */
42332     minChars : 4,
42333     /**
42334      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42335      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42336      */
42337     typeAhead: false,
42338     /**
42339      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42340      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42341      */
42342     queryDelay: 500,
42343     /**
42344      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42345      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42346      */
42347     pageSize: 0,
42348     /**
42349      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42350      * when editable = true (defaults to false)
42351      */
42352     selectOnFocus:false,
42353     /**
42354      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42355      */
42356     queryParam: 'query',
42357     /**
42358      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42359      * when mode = 'remote' (defaults to 'Loading...')
42360      */
42361     loadingText: 'Loading...',
42362     /**
42363      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42364      */
42365     resizable: false,
42366     /**
42367      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42368      */
42369     handleHeight : 8,
42370     /**
42371      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42372      * traditional select (defaults to true)
42373      */
42374     editable: true,
42375     /**
42376      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42377      */
42378     allQuery: '',
42379     /**
42380      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42381      */
42382     mode: 'remote',
42383     /**
42384      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42385      * listWidth has a higher value)
42386      */
42387     minListWidth : 70,
42388     /**
42389      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42390      * allow the user to set arbitrary text into the field (defaults to false)
42391      */
42392     forceSelection:false,
42393     /**
42394      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42395      * if typeAhead = true (defaults to 250)
42396      */
42397     typeAheadDelay : 250,
42398     /**
42399      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42400      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42401      */
42402     valueNotFoundText : undefined,
42403     /**
42404      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42405      */
42406     blockFocus : false,
42407     
42408     /**
42409      * @cfg {Boolean} disableClear Disable showing of clear button.
42410      */
42411     disableClear : false,
42412     /**
42413      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42414      */
42415     alwaysQuery : false,
42416     
42417     //private
42418     addicon : false,
42419     editicon: false,
42420     
42421     // element that contains real text value.. (when hidden is used..)
42422      
42423     // private
42424     onRender : function(ct, position)
42425     {
42426         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42427         
42428         if(this.hiddenName){
42429             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42430                     'before', true);
42431             this.hiddenField.value =
42432                 this.hiddenValue !== undefined ? this.hiddenValue :
42433                 this.value !== undefined ? this.value : '';
42434
42435             // prevent input submission
42436             this.el.dom.removeAttribute('name');
42437              
42438              
42439         }
42440         
42441         if(Roo.isGecko){
42442             this.el.dom.setAttribute('autocomplete', 'off');
42443         }
42444
42445         var cls = 'x-combo-list';
42446
42447         this.list = new Roo.Layer({
42448             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42449         });
42450
42451         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42452         this.list.setWidth(lw);
42453         this.list.swallowEvent('mousewheel');
42454         this.assetHeight = 0;
42455
42456         if(this.title){
42457             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42458             this.assetHeight += this.header.getHeight();
42459         }
42460
42461         this.innerList = this.list.createChild({cls:cls+'-inner'});
42462         this.innerList.on('mouseover', this.onViewOver, this);
42463         this.innerList.on('mousemove', this.onViewMove, this);
42464         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42465         
42466         if(this.allowBlank && !this.pageSize && !this.disableClear){
42467             this.footer = this.list.createChild({cls:cls+'-ft'});
42468             this.pageTb = new Roo.Toolbar(this.footer);
42469            
42470         }
42471         if(this.pageSize){
42472             this.footer = this.list.createChild({cls:cls+'-ft'});
42473             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42474                     {pageSize: this.pageSize});
42475             
42476         }
42477         
42478         if (this.pageTb && this.allowBlank && !this.disableClear) {
42479             var _this = this;
42480             this.pageTb.add(new Roo.Toolbar.Fill(), {
42481                 cls: 'x-btn-icon x-btn-clear',
42482                 text: '&#160;',
42483                 handler: function()
42484                 {
42485                     _this.collapse();
42486                     _this.clearValue();
42487                     _this.onSelect(false, -1);
42488                 }
42489             });
42490         }
42491         if (this.footer) {
42492             this.assetHeight += this.footer.getHeight();
42493         }
42494         
42495
42496         if(!this.tpl){
42497             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42498         }
42499
42500         this.view = new Roo.View(this.innerList, this.tpl, {
42501             singleSelect:true,
42502             store: this.store,
42503             selectedClass: this.selectedClass
42504         });
42505
42506         this.view.on('click', this.onViewClick, this);
42507
42508         this.store.on('beforeload', this.onBeforeLoad, this);
42509         this.store.on('load', this.onLoad, this);
42510         this.store.on('loadexception', this.onLoadException, this);
42511
42512         if(this.resizable){
42513             this.resizer = new Roo.Resizable(this.list,  {
42514                pinned:true, handles:'se'
42515             });
42516             this.resizer.on('resize', function(r, w, h){
42517                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42518                 this.listWidth = w;
42519                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42520                 this.restrictHeight();
42521             }, this);
42522             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42523         }
42524         if(!this.editable){
42525             this.editable = true;
42526             this.setEditable(false);
42527         }  
42528         
42529         
42530         if (typeof(this.events.add.listeners) != 'undefined') {
42531             
42532             this.addicon = this.wrap.createChild(
42533                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42534        
42535             this.addicon.on('click', function(e) {
42536                 this.fireEvent('add', this);
42537             }, this);
42538         }
42539         if (typeof(this.events.edit.listeners) != 'undefined') {
42540             
42541             this.editicon = this.wrap.createChild(
42542                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42543             if (this.addicon) {
42544                 this.editicon.setStyle('margin-left', '40px');
42545             }
42546             this.editicon.on('click', function(e) {
42547                 
42548                 // we fire even  if inothing is selected..
42549                 this.fireEvent('edit', this, this.lastData );
42550                 
42551             }, this);
42552         }
42553         
42554         
42555         
42556     },
42557
42558     // private
42559     initEvents : function(){
42560         Roo.form.ComboBox.superclass.initEvents.call(this);
42561
42562         this.keyNav = new Roo.KeyNav(this.el, {
42563             "up" : function(e){
42564                 this.inKeyMode = true;
42565                 this.selectPrev();
42566             },
42567
42568             "down" : function(e){
42569                 if(!this.isExpanded()){
42570                     this.onTriggerClick();
42571                 }else{
42572                     this.inKeyMode = true;
42573                     this.selectNext();
42574                 }
42575             },
42576
42577             "enter" : function(e){
42578                 this.onViewClick();
42579                 //return true;
42580             },
42581
42582             "esc" : function(e){
42583                 this.collapse();
42584             },
42585
42586             "tab" : function(e){
42587                 this.onViewClick(false);
42588                 this.fireEvent("specialkey", this, e);
42589                 return true;
42590             },
42591
42592             scope : this,
42593
42594             doRelay : function(foo, bar, hname){
42595                 if(hname == 'down' || this.scope.isExpanded()){
42596                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42597                 }
42598                 return true;
42599             },
42600
42601             forceKeyDown: true
42602         });
42603         this.queryDelay = Math.max(this.queryDelay || 10,
42604                 this.mode == 'local' ? 10 : 250);
42605         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42606         if(this.typeAhead){
42607             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42608         }
42609         if(this.editable !== false){
42610             this.el.on("keyup", this.onKeyUp, this);
42611         }
42612         if(this.forceSelection){
42613             this.on('blur', this.doForce, this);
42614         }
42615     },
42616
42617     onDestroy : function(){
42618         if(this.view){
42619             this.view.setStore(null);
42620             this.view.el.removeAllListeners();
42621             this.view.el.remove();
42622             this.view.purgeListeners();
42623         }
42624         if(this.list){
42625             this.list.destroy();
42626         }
42627         if(this.store){
42628             this.store.un('beforeload', this.onBeforeLoad, this);
42629             this.store.un('load', this.onLoad, this);
42630             this.store.un('loadexception', this.onLoadException, this);
42631         }
42632         Roo.form.ComboBox.superclass.onDestroy.call(this);
42633     },
42634
42635     // private
42636     fireKey : function(e){
42637         if(e.isNavKeyPress() && !this.list.isVisible()){
42638             this.fireEvent("specialkey", this, e);
42639         }
42640     },
42641
42642     // private
42643     onResize: function(w, h){
42644         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42645         
42646         if(typeof w != 'number'){
42647             // we do not handle it!?!?
42648             return;
42649         }
42650         var tw = this.trigger.getWidth();
42651         tw += this.addicon ? this.addicon.getWidth() : 0;
42652         tw += this.editicon ? this.editicon.getWidth() : 0;
42653         var x = w - tw;
42654         this.el.setWidth( this.adjustWidth('input', x));
42655             
42656         this.trigger.setStyle('left', x+'px');
42657         
42658         if(this.list && this.listWidth === undefined){
42659             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42660             this.list.setWidth(lw);
42661             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42662         }
42663         
42664     
42665         
42666     },
42667
42668     /**
42669      * Allow or prevent the user from directly editing the field text.  If false is passed,
42670      * the user will only be able to select from the items defined in the dropdown list.  This method
42671      * is the runtime equivalent of setting the 'editable' config option at config time.
42672      * @param {Boolean} value True to allow the user to directly edit the field text
42673      */
42674     setEditable : function(value){
42675         if(value == this.editable){
42676             return;
42677         }
42678         this.editable = value;
42679         if(!value){
42680             this.el.dom.setAttribute('readOnly', true);
42681             this.el.on('mousedown', this.onTriggerClick,  this);
42682             this.el.addClass('x-combo-noedit');
42683         }else{
42684             this.el.dom.setAttribute('readOnly', false);
42685             this.el.un('mousedown', this.onTriggerClick,  this);
42686             this.el.removeClass('x-combo-noedit');
42687         }
42688     },
42689
42690     // private
42691     onBeforeLoad : function(){
42692         if(!this.hasFocus){
42693             return;
42694         }
42695         this.innerList.update(this.loadingText ?
42696                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42697         this.restrictHeight();
42698         this.selectedIndex = -1;
42699     },
42700
42701     // private
42702     onLoad : function(){
42703         if(!this.hasFocus){
42704             return;
42705         }
42706         if(this.store.getCount() > 0){
42707             this.expand();
42708             this.restrictHeight();
42709             if(this.lastQuery == this.allQuery){
42710                 if(this.editable){
42711                     this.el.dom.select();
42712                 }
42713                 if(!this.selectByValue(this.value, true)){
42714                     this.select(0, true);
42715                 }
42716             }else{
42717                 this.selectNext();
42718                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42719                     this.taTask.delay(this.typeAheadDelay);
42720                 }
42721             }
42722         }else{
42723             this.onEmptyResults();
42724         }
42725         //this.el.focus();
42726     },
42727     // private
42728     onLoadException : function()
42729     {
42730         this.collapse();
42731         Roo.log(this.store.reader.jsonData);
42732         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42733             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42734         }
42735         
42736         
42737     },
42738     // private
42739     onTypeAhead : function(){
42740         if(this.store.getCount() > 0){
42741             var r = this.store.getAt(0);
42742             var newValue = r.data[this.displayField];
42743             var len = newValue.length;
42744             var selStart = this.getRawValue().length;
42745             if(selStart != len){
42746                 this.setRawValue(newValue);
42747                 this.selectText(selStart, newValue.length);
42748             }
42749         }
42750     },
42751
42752     // private
42753     onSelect : function(record, index){
42754         if(this.fireEvent('beforeselect', this, record, index) !== false){
42755             this.setFromData(index > -1 ? record.data : false);
42756             this.collapse();
42757             this.fireEvent('select', this, record, index);
42758         }
42759     },
42760
42761     /**
42762      * Returns the currently selected field value or empty string if no value is set.
42763      * @return {String} value The selected value
42764      */
42765     getValue : function(){
42766         if(this.valueField){
42767             return typeof this.value != 'undefined' ? this.value : '';
42768         }
42769         return Roo.form.ComboBox.superclass.getValue.call(this);
42770     },
42771
42772     /**
42773      * Clears any text/value currently set in the field
42774      */
42775     clearValue : function(){
42776         if(this.hiddenField){
42777             this.hiddenField.value = '';
42778         }
42779         this.value = '';
42780         this.setRawValue('');
42781         this.lastSelectionText = '';
42782         
42783     },
42784
42785     /**
42786      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42787      * will be displayed in the field.  If the value does not match the data value of an existing item,
42788      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42789      * Otherwise the field will be blank (although the value will still be set).
42790      * @param {String} value The value to match
42791      */
42792     setValue : function(v){
42793         var text = v;
42794         if(this.valueField){
42795             var r = this.findRecord(this.valueField, v);
42796             if(r){
42797                 text = r.data[this.displayField];
42798             }else if(this.valueNotFoundText !== undefined){
42799                 text = this.valueNotFoundText;
42800             }
42801         }
42802         this.lastSelectionText = text;
42803         if(this.hiddenField){
42804             this.hiddenField.value = v;
42805         }
42806         Roo.form.ComboBox.superclass.setValue.call(this, text);
42807         this.value = v;
42808     },
42809     /**
42810      * @property {Object} the last set data for the element
42811      */
42812     
42813     lastData : false,
42814     /**
42815      * Sets the value of the field based on a object which is related to the record format for the store.
42816      * @param {Object} value the value to set as. or false on reset?
42817      */
42818     setFromData : function(o){
42819         var dv = ''; // display value
42820         var vv = ''; // value value..
42821         this.lastData = o;
42822         if (this.displayField) {
42823             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42824         } else {
42825             // this is an error condition!!!
42826             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42827         }
42828         
42829         if(this.valueField){
42830             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42831         }
42832         if(this.hiddenField){
42833             this.hiddenField.value = vv;
42834             
42835             this.lastSelectionText = dv;
42836             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42837             this.value = vv;
42838             return;
42839         }
42840         // no hidden field.. - we store the value in 'value', but still display
42841         // display field!!!!
42842         this.lastSelectionText = dv;
42843         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42844         this.value = vv;
42845         
42846         
42847     },
42848     // private
42849     reset : function(){
42850         // overridden so that last data is reset..
42851         this.setValue(this.resetValue);
42852         this.originalValue = this.getValue();
42853         this.clearInvalid();
42854         this.lastData = false;
42855         if (this.view) {
42856             this.view.clearSelections();
42857         }
42858     },
42859     // private
42860     findRecord : function(prop, value){
42861         var record;
42862         if(this.store.getCount() > 0){
42863             this.store.each(function(r){
42864                 if(r.data[prop] == value){
42865                     record = r;
42866                     return false;
42867                 }
42868                 return true;
42869             });
42870         }
42871         return record;
42872     },
42873     
42874     getName: function()
42875     {
42876         // returns hidden if it's set..
42877         if (!this.rendered) {return ''};
42878         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42879         
42880     },
42881     // private
42882     onViewMove : function(e, t){
42883         this.inKeyMode = false;
42884     },
42885
42886     // private
42887     onViewOver : function(e, t){
42888         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42889             return;
42890         }
42891         var item = this.view.findItemFromChild(t);
42892         if(item){
42893             var index = this.view.indexOf(item);
42894             this.select(index, false);
42895         }
42896     },
42897
42898     // private
42899     onViewClick : function(doFocus)
42900     {
42901         var index = this.view.getSelectedIndexes()[0];
42902         var r = this.store.getAt(index);
42903         if(r){
42904             this.onSelect(r, index);
42905         }
42906         if(doFocus !== false && !this.blockFocus){
42907             this.el.focus();
42908         }
42909     },
42910
42911     // private
42912     restrictHeight : function(){
42913         this.innerList.dom.style.height = '';
42914         var inner = this.innerList.dom;
42915         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42916         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42917         this.list.beginUpdate();
42918         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42919         this.list.alignTo(this.el, this.listAlign);
42920         this.list.endUpdate();
42921     },
42922
42923     // private
42924     onEmptyResults : function(){
42925         this.collapse();
42926     },
42927
42928     /**
42929      * Returns true if the dropdown list is expanded, else false.
42930      */
42931     isExpanded : function(){
42932         return this.list.isVisible();
42933     },
42934
42935     /**
42936      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42937      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42938      * @param {String} value The data value of the item to select
42939      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42940      * selected item if it is not currently in view (defaults to true)
42941      * @return {Boolean} True if the value matched an item in the list, else false
42942      */
42943     selectByValue : function(v, scrollIntoView){
42944         if(v !== undefined && v !== null){
42945             var r = this.findRecord(this.valueField || this.displayField, v);
42946             if(r){
42947                 this.select(this.store.indexOf(r), scrollIntoView);
42948                 return true;
42949             }
42950         }
42951         return false;
42952     },
42953
42954     /**
42955      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42956      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42957      * @param {Number} index The zero-based index of the list item to select
42958      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42959      * selected item if it is not currently in view (defaults to true)
42960      */
42961     select : function(index, scrollIntoView){
42962         this.selectedIndex = index;
42963         this.view.select(index);
42964         if(scrollIntoView !== false){
42965             var el = this.view.getNode(index);
42966             if(el){
42967                 this.innerList.scrollChildIntoView(el, false);
42968             }
42969         }
42970     },
42971
42972     // private
42973     selectNext : function(){
42974         var ct = this.store.getCount();
42975         if(ct > 0){
42976             if(this.selectedIndex == -1){
42977                 this.select(0);
42978             }else if(this.selectedIndex < ct-1){
42979                 this.select(this.selectedIndex+1);
42980             }
42981         }
42982     },
42983
42984     // private
42985     selectPrev : function(){
42986         var ct = this.store.getCount();
42987         if(ct > 0){
42988             if(this.selectedIndex == -1){
42989                 this.select(0);
42990             }else if(this.selectedIndex != 0){
42991                 this.select(this.selectedIndex-1);
42992             }
42993         }
42994     },
42995
42996     // private
42997     onKeyUp : function(e){
42998         if(this.editable !== false && !e.isSpecialKey()){
42999             this.lastKey = e.getKey();
43000             this.dqTask.delay(this.queryDelay);
43001         }
43002     },
43003
43004     // private
43005     validateBlur : function(){
43006         return !this.list || !this.list.isVisible();   
43007     },
43008
43009     // private
43010     initQuery : function(){
43011         this.doQuery(this.getRawValue());
43012     },
43013
43014     // private
43015     doForce : function(){
43016         if(this.el.dom.value.length > 0){
43017             this.el.dom.value =
43018                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43019              
43020         }
43021     },
43022
43023     /**
43024      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43025      * query allowing the query action to be canceled if needed.
43026      * @param {String} query The SQL query to execute
43027      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43028      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43029      * saved in the current store (defaults to false)
43030      */
43031     doQuery : function(q, forceAll){
43032         if(q === undefined || q === null){
43033             q = '';
43034         }
43035         var qe = {
43036             query: q,
43037             forceAll: forceAll,
43038             combo: this,
43039             cancel:false
43040         };
43041         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43042             return false;
43043         }
43044         q = qe.query;
43045         forceAll = qe.forceAll;
43046         if(forceAll === true || (q.length >= this.minChars)){
43047             if(this.lastQuery != q || this.alwaysQuery){
43048                 this.lastQuery = q;
43049                 if(this.mode == 'local'){
43050                     this.selectedIndex = -1;
43051                     if(forceAll){
43052                         this.store.clearFilter();
43053                     }else{
43054                         this.store.filter(this.displayField, q);
43055                     }
43056                     this.onLoad();
43057                 }else{
43058                     this.store.baseParams[this.queryParam] = q;
43059                     this.store.load({
43060                         params: this.getParams(q)
43061                     });
43062                     this.expand();
43063                 }
43064             }else{
43065                 this.selectedIndex = -1;
43066                 this.onLoad();   
43067             }
43068         }
43069     },
43070
43071     // private
43072     getParams : function(q){
43073         var p = {};
43074         //p[this.queryParam] = q;
43075         if(this.pageSize){
43076             p.start = 0;
43077             p.limit = this.pageSize;
43078         }
43079         return p;
43080     },
43081
43082     /**
43083      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43084      */
43085     collapse : function(){
43086         if(!this.isExpanded()){
43087             return;
43088         }
43089         this.list.hide();
43090         Roo.get(document).un('mousedown', this.collapseIf, this);
43091         Roo.get(document).un('mousewheel', this.collapseIf, this);
43092         if (!this.editable) {
43093             Roo.get(document).un('keydown', this.listKeyPress, this);
43094         }
43095         this.fireEvent('collapse', this);
43096     },
43097
43098     // private
43099     collapseIf : function(e){
43100         if(!e.within(this.wrap) && !e.within(this.list)){
43101             this.collapse();
43102         }
43103     },
43104
43105     /**
43106      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43107      */
43108     expand : function(){
43109         if(this.isExpanded() || !this.hasFocus){
43110             return;
43111         }
43112         this.list.alignTo(this.el, this.listAlign);
43113         this.list.show();
43114         Roo.get(document).on('mousedown', this.collapseIf, this);
43115         Roo.get(document).on('mousewheel', this.collapseIf, this);
43116         if (!this.editable) {
43117             Roo.get(document).on('keydown', this.listKeyPress, this);
43118         }
43119         
43120         this.fireEvent('expand', this);
43121     },
43122
43123     // private
43124     // Implements the default empty TriggerField.onTriggerClick function
43125     onTriggerClick : function(){
43126         if(this.disabled){
43127             return;
43128         }
43129         if(this.isExpanded()){
43130             this.collapse();
43131             if (!this.blockFocus) {
43132                 this.el.focus();
43133             }
43134             
43135         }else {
43136             this.hasFocus = true;
43137             if(this.triggerAction == 'all') {
43138                 this.doQuery(this.allQuery, true);
43139             } else {
43140                 this.doQuery(this.getRawValue());
43141             }
43142             if (!this.blockFocus) {
43143                 this.el.focus();
43144             }
43145         }
43146     },
43147     listKeyPress : function(e)
43148     {
43149         //Roo.log('listkeypress');
43150         // scroll to first matching element based on key pres..
43151         if (e.isSpecialKey()) {
43152             return false;
43153         }
43154         var k = String.fromCharCode(e.getKey()).toUpperCase();
43155         //Roo.log(k);
43156         var match  = false;
43157         var csel = this.view.getSelectedNodes();
43158         var cselitem = false;
43159         if (csel.length) {
43160             var ix = this.view.indexOf(csel[0]);
43161             cselitem  = this.store.getAt(ix);
43162             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43163                 cselitem = false;
43164             }
43165             
43166         }
43167         
43168         this.store.each(function(v) { 
43169             if (cselitem) {
43170                 // start at existing selection.
43171                 if (cselitem.id == v.id) {
43172                     cselitem = false;
43173                 }
43174                 return;
43175             }
43176                 
43177             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43178                 match = this.store.indexOf(v);
43179                 return false;
43180             }
43181         }, this);
43182         
43183         if (match === false) {
43184             return true; // no more action?
43185         }
43186         // scroll to?
43187         this.view.select(match);
43188         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43189         sn.scrollIntoView(sn.dom.parentNode, false);
43190     } 
43191
43192     /** 
43193     * @cfg {Boolean} grow 
43194     * @hide 
43195     */
43196     /** 
43197     * @cfg {Number} growMin 
43198     * @hide 
43199     */
43200     /** 
43201     * @cfg {Number} growMax 
43202     * @hide 
43203     */
43204     /**
43205      * @hide
43206      * @method autoSize
43207      */
43208 });/*
43209  * Copyright(c) 2010-2012, Roo J Solutions Limited
43210  *
43211  * Licence LGPL
43212  *
43213  */
43214
43215 /**
43216  * @class Roo.form.ComboBoxArray
43217  * @extends Roo.form.TextField
43218  * A facebook style adder... for lists of email / people / countries  etc...
43219  * pick multiple items from a combo box, and shows each one.
43220  *
43221  *  Fred [x]  Brian [x]  [Pick another |v]
43222  *
43223  *
43224  *  For this to work: it needs various extra information
43225  *    - normal combo problay has
43226  *      name, hiddenName
43227  *    + displayField, valueField
43228  *
43229  *    For our purpose...
43230  *
43231  *
43232  *   If we change from 'extends' to wrapping...
43233  *   
43234  *  
43235  *
43236  
43237  
43238  * @constructor
43239  * Create a new ComboBoxArray.
43240  * @param {Object} config Configuration options
43241  */
43242  
43243
43244 Roo.form.ComboBoxArray = function(config)
43245 {
43246     this.addEvents({
43247         /**
43248          * @event beforeremove
43249          * Fires before remove the value from the list
43250              * @param {Roo.form.ComboBoxArray} _self This combo box array
43251              * @param {Roo.form.ComboBoxArray.Item} item removed item
43252              */
43253         'beforeremove' : true,
43254         /**
43255          * @event remove
43256          * Fires when remove the value from the list
43257              * @param {Roo.form.ComboBoxArray} _self This combo box array
43258              * @param {Roo.form.ComboBoxArray.Item} item removed item
43259              */
43260         'remove' : true
43261         
43262         
43263     });
43264     
43265     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43266     
43267     this.items = new Roo.util.MixedCollection(false);
43268     
43269     // construct the child combo...
43270     
43271     
43272     
43273     
43274    
43275     
43276 }
43277
43278  
43279 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43280
43281     /**
43282      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43283      */
43284     
43285     lastData : false,
43286     
43287     // behavies liek a hiddne field
43288     inputType:      'hidden',
43289     /**
43290      * @cfg {Number} width The width of the box that displays the selected element
43291      */ 
43292     width:          300,
43293
43294     
43295     
43296     /**
43297      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43298      */
43299     name : false,
43300     /**
43301      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43302      */
43303     hiddenName : false,
43304       /**
43305      * @cfg {String} seperator    The value seperator normally ',' 
43306      */
43307     seperator : ',',
43308     
43309     // private the array of items that are displayed..
43310     items  : false,
43311     // private - the hidden field el.
43312     hiddenEl : false,
43313     // private - the filed el..
43314     el : false,
43315     
43316     //validateValue : function() { return true; }, // all values are ok!
43317     //onAddClick: function() { },
43318     
43319     onRender : function(ct, position) 
43320     {
43321         
43322         // create the standard hidden element
43323         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43324         
43325         
43326         // give fake names to child combo;
43327         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43328         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43329         
43330         this.combo = Roo.factory(this.combo, Roo.form);
43331         this.combo.onRender(ct, position);
43332         if (typeof(this.combo.width) != 'undefined') {
43333             this.combo.onResize(this.combo.width,0);
43334         }
43335         
43336         this.combo.initEvents();
43337         
43338         // assigned so form know we need to do this..
43339         this.store          = this.combo.store;
43340         this.valueField     = this.combo.valueField;
43341         this.displayField   = this.combo.displayField ;
43342         
43343         
43344         this.combo.wrap.addClass('x-cbarray-grp');
43345         
43346         var cbwrap = this.combo.wrap.createChild(
43347             {tag: 'div', cls: 'x-cbarray-cb'},
43348             this.combo.el.dom
43349         );
43350         
43351              
43352         this.hiddenEl = this.combo.wrap.createChild({
43353             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43354         });
43355         this.el = this.combo.wrap.createChild({
43356             tag: 'input',  type:'hidden' , name: this.name, value : ''
43357         });
43358          //   this.el.dom.removeAttribute("name");
43359         
43360         
43361         this.outerWrap = this.combo.wrap;
43362         this.wrap = cbwrap;
43363         
43364         this.outerWrap.setWidth(this.width);
43365         this.outerWrap.dom.removeChild(this.el.dom);
43366         
43367         this.wrap.dom.appendChild(this.el.dom);
43368         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43369         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43370         
43371         this.combo.trigger.setStyle('position','relative');
43372         this.combo.trigger.setStyle('left', '0px');
43373         this.combo.trigger.setStyle('top', '2px');
43374         
43375         this.combo.el.setStyle('vertical-align', 'text-bottom');
43376         
43377         //this.trigger.setStyle('vertical-align', 'top');
43378         
43379         // this should use the code from combo really... on('add' ....)
43380         if (this.adder) {
43381             
43382         
43383             this.adder = this.outerWrap.createChild(
43384                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43385             var _t = this;
43386             this.adder.on('click', function(e) {
43387                 _t.fireEvent('adderclick', this, e);
43388             }, _t);
43389         }
43390         //var _t = this;
43391         //this.adder.on('click', this.onAddClick, _t);
43392         
43393         
43394         this.combo.on('select', function(cb, rec, ix) {
43395             this.addItem(rec.data);
43396             
43397             cb.setValue('');
43398             cb.el.dom.value = '';
43399             //cb.lastData = rec.data;
43400             // add to list
43401             
43402         }, this);
43403         
43404         
43405     },
43406     
43407     
43408     getName: function()
43409     {
43410         // returns hidden if it's set..
43411         if (!this.rendered) {return ''};
43412         return  this.hiddenName ? this.hiddenName : this.name;
43413         
43414     },
43415     
43416     
43417     onResize: function(w, h){
43418         
43419         return;
43420         // not sure if this is needed..
43421         //this.combo.onResize(w,h);
43422         
43423         if(typeof w != 'number'){
43424             // we do not handle it!?!?
43425             return;
43426         }
43427         var tw = this.combo.trigger.getWidth();
43428         tw += this.addicon ? this.addicon.getWidth() : 0;
43429         tw += this.editicon ? this.editicon.getWidth() : 0;
43430         var x = w - tw;
43431         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43432             
43433         this.combo.trigger.setStyle('left', '0px');
43434         
43435         if(this.list && this.listWidth === undefined){
43436             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43437             this.list.setWidth(lw);
43438             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43439         }
43440         
43441     
43442         
43443     },
43444     
43445     addItem: function(rec)
43446     {
43447         var valueField = this.combo.valueField;
43448         var displayField = this.combo.displayField;
43449         
43450         if (this.items.indexOfKey(rec[valueField]) > -1) {
43451             //console.log("GOT " + rec.data.id);
43452             return;
43453         }
43454         
43455         var x = new Roo.form.ComboBoxArray.Item({
43456             //id : rec[this.idField],
43457             data : rec,
43458             displayField : displayField ,
43459             tipField : displayField ,
43460             cb : this
43461         });
43462         // use the 
43463         this.items.add(rec[valueField],x);
43464         // add it before the element..
43465         this.updateHiddenEl();
43466         x.render(this.outerWrap, this.wrap.dom);
43467         // add the image handler..
43468     },
43469     
43470     updateHiddenEl : function()
43471     {
43472         this.validate();
43473         if (!this.hiddenEl) {
43474             return;
43475         }
43476         var ar = [];
43477         var idField = this.combo.valueField;
43478         
43479         this.items.each(function(f) {
43480             ar.push(f.data[idField]);
43481         });
43482         this.hiddenEl.dom.value = ar.join(this.seperator);
43483         this.validate();
43484     },
43485     
43486     reset : function()
43487     {
43488         this.items.clear();
43489         
43490         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43491            el.remove();
43492         });
43493         
43494         this.el.dom.value = '';
43495         if (this.hiddenEl) {
43496             this.hiddenEl.dom.value = '';
43497         }
43498         
43499     },
43500     getValue: function()
43501     {
43502         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43503     },
43504     setValue: function(v) // not a valid action - must use addItems..
43505     {
43506         
43507         this.reset();
43508          
43509         if (this.store.isLocal && (typeof(v) == 'string')) {
43510             // then we can use the store to find the values..
43511             // comma seperated at present.. this needs to allow JSON based encoding..
43512             this.hiddenEl.value  = v;
43513             var v_ar = [];
43514             Roo.each(v.split(this.seperator), function(k) {
43515                 Roo.log("CHECK " + this.valueField + ',' + k);
43516                 var li = this.store.query(this.valueField, k);
43517                 if (!li.length) {
43518                     return;
43519                 }
43520                 var add = {};
43521                 add[this.valueField] = k;
43522                 add[this.displayField] = li.item(0).data[this.displayField];
43523                 
43524                 this.addItem(add);
43525             }, this) 
43526              
43527         }
43528         if (typeof(v) == 'object' ) {
43529             // then let's assume it's an array of objects..
43530             Roo.each(v, function(l) {
43531                 var add = l;
43532                 if (typeof(l) == 'string') {
43533                     add = {};
43534                     add[this.valueField] = l;
43535                     add[this.displayField] = l
43536                 }
43537                 this.addItem(add);
43538             }, this);
43539              
43540         }
43541         
43542         
43543     },
43544     setFromData: function(v)
43545     {
43546         // this recieves an object, if setValues is called.
43547         this.reset();
43548         this.el.dom.value = v[this.displayField];
43549         this.hiddenEl.dom.value = v[this.valueField];
43550         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43551             return;
43552         }
43553         var kv = v[this.valueField];
43554         var dv = v[this.displayField];
43555         kv = typeof(kv) != 'string' ? '' : kv;
43556         dv = typeof(dv) != 'string' ? '' : dv;
43557         
43558         
43559         var keys = kv.split(this.seperator);
43560         var display = dv.split(this.seperator);
43561         for (var i = 0 ; i < keys.length; i++) {
43562             add = {};
43563             add[this.valueField] = keys[i];
43564             add[this.displayField] = display[i];
43565             this.addItem(add);
43566         }
43567       
43568         
43569     },
43570     
43571     /**
43572      * Validates the combox array value
43573      * @return {Boolean} True if the value is valid, else false
43574      */
43575     validate : function(){
43576         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43577             this.clearInvalid();
43578             return true;
43579         }
43580         return false;
43581     },
43582     
43583     validateValue : function(value){
43584         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43585         
43586     },
43587     
43588     /*@
43589      * overide
43590      * 
43591      */
43592     isDirty : function() {
43593         if(this.disabled) {
43594             return false;
43595         }
43596         
43597         try {
43598             var d = Roo.decode(String(this.originalValue));
43599         } catch (e) {
43600             return String(this.getValue()) !== String(this.originalValue);
43601         }
43602         
43603         var originalValue = [];
43604         
43605         for (var i = 0; i < d.length; i++){
43606             originalValue.push(d[i][this.valueField]);
43607         }
43608         
43609         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43610         
43611     }
43612     
43613 });
43614
43615
43616
43617 /**
43618  * @class Roo.form.ComboBoxArray.Item
43619  * @extends Roo.BoxComponent
43620  * A selected item in the list
43621  *  Fred [x]  Brian [x]  [Pick another |v]
43622  * 
43623  * @constructor
43624  * Create a new item.
43625  * @param {Object} config Configuration options
43626  */
43627  
43628 Roo.form.ComboBoxArray.Item = function(config) {
43629     config.id = Roo.id();
43630     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43631 }
43632
43633 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43634     data : {},
43635     cb: false,
43636     displayField : false,
43637     tipField : false,
43638     
43639     
43640     defaultAutoCreate : {
43641         tag: 'div',
43642         cls: 'x-cbarray-item',
43643         cn : [ 
43644             { tag: 'div' },
43645             {
43646                 tag: 'img',
43647                 width:16,
43648                 height : 16,
43649                 src : Roo.BLANK_IMAGE_URL ,
43650                 align: 'center'
43651             }
43652         ]
43653         
43654     },
43655     
43656  
43657     onRender : function(ct, position)
43658     {
43659         Roo.form.Field.superclass.onRender.call(this, ct, position);
43660         
43661         if(!this.el){
43662             var cfg = this.getAutoCreate();
43663             this.el = ct.createChild(cfg, position);
43664         }
43665         
43666         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43667         
43668         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43669             this.cb.renderer(this.data) :
43670             String.format('{0}',this.data[this.displayField]);
43671         
43672             
43673         this.el.child('div').dom.setAttribute('qtip',
43674                         String.format('{0}',this.data[this.tipField])
43675         );
43676         
43677         this.el.child('img').on('click', this.remove, this);
43678         
43679     },
43680    
43681     remove : function()
43682     {
43683         if(this.cb.disabled){
43684             return;
43685         }
43686         
43687         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43688             this.cb.items.remove(this);
43689             this.el.child('img').un('click', this.remove, this);
43690             this.el.remove();
43691             this.cb.updateHiddenEl();
43692
43693             this.cb.fireEvent('remove', this.cb, this);
43694         }
43695         
43696     }
43697 });/*
43698  * RooJS Library 1.1.1
43699  * Copyright(c) 2008-2011  Alan Knowles
43700  *
43701  * License - LGPL
43702  */
43703  
43704
43705 /**
43706  * @class Roo.form.ComboNested
43707  * @extends Roo.form.ComboBox
43708  * A combobox for that allows selection of nested items in a list,
43709  * eg.
43710  *
43711  *  Book
43712  *    -> red
43713  *    -> green
43714  *  Table
43715  *    -> square
43716  *      ->red
43717  *      ->green
43718  *    -> rectangle
43719  *      ->green
43720  *      
43721  * 
43722  * @constructor
43723  * Create a new ComboNested
43724  * @param {Object} config Configuration options
43725  */
43726 Roo.form.ComboNested = function(config){
43727     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43728     // should verify some data...
43729     // like
43730     // hiddenName = required..
43731     // displayField = required
43732     // valudField == required
43733     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43734     var _t = this;
43735     Roo.each(req, function(e) {
43736         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43737             throw "Roo.form.ComboNested : missing value for: " + e;
43738         }
43739     });
43740      
43741     
43742 };
43743
43744 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43745    
43746     /*
43747      * @config {Number} max Number of columns to show
43748      */
43749     
43750     maxColumns : 3,
43751    
43752     list : null, // the outermost div..
43753     innerLists : null, // the
43754     views : null,
43755     stores : null,
43756     // private
43757     loadingChildren : false,
43758     
43759     onRender : function(ct, position)
43760     {
43761         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43762         
43763         if(this.hiddenName){
43764             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43765                     'before', true);
43766             this.hiddenField.value =
43767                 this.hiddenValue !== undefined ? this.hiddenValue :
43768                 this.value !== undefined ? this.value : '';
43769
43770             // prevent input submission
43771             this.el.dom.removeAttribute('name');
43772              
43773              
43774         }
43775         
43776         if(Roo.isGecko){
43777             this.el.dom.setAttribute('autocomplete', 'off');
43778         }
43779
43780         var cls = 'x-combo-list';
43781
43782         this.list = new Roo.Layer({
43783             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43784         });
43785
43786         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43787         this.list.setWidth(lw);
43788         this.list.swallowEvent('mousewheel');
43789         this.assetHeight = 0;
43790
43791         if(this.title){
43792             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43793             this.assetHeight += this.header.getHeight();
43794         }
43795         this.innerLists = [];
43796         this.views = [];
43797         this.stores = [];
43798         for (var i =0 ; i < this.maxColumns; i++) {
43799             this.onRenderList( cls, i);
43800         }
43801         
43802         // always needs footer, as we are going to have an 'OK' button.
43803         this.footer = this.list.createChild({cls:cls+'-ft'});
43804         this.pageTb = new Roo.Toolbar(this.footer);  
43805         var _this = this;
43806         this.pageTb.add(  {
43807             
43808             text: 'Done',
43809             handler: function()
43810             {
43811                 _this.collapse();
43812             }
43813         });
43814         
43815         if ( this.allowBlank && !this.disableClear) {
43816             
43817             this.pageTb.add(new Roo.Toolbar.Fill(), {
43818                 cls: 'x-btn-icon x-btn-clear',
43819                 text: '&#160;',
43820                 handler: function()
43821                 {
43822                     _this.collapse();
43823                     _this.clearValue();
43824                     _this.onSelect(false, -1);
43825                 }
43826             });
43827         }
43828         if (this.footer) {
43829             this.assetHeight += this.footer.getHeight();
43830         }
43831         
43832     },
43833     onRenderList : function (  cls, i)
43834     {
43835         
43836         var lw = Math.floor(
43837                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43838         );
43839         
43840         this.list.setWidth(lw); // default to '1'
43841
43842         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43843         //il.on('mouseover', this.onViewOver, this, { list:  i });
43844         //il.on('mousemove', this.onViewMove, this, { list:  i });
43845         il.setWidth(lw);
43846         il.setStyle({ 'overflow-x' : 'hidden'});
43847
43848         if(!this.tpl){
43849             this.tpl = new Roo.Template({
43850                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43851                 isEmpty: function (value, allValues) {
43852                     //Roo.log(value);
43853                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43854                     return dl ? 'has-children' : 'no-children'
43855                 }
43856             });
43857         }
43858         
43859         var store  = this.store;
43860         if (i > 0) {
43861             store  = new Roo.data.SimpleStore({
43862                 //fields : this.store.reader.meta.fields,
43863                 reader : this.store.reader,
43864                 data : [ ]
43865             });
43866         }
43867         this.stores[i]  = store;
43868                   
43869         var view = this.views[i] = new Roo.View(
43870             il,
43871             this.tpl,
43872             {
43873                 singleSelect:true,
43874                 store: store,
43875                 selectedClass: this.selectedClass
43876             }
43877         );
43878         view.getEl().setWidth(lw);
43879         view.getEl().setStyle({
43880             position: i < 1 ? 'relative' : 'absolute',
43881             top: 0,
43882             left: (i * lw ) + 'px',
43883             display : i > 0 ? 'none' : 'block'
43884         });
43885         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43886         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43887         //view.on('click', this.onViewClick, this, { list : i });
43888
43889         store.on('beforeload', this.onBeforeLoad, this);
43890         store.on('load',  this.onLoad, this, { list  : i});
43891         store.on('loadexception', this.onLoadException, this);
43892
43893         // hide the other vies..
43894         
43895         
43896         
43897     },
43898       
43899     restrictHeight : function()
43900     {
43901         var mh = 0;
43902         Roo.each(this.innerLists, function(il,i) {
43903             var el = this.views[i].getEl();
43904             el.dom.style.height = '';
43905             var inner = el.dom;
43906             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43907             // only adjust heights on other ones..
43908             mh = Math.max(h, mh);
43909             if (i < 1) {
43910                 
43911                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43912                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43913                
43914             }
43915             
43916             
43917         }, this);
43918         
43919         this.list.beginUpdate();
43920         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43921         this.list.alignTo(this.el, this.listAlign);
43922         this.list.endUpdate();
43923         
43924     },
43925      
43926     
43927     // -- store handlers..
43928     // private
43929     onBeforeLoad : function()
43930     {
43931         if(!this.hasFocus){
43932             return;
43933         }
43934         this.innerLists[0].update(this.loadingText ?
43935                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43936         this.restrictHeight();
43937         this.selectedIndex = -1;
43938     },
43939     // private
43940     onLoad : function(a,b,c,d)
43941     {
43942         if (!this.loadingChildren) {
43943             // then we are loading the top level. - hide the children
43944             for (var i = 1;i < this.views.length; i++) {
43945                 this.views[i].getEl().setStyle({ display : 'none' });
43946             }
43947             var lw = Math.floor(
43948                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43949             );
43950         
43951              this.list.setWidth(lw); // default to '1'
43952
43953             
43954         }
43955         if(!this.hasFocus){
43956             return;
43957         }
43958         
43959         if(this.store.getCount() > 0) {
43960             this.expand();
43961             this.restrictHeight();   
43962         } else {
43963             this.onEmptyResults();
43964         }
43965         
43966         if (!this.loadingChildren) {
43967             this.selectActive();
43968         }
43969         /*
43970         this.stores[1].loadData([]);
43971         this.stores[2].loadData([]);
43972         this.views
43973         */    
43974     
43975         //this.el.focus();
43976     },
43977     
43978     
43979     // private
43980     onLoadException : function()
43981     {
43982         this.collapse();
43983         Roo.log(this.store.reader.jsonData);
43984         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43985             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43986         }
43987         
43988         
43989     },
43990     // no cleaning of leading spaces on blur here.
43991     cleanLeadingSpace : function(e) { },
43992     
43993
43994     onSelectChange : function (view, sels, opts )
43995     {
43996         var ix = view.getSelectedIndexes();
43997          
43998         if (opts.list > this.maxColumns - 2) {
43999             if (view.store.getCount()<  1) {
44000                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44001
44002             } else  {
44003                 if (ix.length) {
44004                     // used to clear ?? but if we are loading unselected 
44005                     this.setFromData(view.store.getAt(ix[0]).data);
44006                 }
44007                 
44008             }
44009             
44010             return;
44011         }
44012         
44013         if (!ix.length) {
44014             // this get's fired when trigger opens..
44015            // this.setFromData({});
44016             var str = this.stores[opts.list+1];
44017             str.data.clear(); // removeall wihtout the fire events..
44018             return;
44019         }
44020         
44021         var rec = view.store.getAt(ix[0]);
44022          
44023         this.setFromData(rec.data);
44024         this.fireEvent('select', this, rec, ix[0]);
44025         
44026         var lw = Math.floor(
44027              (
44028                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44029              ) / this.maxColumns
44030         );
44031         this.loadingChildren = true;
44032         this.stores[opts.list+1].loadDataFromChildren( rec );
44033         this.loadingChildren = false;
44034         var dl = this.stores[opts.list+1]. getTotalCount();
44035         
44036         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44037         
44038         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44039         for (var i = opts.list+2; i < this.views.length;i++) {
44040             this.views[i].getEl().setStyle({ display : 'none' });
44041         }
44042         
44043         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44044         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44045         
44046         if (this.isLoading) {
44047            // this.selectActive(opts.list);
44048         }
44049          
44050     },
44051     
44052     
44053     
44054     
44055     onDoubleClick : function()
44056     {
44057         this.collapse(); //??
44058     },
44059     
44060      
44061     
44062     
44063     
44064     // private
44065     recordToStack : function(store, prop, value, stack)
44066     {
44067         var cstore = new Roo.data.SimpleStore({
44068             //fields : this.store.reader.meta.fields, // we need array reader.. for
44069             reader : this.store.reader,
44070             data : [ ]
44071         });
44072         var _this = this;
44073         var record  = false;
44074         var srec = false;
44075         if(store.getCount() < 1){
44076             return false;
44077         }
44078         store.each(function(r){
44079             if(r.data[prop] == value){
44080                 record = r;
44081             srec = r;
44082                 return false;
44083             }
44084             if (r.data.cn && r.data.cn.length) {
44085                 cstore.loadDataFromChildren( r);
44086                 var cret = _this.recordToStack(cstore, prop, value, stack);
44087                 if (cret !== false) {
44088                     record = cret;
44089                     srec = r;
44090                     return false;
44091                 }
44092             }
44093              
44094             return true;
44095         });
44096         if (record == false) {
44097             return false
44098         }
44099         stack.unshift(srec);
44100         return record;
44101     },
44102     
44103     /*
44104      * find the stack of stores that match our value.
44105      *
44106      * 
44107      */
44108     
44109     selectActive : function ()
44110     {
44111         // if store is not loaded, then we will need to wait for that to happen first.
44112         var stack = [];
44113         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44114         for (var i = 0; i < stack.length; i++ ) {
44115             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44116         }
44117         
44118     }
44119         
44120          
44121     
44122     
44123     
44124     
44125 });/*
44126  * Based on:
44127  * Ext JS Library 1.1.1
44128  * Copyright(c) 2006-2007, Ext JS, LLC.
44129  *
44130  * Originally Released Under LGPL - original licence link has changed is not relivant.
44131  *
44132  * Fork - LGPL
44133  * <script type="text/javascript">
44134  */
44135 /**
44136  * @class Roo.form.Checkbox
44137  * @extends Roo.form.Field
44138  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44139  * @constructor
44140  * Creates a new Checkbox
44141  * @param {Object} config Configuration options
44142  */
44143 Roo.form.Checkbox = function(config){
44144     Roo.form.Checkbox.superclass.constructor.call(this, config);
44145     this.addEvents({
44146         /**
44147          * @event check
44148          * Fires when the checkbox is checked or unchecked.
44149              * @param {Roo.form.Checkbox} this This checkbox
44150              * @param {Boolean} checked The new checked value
44151              */
44152         check : true
44153     });
44154 };
44155
44156 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44157     /**
44158      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44159      */
44160     focusClass : undefined,
44161     /**
44162      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44163      */
44164     fieldClass: "x-form-field",
44165     /**
44166      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44167      */
44168     checked: false,
44169     /**
44170      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44171      * {tag: "input", type: "checkbox", autocomplete: "off"})
44172      */
44173     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44174     /**
44175      * @cfg {String} boxLabel The text that appears beside the checkbox
44176      */
44177     boxLabel : "",
44178     /**
44179      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44180      */  
44181     inputValue : '1',
44182     /**
44183      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44184      */
44185      valueOff: '0', // value when not checked..
44186
44187     actionMode : 'viewEl', 
44188     //
44189     // private
44190     itemCls : 'x-menu-check-item x-form-item',
44191     groupClass : 'x-menu-group-item',
44192     inputType : 'hidden',
44193     
44194     
44195     inSetChecked: false, // check that we are not calling self...
44196     
44197     inputElement: false, // real input element?
44198     basedOn: false, // ????
44199     
44200     isFormField: true, // not sure where this is needed!!!!
44201
44202     onResize : function(){
44203         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44204         if(!this.boxLabel){
44205             this.el.alignTo(this.wrap, 'c-c');
44206         }
44207     },
44208
44209     initEvents : function(){
44210         Roo.form.Checkbox.superclass.initEvents.call(this);
44211         this.el.on("click", this.onClick,  this);
44212         this.el.on("change", this.onClick,  this);
44213     },
44214
44215
44216     getResizeEl : function(){
44217         return this.wrap;
44218     },
44219
44220     getPositionEl : function(){
44221         return this.wrap;
44222     },
44223
44224     // private
44225     onRender : function(ct, position){
44226         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44227         /*
44228         if(this.inputValue !== undefined){
44229             this.el.dom.value = this.inputValue;
44230         }
44231         */
44232         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44233         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44234         var viewEl = this.wrap.createChild({ 
44235             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44236         this.viewEl = viewEl;   
44237         this.wrap.on('click', this.onClick,  this); 
44238         
44239         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44240         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44241         
44242         
44243         
44244         if(this.boxLabel){
44245             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44246         //    viewEl.on('click', this.onClick,  this); 
44247         }
44248         //if(this.checked){
44249             this.setChecked(this.checked);
44250         //}else{
44251             //this.checked = this.el.dom;
44252         //}
44253
44254     },
44255
44256     // private
44257     initValue : Roo.emptyFn,
44258
44259     /**
44260      * Returns the checked state of the checkbox.
44261      * @return {Boolean} True if checked, else false
44262      */
44263     getValue : function(){
44264         if(this.el){
44265             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44266         }
44267         return this.valueOff;
44268         
44269     },
44270
44271         // private
44272     onClick : function(){ 
44273         if (this.disabled) {
44274             return;
44275         }
44276         this.setChecked(!this.checked);
44277
44278         //if(this.el.dom.checked != this.checked){
44279         //    this.setValue(this.el.dom.checked);
44280        // }
44281     },
44282
44283     /**
44284      * Sets the checked state of the checkbox.
44285      * On is always based on a string comparison between inputValue and the param.
44286      * @param {Boolean/String} value - the value to set 
44287      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44288      */
44289     setValue : function(v,suppressEvent){
44290         
44291         
44292         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44293         //if(this.el && this.el.dom){
44294         //    this.el.dom.checked = this.checked;
44295         //    this.el.dom.defaultChecked = this.checked;
44296         //}
44297         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44298         //this.fireEvent("check", this, this.checked);
44299     },
44300     // private..
44301     setChecked : function(state,suppressEvent)
44302     {
44303         if (this.inSetChecked) {
44304             this.checked = state;
44305             return;
44306         }
44307         
44308     
44309         if(this.wrap){
44310             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44311         }
44312         this.checked = state;
44313         if(suppressEvent !== true){
44314             this.fireEvent('check', this, state);
44315         }
44316         this.inSetChecked = true;
44317         this.el.dom.value = state ? this.inputValue : this.valueOff;
44318         this.inSetChecked = false;
44319         
44320     },
44321     // handle setting of hidden value by some other method!!?!?
44322     setFromHidden: function()
44323     {
44324         if(!this.el){
44325             return;
44326         }
44327         //console.log("SET FROM HIDDEN");
44328         //alert('setFrom hidden');
44329         this.setValue(this.el.dom.value);
44330     },
44331     
44332     onDestroy : function()
44333     {
44334         if(this.viewEl){
44335             Roo.get(this.viewEl).remove();
44336         }
44337          
44338         Roo.form.Checkbox.superclass.onDestroy.call(this);
44339     },
44340     
44341     setBoxLabel : function(str)
44342     {
44343         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44344     }
44345
44346 });/*
44347  * Based on:
44348  * Ext JS Library 1.1.1
44349  * Copyright(c) 2006-2007, Ext JS, LLC.
44350  *
44351  * Originally Released Under LGPL - original licence link has changed is not relivant.
44352  *
44353  * Fork - LGPL
44354  * <script type="text/javascript">
44355  */
44356  
44357 /**
44358  * @class Roo.form.Radio
44359  * @extends Roo.form.Checkbox
44360  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44361  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44362  * @constructor
44363  * Creates a new Radio
44364  * @param {Object} config Configuration options
44365  */
44366 Roo.form.Radio = function(){
44367     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44368 };
44369 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44370     inputType: 'radio',
44371
44372     /**
44373      * If this radio is part of a group, it will return the selected value
44374      * @return {String}
44375      */
44376     getGroupValue : function(){
44377         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44378     },
44379     
44380     
44381     onRender : function(ct, position){
44382         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44383         
44384         if(this.inputValue !== undefined){
44385             this.el.dom.value = this.inputValue;
44386         }
44387          
44388         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44389         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44390         //var viewEl = this.wrap.createChild({ 
44391         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44392         //this.viewEl = viewEl;   
44393         //this.wrap.on('click', this.onClick,  this); 
44394         
44395         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44396         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44397         
44398         
44399         
44400         if(this.boxLabel){
44401             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44402         //    viewEl.on('click', this.onClick,  this); 
44403         }
44404          if(this.checked){
44405             this.el.dom.checked =   'checked' ;
44406         }
44407          
44408     } 
44409     
44410     
44411 });//<script type="text/javascript">
44412
44413 /*
44414  * Based  Ext JS Library 1.1.1
44415  * Copyright(c) 2006-2007, Ext JS, LLC.
44416  * LGPL
44417  *
44418  */
44419  
44420 /**
44421  * @class Roo.HtmlEditorCore
44422  * @extends Roo.Component
44423  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
44424  *
44425  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44426  */
44427
44428 Roo.HtmlEditorCore = function(config){
44429     
44430     
44431     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
44432     
44433     
44434     this.addEvents({
44435         /**
44436          * @event initialize
44437          * Fires when the editor is fully initialized (including the iframe)
44438          * @param {Roo.HtmlEditorCore} this
44439          */
44440         initialize: true,
44441         /**
44442          * @event activate
44443          * Fires when the editor is first receives the focus. Any insertion must wait
44444          * until after this event.
44445          * @param {Roo.HtmlEditorCore} this
44446          */
44447         activate: true,
44448          /**
44449          * @event beforesync
44450          * Fires before the textarea is updated with content from the editor iframe. Return false
44451          * to cancel the sync.
44452          * @param {Roo.HtmlEditorCore} this
44453          * @param {String} html
44454          */
44455         beforesync: true,
44456          /**
44457          * @event beforepush
44458          * Fires before the iframe editor is updated with content from the textarea. Return false
44459          * to cancel the push.
44460          * @param {Roo.HtmlEditorCore} this
44461          * @param {String} html
44462          */
44463         beforepush: true,
44464          /**
44465          * @event sync
44466          * Fires when the textarea is updated with content from the editor iframe.
44467          * @param {Roo.HtmlEditorCore} this
44468          * @param {String} html
44469          */
44470         sync: true,
44471          /**
44472          * @event push
44473          * Fires when the iframe editor is updated with content from the textarea.
44474          * @param {Roo.HtmlEditorCore} this
44475          * @param {String} html
44476          */
44477         push: true,
44478         
44479         /**
44480          * @event editorevent
44481          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44482          * @param {Roo.HtmlEditorCore} this
44483          */
44484         editorevent: true
44485         
44486     });
44487     
44488     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
44489     
44490     // defaults : white / black...
44491     this.applyBlacklists();
44492     
44493     
44494     
44495 };
44496
44497
44498 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
44499
44500
44501      /**
44502      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
44503      */
44504     
44505     owner : false,
44506     
44507      /**
44508      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44509      *                        Roo.resizable.
44510      */
44511     resizable : false,
44512      /**
44513      * @cfg {Number} height (in pixels)
44514      */   
44515     height: 300,
44516    /**
44517      * @cfg {Number} width (in pixels)
44518      */   
44519     width: 500,
44520     
44521     /**
44522      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44523      * 
44524      */
44525     stylesheets: false,
44526     
44527     /**
44528      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
44529      */
44530     allowComments: false,
44531     // id of frame..
44532     frameId: false,
44533     
44534     // private properties
44535     validationEvent : false,
44536     deferHeight: true,
44537     initialized : false,
44538     activated : false,
44539     sourceEditMode : false,
44540     onFocus : Roo.emptyFn,
44541     iframePad:3,
44542     hideMode:'offsets',
44543     
44544     clearUp: true,
44545     
44546     // blacklist + whitelisted elements..
44547     black: false,
44548     white: false,
44549      
44550     bodyCls : '',
44551
44552     /**
44553      * Protected method that will not generally be called directly. It
44554      * is called when the editor initializes the iframe with HTML contents. Override this method if you
44555      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
44556      */
44557     getDocMarkup : function(){
44558         // body styles..
44559         var st = '';
44560         
44561         // inherit styels from page...?? 
44562         if (this.stylesheets === false) {
44563             
44564             Roo.get(document.head).select('style').each(function(node) {
44565                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44566             });
44567             
44568             Roo.get(document.head).select('link').each(function(node) { 
44569                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44570             });
44571             
44572         } else if (!this.stylesheets.length) {
44573                 // simple..
44574                 st = '<style type="text/css">' +
44575                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44576                    '</style>';
44577         } else {
44578             for (var i in this.stylesheets) { 
44579                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
44580             }
44581             
44582         }
44583         
44584         st +=  '<style type="text/css">' +
44585             'IMG { cursor: pointer } ' +
44586         '</style>';
44587
44588         var cls = 'roo-htmleditor-body';
44589         
44590         if(this.bodyCls.length){
44591             cls += ' ' + this.bodyCls;
44592         }
44593         
44594         return '<html><head>' + st  +
44595             //<style type="text/css">' +
44596             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44597             //'</style>' +
44598             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
44599     },
44600
44601     // private
44602     onRender : function(ct, position)
44603     {
44604         var _t = this;
44605         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
44606         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
44607         
44608         
44609         this.el.dom.style.border = '0 none';
44610         this.el.dom.setAttribute('tabIndex', -1);
44611         this.el.addClass('x-hidden hide');
44612         
44613         
44614         
44615         if(Roo.isIE){ // fix IE 1px bogus margin
44616             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
44617         }
44618        
44619         
44620         this.frameId = Roo.id();
44621         
44622          
44623         
44624         var iframe = this.owner.wrap.createChild({
44625             tag: 'iframe',
44626             cls: 'form-control', // bootstrap..
44627             id: this.frameId,
44628             name: this.frameId,
44629             frameBorder : 'no',
44630             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
44631         }, this.el
44632         );
44633         
44634         
44635         this.iframe = iframe.dom;
44636
44637          this.assignDocWin();
44638         
44639         this.doc.designMode = 'on';
44640        
44641         this.doc.open();
44642         this.doc.write(this.getDocMarkup());
44643         this.doc.close();
44644
44645         
44646         var task = { // must defer to wait for browser to be ready
44647             run : function(){
44648                 //console.log("run task?" + this.doc.readyState);
44649                 this.assignDocWin();
44650                 if(this.doc.body || this.doc.readyState == 'complete'){
44651                     try {
44652                         this.doc.designMode="on";
44653                     } catch (e) {
44654                         return;
44655                     }
44656                     Roo.TaskMgr.stop(task);
44657                     this.initEditor.defer(10, this);
44658                 }
44659             },
44660             interval : 10,
44661             duration: 10000,
44662             scope: this
44663         };
44664         Roo.TaskMgr.start(task);
44665
44666     },
44667
44668     // private
44669     onResize : function(w, h)
44670     {
44671          Roo.log('resize: ' +w + ',' + h );
44672         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
44673         if(!this.iframe){
44674             return;
44675         }
44676         if(typeof w == 'number'){
44677             
44678             this.iframe.style.width = w + 'px';
44679         }
44680         if(typeof h == 'number'){
44681             
44682             this.iframe.style.height = h + 'px';
44683             if(this.doc){
44684                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
44685             }
44686         }
44687         
44688     },
44689
44690     /**
44691      * Toggles the editor between standard and source edit mode.
44692      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44693      */
44694     toggleSourceEdit : function(sourceEditMode){
44695         
44696         this.sourceEditMode = sourceEditMode === true;
44697         
44698         if(this.sourceEditMode){
44699  
44700             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
44701             
44702         }else{
44703             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
44704             //this.iframe.className = '';
44705             this.deferFocus();
44706         }
44707         //this.setSize(this.owner.wrap.getSize());
44708         //this.fireEvent('editmodechange', this, this.sourceEditMode);
44709     },
44710
44711     
44712   
44713
44714     /**
44715      * Protected method that will not generally be called directly. If you need/want
44716      * custom HTML cleanup, this is the method you should override.
44717      * @param {String} html The HTML to be cleaned
44718      * return {String} The cleaned HTML
44719      */
44720     cleanHtml : function(html){
44721         html = String(html);
44722         if(html.length > 5){
44723             if(Roo.isSafari){ // strip safari nonsense
44724                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44725             }
44726         }
44727         if(html == '&nbsp;'){
44728             html = '';
44729         }
44730         return html;
44731     },
44732
44733     /**
44734      * HTML Editor -> Textarea
44735      * Protected method that will not generally be called directly. Syncs the contents
44736      * of the editor iframe with the textarea.
44737      */
44738     syncValue : function(){
44739         if(this.initialized){
44740             var bd = (this.doc.body || this.doc.documentElement);
44741             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44742             var html = bd.innerHTML;
44743             if(Roo.isSafari){
44744                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44745                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44746                 if(m && m[1]){
44747                     html = '<div style="'+m[0]+'">' + html + '</div>';
44748                 }
44749             }
44750             html = this.cleanHtml(html);
44751             // fix up the special chars.. normaly like back quotes in word...
44752             // however we do not want to do this with chinese..
44753             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44754                 
44755                 var cc = match.charCodeAt();
44756
44757                 // Get the character value, handling surrogate pairs
44758                 if (match.length == 2) {
44759                     // It's a surrogate pair, calculate the Unicode code point
44760                     var high = match.charCodeAt(0) - 0xD800;
44761                     var low  = match.charCodeAt(1) - 0xDC00;
44762                     cc = (high * 0x400) + low + 0x10000;
44763                 }  else if (
44764                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44765                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44766                     (cc >= 0xf900 && cc < 0xfb00 )
44767                 ) {
44768                         return match;
44769                 }  
44770          
44771                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44772                 return "&#" + cc + ";";
44773                 
44774                 
44775             });
44776             
44777             
44778              
44779             if(this.owner.fireEvent('beforesync', this, html) !== false){
44780                 this.el.dom.value = html;
44781                 this.owner.fireEvent('sync', this, html);
44782             }
44783         }
44784     },
44785
44786     /**
44787      * Protected method that will not generally be called directly. Pushes the value of the textarea
44788      * into the iframe editor.
44789      */
44790     pushValue : function(){
44791         if(this.initialized){
44792             var v = this.el.dom.value.trim();
44793             
44794 //            if(v.length < 1){
44795 //                v = '&#160;';
44796 //            }
44797             
44798             if(this.owner.fireEvent('beforepush', this, v) !== false){
44799                 var d = (this.doc.body || this.doc.documentElement);
44800                 d.innerHTML = v;
44801                 this.cleanUpPaste();
44802                 this.el.dom.value = d.innerHTML;
44803                 this.owner.fireEvent('push', this, v);
44804             }
44805         }
44806     },
44807
44808     // private
44809     deferFocus : function(){
44810         this.focus.defer(10, this);
44811     },
44812
44813     // doc'ed in Field
44814     focus : function(){
44815         if(this.win && !this.sourceEditMode){
44816             this.win.focus();
44817         }else{
44818             this.el.focus();
44819         }
44820     },
44821     
44822     assignDocWin: function()
44823     {
44824         var iframe = this.iframe;
44825         
44826          if(Roo.isIE){
44827             this.doc = iframe.contentWindow.document;
44828             this.win = iframe.contentWindow;
44829         } else {
44830 //            if (!Roo.get(this.frameId)) {
44831 //                return;
44832 //            }
44833 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44834 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44835             
44836             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44837                 return;
44838             }
44839             
44840             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44841             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44842         }
44843     },
44844     
44845     // private
44846     initEditor : function(){
44847         //console.log("INIT EDITOR");
44848         this.assignDocWin();
44849         
44850         
44851         
44852         this.doc.designMode="on";
44853         this.doc.open();
44854         this.doc.write(this.getDocMarkup());
44855         this.doc.close();
44856         
44857         var dbody = (this.doc.body || this.doc.documentElement);
44858         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44859         // this copies styles from the containing element into thsi one..
44860         // not sure why we need all of this..
44861         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44862         
44863         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44864         //ss['background-attachment'] = 'fixed'; // w3c
44865         dbody.bgProperties = 'fixed'; // ie
44866         //Roo.DomHelper.applyStyles(dbody, ss);
44867         Roo.EventManager.on(this.doc, {
44868             //'mousedown': this.onEditorEvent,
44869             'mouseup': this.onEditorEvent,
44870             'dblclick': this.onEditorEvent,
44871             'click': this.onEditorEvent,
44872             'keyup': this.onEditorEvent,
44873             buffer:100,
44874             scope: this
44875         });
44876         if(Roo.isGecko){
44877             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44878         }
44879         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44880             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44881         }
44882         this.initialized = true;
44883
44884         this.owner.fireEvent('initialize', this);
44885         this.pushValue();
44886     },
44887
44888     // private
44889     onDestroy : function(){
44890         
44891         
44892         
44893         if(this.rendered){
44894             
44895             //for (var i =0; i < this.toolbars.length;i++) {
44896             //    // fixme - ask toolbars for heights?
44897             //    this.toolbars[i].onDestroy();
44898            // }
44899             
44900             //this.wrap.dom.innerHTML = '';
44901             //this.wrap.remove();
44902         }
44903     },
44904
44905     // private
44906     onFirstFocus : function(){
44907         
44908         this.assignDocWin();
44909         
44910         
44911         this.activated = true;
44912          
44913     
44914         if(Roo.isGecko){ // prevent silly gecko errors
44915             this.win.focus();
44916             var s = this.win.getSelection();
44917             if(!s.focusNode || s.focusNode.nodeType != 3){
44918                 var r = s.getRangeAt(0);
44919                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44920                 r.collapse(true);
44921                 this.deferFocus();
44922             }
44923             try{
44924                 this.execCmd('useCSS', true);
44925                 this.execCmd('styleWithCSS', false);
44926             }catch(e){}
44927         }
44928         this.owner.fireEvent('activate', this);
44929     },
44930
44931     // private
44932     adjustFont: function(btn){
44933         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44934         //if(Roo.isSafari){ // safari
44935         //    adjust *= 2;
44936        // }
44937         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44938         if(Roo.isSafari){ // safari
44939             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44940             v =  (v < 10) ? 10 : v;
44941             v =  (v > 48) ? 48 : v;
44942             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44943             
44944         }
44945         
44946         
44947         v = Math.max(1, v+adjust);
44948         
44949         this.execCmd('FontSize', v  );
44950     },
44951
44952     onEditorEvent : function(e)
44953     {
44954         this.owner.fireEvent('editorevent', this, e);
44955       //  this.updateToolbar();
44956         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44957     },
44958
44959     insertTag : function(tg)
44960     {
44961         // could be a bit smarter... -> wrap the current selected tRoo..
44962         if (tg.toLowerCase() == 'span' ||
44963             tg.toLowerCase() == 'code' ||
44964             tg.toLowerCase() == 'sup' ||
44965             tg.toLowerCase() == 'sub' 
44966             ) {
44967             
44968             range = this.createRange(this.getSelection());
44969             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44970             wrappingNode.appendChild(range.extractContents());
44971             range.insertNode(wrappingNode);
44972
44973             return;
44974             
44975             
44976             
44977         }
44978         this.execCmd("formatblock",   tg);
44979         
44980     },
44981     
44982     insertText : function(txt)
44983     {
44984         
44985         
44986         var range = this.createRange();
44987         range.deleteContents();
44988                //alert(Sender.getAttribute('label'));
44989                
44990         range.insertNode(this.doc.createTextNode(txt));
44991     } ,
44992     
44993      
44994
44995     /**
44996      * Executes a Midas editor command on the editor document and performs necessary focus and
44997      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44998      * @param {String} cmd The Midas command
44999      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45000      */
45001     relayCmd : function(cmd, value){
45002         this.win.focus();
45003         this.execCmd(cmd, value);
45004         this.owner.fireEvent('editorevent', this);
45005         //this.updateToolbar();
45006         this.owner.deferFocus();
45007     },
45008
45009     /**
45010      * Executes a Midas editor command directly on the editor document.
45011      * For visual commands, you should use {@link #relayCmd} instead.
45012      * <b>This should only be called after the editor is initialized.</b>
45013      * @param {String} cmd The Midas command
45014      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45015      */
45016     execCmd : function(cmd, value){
45017         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45018         this.syncValue();
45019     },
45020  
45021  
45022    
45023     /**
45024      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45025      * to insert tRoo.
45026      * @param {String} text | dom node.. 
45027      */
45028     insertAtCursor : function(text)
45029     {
45030         
45031         if(!this.activated){
45032             return;
45033         }
45034         /*
45035         if(Roo.isIE){
45036             this.win.focus();
45037             var r = this.doc.selection.createRange();
45038             if(r){
45039                 r.collapse(true);
45040                 r.pasteHTML(text);
45041                 this.syncValue();
45042                 this.deferFocus();
45043             
45044             }
45045             return;
45046         }
45047         */
45048         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45049             this.win.focus();
45050             
45051             
45052             // from jquery ui (MIT licenced)
45053             var range, node;
45054             var win = this.win;
45055             
45056             if (win.getSelection && win.getSelection().getRangeAt) {
45057                 range = win.getSelection().getRangeAt(0);
45058                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45059                 range.insertNode(node);
45060             } else if (win.document.selection && win.document.selection.createRange) {
45061                 // no firefox support
45062                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45063                 win.document.selection.createRange().pasteHTML(txt);
45064             } else {
45065                 // no firefox support
45066                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45067                 this.execCmd('InsertHTML', txt);
45068             } 
45069             
45070             this.syncValue();
45071             
45072             this.deferFocus();
45073         }
45074     },
45075  // private
45076     mozKeyPress : function(e){
45077         if(e.ctrlKey){
45078             var c = e.getCharCode(), cmd;
45079           
45080             if(c > 0){
45081                 c = String.fromCharCode(c).toLowerCase();
45082                 switch(c){
45083                     case 'b':
45084                         cmd = 'bold';
45085                         break;
45086                     case 'i':
45087                         cmd = 'italic';
45088                         break;
45089                     
45090                     case 'u':
45091                         cmd = 'underline';
45092                         break;
45093                     
45094                     case 'v':
45095                         this.cleanUpPaste.defer(100, this);
45096                         return;
45097                         
45098                 }
45099                 if(cmd){
45100                     this.win.focus();
45101                     this.execCmd(cmd);
45102                     this.deferFocus();
45103                     e.preventDefault();
45104                 }
45105                 
45106             }
45107         }
45108     },
45109
45110     // private
45111     fixKeys : function(){ // load time branching for fastest keydown performance
45112         if(Roo.isIE){
45113             return function(e){
45114                 var k = e.getKey(), r;
45115                 if(k == e.TAB){
45116                     e.stopEvent();
45117                     r = this.doc.selection.createRange();
45118                     if(r){
45119                         r.collapse(true);
45120                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45121                         this.deferFocus();
45122                     }
45123                     return;
45124                 }
45125                 
45126                 if(k == e.ENTER){
45127                     r = this.doc.selection.createRange();
45128                     if(r){
45129                         var target = r.parentElement();
45130                         if(!target || target.tagName.toLowerCase() != 'li'){
45131                             e.stopEvent();
45132                             r.pasteHTML('<br />');
45133                             r.collapse(false);
45134                             r.select();
45135                         }
45136                     }
45137                 }
45138                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45139                     this.cleanUpPaste.defer(100, this);
45140                     return;
45141                 }
45142                 
45143                 
45144             };
45145         }else if(Roo.isOpera){
45146             return function(e){
45147                 var k = e.getKey();
45148                 if(k == e.TAB){
45149                     e.stopEvent();
45150                     this.win.focus();
45151                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45152                     this.deferFocus();
45153                 }
45154                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45155                     this.cleanUpPaste.defer(100, this);
45156                     return;
45157                 }
45158                 
45159             };
45160         }else if(Roo.isSafari){
45161             return function(e){
45162                 var k = e.getKey();
45163                 
45164                 if(k == e.TAB){
45165                     e.stopEvent();
45166                     this.execCmd('InsertText','\t');
45167                     this.deferFocus();
45168                     return;
45169                 }
45170                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45171                     this.cleanUpPaste.defer(100, this);
45172                     return;
45173                 }
45174                 
45175              };
45176         }
45177     }(),
45178     
45179     getAllAncestors: function()
45180     {
45181         var p = this.getSelectedNode();
45182         var a = [];
45183         if (!p) {
45184             a.push(p); // push blank onto stack..
45185             p = this.getParentElement();
45186         }
45187         
45188         
45189         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45190             a.push(p);
45191             p = p.parentNode;
45192         }
45193         a.push(this.doc.body);
45194         return a;
45195     },
45196     lastSel : false,
45197     lastSelNode : false,
45198     
45199     
45200     getSelection : function() 
45201     {
45202         this.assignDocWin();
45203         return Roo.isIE ? this.doc.selection : this.win.getSelection();
45204     },
45205     
45206     getSelectedNode: function() 
45207     {
45208         // this may only work on Gecko!!!
45209         
45210         // should we cache this!!!!
45211         
45212         
45213         
45214          
45215         var range = this.createRange(this.getSelection()).cloneRange();
45216         
45217         if (Roo.isIE) {
45218             var parent = range.parentElement();
45219             while (true) {
45220                 var testRange = range.duplicate();
45221                 testRange.moveToElementText(parent);
45222                 if (testRange.inRange(range)) {
45223                     break;
45224                 }
45225                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45226                     break;
45227                 }
45228                 parent = parent.parentElement;
45229             }
45230             return parent;
45231         }
45232         
45233         // is ancestor a text element.
45234         var ac =  range.commonAncestorContainer;
45235         if (ac.nodeType == 3) {
45236             ac = ac.parentNode;
45237         }
45238         
45239         var ar = ac.childNodes;
45240          
45241         var nodes = [];
45242         var other_nodes = [];
45243         var has_other_nodes = false;
45244         for (var i=0;i<ar.length;i++) {
45245             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
45246                 continue;
45247             }
45248             // fullly contained node.
45249             
45250             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
45251                 nodes.push(ar[i]);
45252                 continue;
45253             }
45254             
45255             // probably selected..
45256             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
45257                 other_nodes.push(ar[i]);
45258                 continue;
45259             }
45260             // outer..
45261             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
45262                 continue;
45263             }
45264             
45265             
45266             has_other_nodes = true;
45267         }
45268         if (!nodes.length && other_nodes.length) {
45269             nodes= other_nodes;
45270         }
45271         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
45272             return false;
45273         }
45274         
45275         return nodes[0];
45276     },
45277     createRange: function(sel)
45278     {
45279         // this has strange effects when using with 
45280         // top toolbar - not sure if it's a great idea.
45281         //this.editor.contentWindow.focus();
45282         if (typeof sel != "undefined") {
45283             try {
45284                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
45285             } catch(e) {
45286                 return this.doc.createRange();
45287             }
45288         } else {
45289             return this.doc.createRange();
45290         }
45291     },
45292     getParentElement: function()
45293     {
45294         
45295         this.assignDocWin();
45296         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
45297         
45298         var range = this.createRange(sel);
45299          
45300         try {
45301             var p = range.commonAncestorContainer;
45302             while (p.nodeType == 3) { // text node
45303                 p = p.parentNode;
45304             }
45305             return p;
45306         } catch (e) {
45307             return null;
45308         }
45309     
45310     },
45311     /***
45312      *
45313      * Range intersection.. the hard stuff...
45314      *  '-1' = before
45315      *  '0' = hits..
45316      *  '1' = after.
45317      *         [ -- selected range --- ]
45318      *   [fail]                        [fail]
45319      *
45320      *    basically..
45321      *      if end is before start or  hits it. fail.
45322      *      if start is after end or hits it fail.
45323      *
45324      *   if either hits (but other is outside. - then it's not 
45325      *   
45326      *    
45327      **/
45328     
45329     
45330     // @see http://www.thismuchiknow.co.uk/?p=64.
45331     rangeIntersectsNode : function(range, node)
45332     {
45333         var nodeRange = node.ownerDocument.createRange();
45334         try {
45335             nodeRange.selectNode(node);
45336         } catch (e) {
45337             nodeRange.selectNodeContents(node);
45338         }
45339     
45340         var rangeStartRange = range.cloneRange();
45341         rangeStartRange.collapse(true);
45342     
45343         var rangeEndRange = range.cloneRange();
45344         rangeEndRange.collapse(false);
45345     
45346         var nodeStartRange = nodeRange.cloneRange();
45347         nodeStartRange.collapse(true);
45348     
45349         var nodeEndRange = nodeRange.cloneRange();
45350         nodeEndRange.collapse(false);
45351     
45352         return rangeStartRange.compareBoundaryPoints(
45353                  Range.START_TO_START, nodeEndRange) == -1 &&
45354                rangeEndRange.compareBoundaryPoints(
45355                  Range.START_TO_START, nodeStartRange) == 1;
45356         
45357          
45358     },
45359     rangeCompareNode : function(range, node)
45360     {
45361         var nodeRange = node.ownerDocument.createRange();
45362         try {
45363             nodeRange.selectNode(node);
45364         } catch (e) {
45365             nodeRange.selectNodeContents(node);
45366         }
45367         
45368         
45369         range.collapse(true);
45370     
45371         nodeRange.collapse(true);
45372      
45373         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
45374         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
45375          
45376         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
45377         
45378         var nodeIsBefore   =  ss == 1;
45379         var nodeIsAfter    = ee == -1;
45380         
45381         if (nodeIsBefore && nodeIsAfter) {
45382             return 0; // outer
45383         }
45384         if (!nodeIsBefore && nodeIsAfter) {
45385             return 1; //right trailed.
45386         }
45387         
45388         if (nodeIsBefore && !nodeIsAfter) {
45389             return 2;  // left trailed.
45390         }
45391         // fully contined.
45392         return 3;
45393     },
45394
45395     // private? - in a new class?
45396     cleanUpPaste :  function()
45397     {
45398         // cleans up the whole document..
45399         Roo.log('cleanuppaste');
45400         
45401         this.cleanUpChildren(this.doc.body);
45402         var clean = this.cleanWordChars(this.doc.body.innerHTML);
45403         if (clean != this.doc.body.innerHTML) {
45404             this.doc.body.innerHTML = clean;
45405         }
45406         
45407     },
45408     
45409     cleanWordChars : function(input) {// change the chars to hex code
45410         var he = Roo.HtmlEditorCore;
45411         
45412         var output = input;
45413         Roo.each(he.swapCodes, function(sw) { 
45414             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
45415             
45416             output = output.replace(swapper, sw[1]);
45417         });
45418         
45419         return output;
45420     },
45421     
45422     
45423     cleanUpChildren : function (n)
45424     {
45425         if (!n.childNodes.length) {
45426             return;
45427         }
45428         for (var i = n.childNodes.length-1; i > -1 ; i--) {
45429            this.cleanUpChild(n.childNodes[i]);
45430         }
45431     },
45432     
45433     
45434         
45435     
45436     cleanUpChild : function (node)
45437     {
45438         var ed = this;
45439         //console.log(node);
45440         if (node.nodeName == "#text") {
45441             // clean up silly Windows -- stuff?
45442             return; 
45443         }
45444         if (node.nodeName == "#comment") {
45445             if (!this.allowComments) {
45446                 node.parentNode.removeChild(node);
45447             }
45448             // clean up silly Windows -- stuff?
45449             return; 
45450         }
45451         var lcname = node.tagName.toLowerCase();
45452         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
45453         // whitelist of tags..
45454         
45455         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
45456             // remove node.
45457             node.parentNode.removeChild(node);
45458             return;
45459             
45460         }
45461         
45462         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
45463         
45464         // spans with no attributes - just remove them..
45465         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
45466             remove_keep_children = true;
45467         }
45468         
45469         // remove <a name=....> as rendering on yahoo mailer is borked with this.
45470         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
45471         
45472         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
45473         //    remove_keep_children = true;
45474         //}
45475         
45476         if (remove_keep_children) {
45477             this.cleanUpChildren(node);
45478             // inserts everything just before this node...
45479             while (node.childNodes.length) {
45480                 var cn = node.childNodes[0];
45481                 node.removeChild(cn);
45482                 node.parentNode.insertBefore(cn, node);
45483             }
45484             node.parentNode.removeChild(node);
45485             return;
45486         }
45487         
45488         if (!node.attributes || !node.attributes.length) {
45489             
45490           
45491             
45492             
45493             this.cleanUpChildren(node);
45494             return;
45495         }
45496         
45497         function cleanAttr(n,v)
45498         {
45499             
45500             if (v.match(/^\./) || v.match(/^\//)) {
45501                 return;
45502             }
45503             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
45504                 return;
45505             }
45506             if (v.match(/^#/)) {
45507                 return;
45508             }
45509             if (v.match(/^\{/)) { // allow template editing.
45510                 return;
45511             }
45512 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45513             node.removeAttribute(n);
45514             
45515         }
45516         
45517         var cwhite = this.cwhite;
45518         var cblack = this.cblack;
45519             
45520         function cleanStyle(n,v)
45521         {
45522             if (v.match(/expression/)) { //XSS?? should we even bother..
45523                 node.removeAttribute(n);
45524                 return;
45525             }
45526             
45527             var parts = v.split(/;/);
45528             var clean = [];
45529             
45530             Roo.each(parts, function(p) {
45531                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45532                 if (!p.length) {
45533                     return true;
45534                 }
45535                 var l = p.split(':').shift().replace(/\s+/g,'');
45536                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45537                 
45538                 if ( cwhite.length && cblack.indexOf(l) > -1) {
45539 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
45540                     //node.removeAttribute(n);
45541                     return true;
45542                 }
45543                 //Roo.log()
45544                 // only allow 'c whitelisted system attributes'
45545                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
45546 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
45547                     //node.removeAttribute(n);
45548                     return true;
45549                 }
45550                 
45551                 
45552                  
45553                 
45554                 clean.push(p);
45555                 return true;
45556             });
45557             if (clean.length) { 
45558                 node.setAttribute(n, clean.join(';'));
45559             } else {
45560                 node.removeAttribute(n);
45561             }
45562             
45563         }
45564         
45565         
45566         for (var i = node.attributes.length-1; i > -1 ; i--) {
45567             var a = node.attributes[i];
45568             //console.log(a);
45569             
45570             if (a.name.toLowerCase().substr(0,2)=='on')  {
45571                 node.removeAttribute(a.name);
45572                 continue;
45573             }
45574             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
45575                 node.removeAttribute(a.name);
45576                 continue;
45577             }
45578             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
45579                 cleanAttr(a.name,a.value); // fixme..
45580                 continue;
45581             }
45582             if (a.name == 'style') {
45583                 cleanStyle(a.name,a.value);
45584                 continue;
45585             }
45586             /// clean up MS crap..
45587             // tecnically this should be a list of valid class'es..
45588             
45589             
45590             if (a.name == 'class') {
45591                 if (a.value.match(/^Mso/)) {
45592                     node.removeAttribute('class');
45593                 }
45594                 
45595                 if (a.value.match(/^body$/)) {
45596                     node.removeAttribute('class');
45597                 }
45598                 continue;
45599             }
45600             
45601             // style cleanup!?
45602             // class cleanup?
45603             
45604         }
45605         
45606         
45607         this.cleanUpChildren(node);
45608         
45609         
45610     },
45611     
45612     /**
45613      * Clean up MS wordisms...
45614      */
45615     cleanWord : function(node)
45616     {
45617         if (!node) {
45618             this.cleanWord(this.doc.body);
45619             return;
45620         }
45621         
45622         if(
45623                 node.nodeName == 'SPAN' &&
45624                 !node.hasAttributes() &&
45625                 node.childNodes.length == 1 &&
45626                 node.firstChild.nodeName == "#text"  
45627         ) {
45628             var textNode = node.firstChild;
45629             node.removeChild(textNode);
45630             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45631                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45632             }
45633             node.parentNode.insertBefore(textNode, node);
45634             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45635                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45636             }
45637             node.parentNode.removeChild(node);
45638         }
45639         
45640         if (node.nodeName == "#text") {
45641             // clean up silly Windows -- stuff?
45642             return; 
45643         }
45644         if (node.nodeName == "#comment") {
45645             node.parentNode.removeChild(node);
45646             // clean up silly Windows -- stuff?
45647             return; 
45648         }
45649         
45650         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45651             node.parentNode.removeChild(node);
45652             return;
45653         }
45654         //Roo.log(node.tagName);
45655         // remove - but keep children..
45656         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45657             //Roo.log('-- removed');
45658             while (node.childNodes.length) {
45659                 var cn = node.childNodes[0];
45660                 node.removeChild(cn);
45661                 node.parentNode.insertBefore(cn, node);
45662                 // move node to parent - and clean it..
45663                 this.cleanWord(cn);
45664             }
45665             node.parentNode.removeChild(node);
45666             /// no need to iterate chidlren = it's got none..
45667             //this.iterateChildren(node, this.cleanWord);
45668             return;
45669         }
45670         // clean styles
45671         if (node.className.length) {
45672             
45673             var cn = node.className.split(/\W+/);
45674             var cna = [];
45675             Roo.each(cn, function(cls) {
45676                 if (cls.match(/Mso[a-zA-Z]+/)) {
45677                     return;
45678                 }
45679                 cna.push(cls);
45680             });
45681             node.className = cna.length ? cna.join(' ') : '';
45682             if (!cna.length) {
45683                 node.removeAttribute("class");
45684             }
45685         }
45686         
45687         if (node.hasAttribute("lang")) {
45688             node.removeAttribute("lang");
45689         }
45690         
45691         if (node.hasAttribute("style")) {
45692             
45693             var styles = node.getAttribute("style").split(";");
45694             var nstyle = [];
45695             Roo.each(styles, function(s) {
45696                 if (!s.match(/:/)) {
45697                     return;
45698                 }
45699                 var kv = s.split(":");
45700                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45701                     return;
45702                 }
45703                 // what ever is left... we allow.
45704                 nstyle.push(s);
45705             });
45706             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45707             if (!nstyle.length) {
45708                 node.removeAttribute('style');
45709             }
45710         }
45711         this.iterateChildren(node, this.cleanWord);
45712         
45713         
45714         
45715     },
45716     /**
45717      * iterateChildren of a Node, calling fn each time, using this as the scole..
45718      * @param {DomNode} node node to iterate children of.
45719      * @param {Function} fn method of this class to call on each item.
45720      */
45721     iterateChildren : function(node, fn)
45722     {
45723         if (!node.childNodes.length) {
45724                 return;
45725         }
45726         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45727            fn.call(this, node.childNodes[i])
45728         }
45729     },
45730     
45731     
45732     /**
45733      * cleanTableWidths.
45734      *
45735      * Quite often pasting from word etc.. results in tables with column and widths.
45736      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45737      *
45738      */
45739     cleanTableWidths : function(node)
45740     {
45741          
45742          
45743         if (!node) {
45744             this.cleanTableWidths(this.doc.body);
45745             return;
45746         }
45747         
45748         // ignore list...
45749         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45750             return; 
45751         }
45752         Roo.log(node.tagName);
45753         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45754             this.iterateChildren(node, this.cleanTableWidths);
45755             return;
45756         }
45757         if (node.hasAttribute('width')) {
45758             node.removeAttribute('width');
45759         }
45760         
45761          
45762         if (node.hasAttribute("style")) {
45763             // pretty basic...
45764             
45765             var styles = node.getAttribute("style").split(";");
45766             var nstyle = [];
45767             Roo.each(styles, function(s) {
45768                 if (!s.match(/:/)) {
45769                     return;
45770                 }
45771                 var kv = s.split(":");
45772                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45773                     return;
45774                 }
45775                 // what ever is left... we allow.
45776                 nstyle.push(s);
45777             });
45778             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45779             if (!nstyle.length) {
45780                 node.removeAttribute('style');
45781             }
45782         }
45783         
45784         this.iterateChildren(node, this.cleanTableWidths);
45785         
45786         
45787     },
45788     
45789     
45790     
45791     
45792     domToHTML : function(currentElement, depth, nopadtext) {
45793         
45794         depth = depth || 0;
45795         nopadtext = nopadtext || false;
45796     
45797         if (!currentElement) {
45798             return this.domToHTML(this.doc.body);
45799         }
45800         
45801         //Roo.log(currentElement);
45802         var j;
45803         var allText = false;
45804         var nodeName = currentElement.nodeName;
45805         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45806         
45807         if  (nodeName == '#text') {
45808             
45809             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45810         }
45811         
45812         
45813         var ret = '';
45814         if (nodeName != 'BODY') {
45815              
45816             var i = 0;
45817             // Prints the node tagName, such as <A>, <IMG>, etc
45818             if (tagName) {
45819                 var attr = [];
45820                 for(i = 0; i < currentElement.attributes.length;i++) {
45821                     // quoting?
45822                     var aname = currentElement.attributes.item(i).name;
45823                     if (!currentElement.attributes.item(i).value.length) {
45824                         continue;
45825                     }
45826                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45827                 }
45828                 
45829                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45830             } 
45831             else {
45832                 
45833                 // eack
45834             }
45835         } else {
45836             tagName = false;
45837         }
45838         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45839             return ret;
45840         }
45841         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45842             nopadtext = true;
45843         }
45844         
45845         
45846         // Traverse the tree
45847         i = 0;
45848         var currentElementChild = currentElement.childNodes.item(i);
45849         var allText = true;
45850         var innerHTML  = '';
45851         lastnode = '';
45852         while (currentElementChild) {
45853             // Formatting code (indent the tree so it looks nice on the screen)
45854             var nopad = nopadtext;
45855             if (lastnode == 'SPAN') {
45856                 nopad  = true;
45857             }
45858             // text
45859             if  (currentElementChild.nodeName == '#text') {
45860                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45861                 toadd = nopadtext ? toadd : toadd.trim();
45862                 if (!nopad && toadd.length > 80) {
45863                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45864                 }
45865                 innerHTML  += toadd;
45866                 
45867                 i++;
45868                 currentElementChild = currentElement.childNodes.item(i);
45869                 lastNode = '';
45870                 continue;
45871             }
45872             allText = false;
45873             
45874             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45875                 
45876             // Recursively traverse the tree structure of the child node
45877             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45878             lastnode = currentElementChild.nodeName;
45879             i++;
45880             currentElementChild=currentElement.childNodes.item(i);
45881         }
45882         
45883         ret += innerHTML;
45884         
45885         if (!allText) {
45886                 // The remaining code is mostly for formatting the tree
45887             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45888         }
45889         
45890         
45891         if (tagName) {
45892             ret+= "</"+tagName+">";
45893         }
45894         return ret;
45895         
45896     },
45897         
45898     applyBlacklists : function()
45899     {
45900         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45901         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45902         
45903         this.white = [];
45904         this.black = [];
45905         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45906             if (b.indexOf(tag) > -1) {
45907                 return;
45908             }
45909             this.white.push(tag);
45910             
45911         }, this);
45912         
45913         Roo.each(w, function(tag) {
45914             if (b.indexOf(tag) > -1) {
45915                 return;
45916             }
45917             if (this.white.indexOf(tag) > -1) {
45918                 return;
45919             }
45920             this.white.push(tag);
45921             
45922         }, this);
45923         
45924         
45925         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45926             if (w.indexOf(tag) > -1) {
45927                 return;
45928             }
45929             this.black.push(tag);
45930             
45931         }, this);
45932         
45933         Roo.each(b, function(tag) {
45934             if (w.indexOf(tag) > -1) {
45935                 return;
45936             }
45937             if (this.black.indexOf(tag) > -1) {
45938                 return;
45939             }
45940             this.black.push(tag);
45941             
45942         }, this);
45943         
45944         
45945         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45946         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45947         
45948         this.cwhite = [];
45949         this.cblack = [];
45950         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45951             if (b.indexOf(tag) > -1) {
45952                 return;
45953             }
45954             this.cwhite.push(tag);
45955             
45956         }, this);
45957         
45958         Roo.each(w, function(tag) {
45959             if (b.indexOf(tag) > -1) {
45960                 return;
45961             }
45962             if (this.cwhite.indexOf(tag) > -1) {
45963                 return;
45964             }
45965             this.cwhite.push(tag);
45966             
45967         }, this);
45968         
45969         
45970         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45971             if (w.indexOf(tag) > -1) {
45972                 return;
45973             }
45974             this.cblack.push(tag);
45975             
45976         }, this);
45977         
45978         Roo.each(b, function(tag) {
45979             if (w.indexOf(tag) > -1) {
45980                 return;
45981             }
45982             if (this.cblack.indexOf(tag) > -1) {
45983                 return;
45984             }
45985             this.cblack.push(tag);
45986             
45987         }, this);
45988     },
45989     
45990     setStylesheets : function(stylesheets)
45991     {
45992         if(typeof(stylesheets) == 'string'){
45993             Roo.get(this.iframe.contentDocument.head).createChild({
45994                 tag : 'link',
45995                 rel : 'stylesheet',
45996                 type : 'text/css',
45997                 href : stylesheets
45998             });
45999             
46000             return;
46001         }
46002         var _this = this;
46003      
46004         Roo.each(stylesheets, function(s) {
46005             if(!s.length){
46006                 return;
46007             }
46008             
46009             Roo.get(_this.iframe.contentDocument.head).createChild({
46010                 tag : 'link',
46011                 rel : 'stylesheet',
46012                 type : 'text/css',
46013                 href : s
46014             });
46015         });
46016
46017         
46018     },
46019     
46020     removeStylesheets : function()
46021     {
46022         var _this = this;
46023         
46024         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46025             s.remove();
46026         });
46027     },
46028     
46029     setStyle : function(style)
46030     {
46031         Roo.get(this.iframe.contentDocument.head).createChild({
46032             tag : 'style',
46033             type : 'text/css',
46034             html : style
46035         });
46036
46037         return;
46038     }
46039     
46040     // hide stuff that is not compatible
46041     /**
46042      * @event blur
46043      * @hide
46044      */
46045     /**
46046      * @event change
46047      * @hide
46048      */
46049     /**
46050      * @event focus
46051      * @hide
46052      */
46053     /**
46054      * @event specialkey
46055      * @hide
46056      */
46057     /**
46058      * @cfg {String} fieldClass @hide
46059      */
46060     /**
46061      * @cfg {String} focusClass @hide
46062      */
46063     /**
46064      * @cfg {String} autoCreate @hide
46065      */
46066     /**
46067      * @cfg {String} inputType @hide
46068      */
46069     /**
46070      * @cfg {String} invalidClass @hide
46071      */
46072     /**
46073      * @cfg {String} invalidText @hide
46074      */
46075     /**
46076      * @cfg {String} msgFx @hide
46077      */
46078     /**
46079      * @cfg {String} validateOnBlur @hide
46080      */
46081 });
46082
46083 Roo.HtmlEditorCore.white = [
46084         'area', 'br', 'img', 'input', 'hr', 'wbr',
46085         
46086        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46087        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46088        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46089        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46090        'table',   'ul',         'xmp', 
46091        
46092        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46093       'thead',   'tr', 
46094      
46095       'dir', 'menu', 'ol', 'ul', 'dl',
46096        
46097       'embed',  'object'
46098 ];
46099
46100
46101 Roo.HtmlEditorCore.black = [
46102     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46103         'applet', // 
46104         'base',   'basefont', 'bgsound', 'blink',  'body', 
46105         'frame',  'frameset', 'head',    'html',   'ilayer', 
46106         'iframe', 'layer',  'link',     'meta',    'object',   
46107         'script', 'style' ,'title',  'xml' // clean later..
46108 ];
46109 Roo.HtmlEditorCore.clean = [
46110     'script', 'style', 'title', 'xml'
46111 ];
46112 Roo.HtmlEditorCore.remove = [
46113     'font'
46114 ];
46115 // attributes..
46116
46117 Roo.HtmlEditorCore.ablack = [
46118     'on'
46119 ];
46120     
46121 Roo.HtmlEditorCore.aclean = [ 
46122     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46123 ];
46124
46125 // protocols..
46126 Roo.HtmlEditorCore.pwhite= [
46127         'http',  'https',  'mailto'
46128 ];
46129
46130 // white listed style attributes.
46131 Roo.HtmlEditorCore.cwhite= [
46132       //  'text-align', /// default is to allow most things..
46133       
46134          
46135 //        'font-size'//??
46136 ];
46137
46138 // black listed style attributes.
46139 Roo.HtmlEditorCore.cblack= [
46140       //  'font-size' -- this can be set by the project 
46141 ];
46142
46143
46144 Roo.HtmlEditorCore.swapCodes   =[ 
46145     [    8211, "&#8211;" ], 
46146     [    8212, "&#8212;" ], 
46147     [    8216,  "'" ],  
46148     [    8217, "'" ],  
46149     [    8220, '"' ],  
46150     [    8221, '"' ],  
46151     [    8226, "*" ],  
46152     [    8230, "..." ]
46153 ]; 
46154
46155     //<script type="text/javascript">
46156
46157 /*
46158  * Ext JS Library 1.1.1
46159  * Copyright(c) 2006-2007, Ext JS, LLC.
46160  * Licence LGPL
46161  * 
46162  */
46163  
46164  
46165 Roo.form.HtmlEditor = function(config){
46166     
46167     
46168     
46169     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46170     
46171     if (!this.toolbars) {
46172         this.toolbars = [];
46173     }
46174     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46175     
46176     
46177 };
46178
46179 /**
46180  * @class Roo.form.HtmlEditor
46181  * @extends Roo.form.Field
46182  * Provides a lightweight HTML Editor component.
46183  *
46184  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46185  * 
46186  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46187  * supported by this editor.</b><br/><br/>
46188  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46189  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46190  */
46191 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46192     /**
46193      * @cfg {Boolean} clearUp
46194      */
46195     clearUp : true,
46196       /**
46197      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46198      */
46199     toolbars : false,
46200    
46201      /**
46202      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46203      *                        Roo.resizable.
46204      */
46205     resizable : false,
46206      /**
46207      * @cfg {Number} height (in pixels)
46208      */   
46209     height: 300,
46210    /**
46211      * @cfg {Number} width (in pixels)
46212      */   
46213     width: 500,
46214     
46215     /**
46216      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46217      * 
46218      */
46219     stylesheets: false,
46220     
46221     
46222      /**
46223      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46224      * 
46225      */
46226     cblack: false,
46227     /**
46228      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46229      * 
46230      */
46231     cwhite: false,
46232     
46233      /**
46234      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46235      * 
46236      */
46237     black: false,
46238     /**
46239      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46240      * 
46241      */
46242     white: false,
46243     /**
46244      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46245      */
46246     allowComments: false,
46247     
46248     // id of frame..
46249     frameId: false,
46250     
46251     // private properties
46252     validationEvent : false,
46253     deferHeight: true,
46254     initialized : false,
46255     activated : false,
46256     
46257     onFocus : Roo.emptyFn,
46258     iframePad:3,
46259     hideMode:'offsets',
46260     
46261     actionMode : 'container', // defaults to hiding it...
46262     
46263     defaultAutoCreate : { // modified by initCompnoent..
46264         tag: "textarea",
46265         style:"width:500px;height:300px;",
46266         autocomplete: "new-password"
46267     },
46268
46269     // private
46270     initComponent : function(){
46271         this.addEvents({
46272             /**
46273              * @event initialize
46274              * Fires when the editor is fully initialized (including the iframe)
46275              * @param {HtmlEditor} this
46276              */
46277             initialize: true,
46278             /**
46279              * @event activate
46280              * Fires when the editor is first receives the focus. Any insertion must wait
46281              * until after this event.
46282              * @param {HtmlEditor} this
46283              */
46284             activate: true,
46285              /**
46286              * @event beforesync
46287              * Fires before the textarea is updated with content from the editor iframe. Return false
46288              * to cancel the sync.
46289              * @param {HtmlEditor} this
46290              * @param {String} html
46291              */
46292             beforesync: true,
46293              /**
46294              * @event beforepush
46295              * Fires before the iframe editor is updated with content from the textarea. Return false
46296              * to cancel the push.
46297              * @param {HtmlEditor} this
46298              * @param {String} html
46299              */
46300             beforepush: true,
46301              /**
46302              * @event sync
46303              * Fires when the textarea is updated with content from the editor iframe.
46304              * @param {HtmlEditor} this
46305              * @param {String} html
46306              */
46307             sync: true,
46308              /**
46309              * @event push
46310              * Fires when the iframe editor is updated with content from the textarea.
46311              * @param {HtmlEditor} this
46312              * @param {String} html
46313              */
46314             push: true,
46315              /**
46316              * @event editmodechange
46317              * Fires when the editor switches edit modes
46318              * @param {HtmlEditor} this
46319              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46320              */
46321             editmodechange: true,
46322             /**
46323              * @event editorevent
46324              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46325              * @param {HtmlEditor} this
46326              */
46327             editorevent: true,
46328             /**
46329              * @event firstfocus
46330              * Fires when on first focus - needed by toolbars..
46331              * @param {HtmlEditor} this
46332              */
46333             firstfocus: true,
46334             /**
46335              * @event autosave
46336              * Auto save the htmlEditor value as a file into Events
46337              * @param {HtmlEditor} this
46338              */
46339             autosave: true,
46340             /**
46341              * @event savedpreview
46342              * preview the saved version of htmlEditor
46343              * @param {HtmlEditor} this
46344              */
46345             savedpreview: true,
46346             
46347             /**
46348             * @event stylesheetsclick
46349             * Fires when press the Sytlesheets button
46350             * @param {Roo.HtmlEditorCore} this
46351             */
46352             stylesheetsclick: true
46353         });
46354         this.defaultAutoCreate =  {
46355             tag: "textarea",
46356             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46357             autocomplete: "new-password"
46358         };
46359     },
46360
46361     /**
46362      * Protected method that will not generally be called directly. It
46363      * is called when the editor creates its toolbar. Override this method if you need to
46364      * add custom toolbar buttons.
46365      * @param {HtmlEditor} editor
46366      */
46367     createToolbar : function(editor){
46368         Roo.log("create toolbars");
46369         if (!editor.toolbars || !editor.toolbars.length) {
46370             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46371         }
46372         
46373         for (var i =0 ; i < editor.toolbars.length;i++) {
46374             editor.toolbars[i] = Roo.factory(
46375                     typeof(editor.toolbars[i]) == 'string' ?
46376                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46377                 Roo.form.HtmlEditor);
46378             editor.toolbars[i].init(editor);
46379         }
46380          
46381         
46382     },
46383
46384      
46385     // private
46386     onRender : function(ct, position)
46387     {
46388         var _t = this;
46389         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46390         
46391         this.wrap = this.el.wrap({
46392             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46393         });
46394         
46395         this.editorcore.onRender(ct, position);
46396          
46397         if (this.resizable) {
46398             this.resizeEl = new Roo.Resizable(this.wrap, {
46399                 pinned : true,
46400                 wrap: true,
46401                 dynamic : true,
46402                 minHeight : this.height,
46403                 height: this.height,
46404                 handles : this.resizable,
46405                 width: this.width,
46406                 listeners : {
46407                     resize : function(r, w, h) {
46408                         _t.onResize(w,h); // -something
46409                     }
46410                 }
46411             });
46412             
46413         }
46414         this.createToolbar(this);
46415        
46416         
46417         if(!this.width){
46418             this.setSize(this.wrap.getSize());
46419         }
46420         if (this.resizeEl) {
46421             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46422             // should trigger onReize..
46423         }
46424         
46425         this.keyNav = new Roo.KeyNav(this.el, {
46426             
46427             "tab" : function(e){
46428                 e.preventDefault();
46429                 
46430                 var value = this.getValue();
46431                 
46432                 var start = this.el.dom.selectionStart;
46433                 var end = this.el.dom.selectionEnd;
46434                 
46435                 if(!e.shiftKey){
46436                     
46437                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46438                     this.el.dom.setSelectionRange(end + 1, end + 1);
46439                     return;
46440                 }
46441                 
46442                 var f = value.substring(0, start).split("\t");
46443                 
46444                 if(f.pop().length != 0){
46445                     return;
46446                 }
46447                 
46448                 this.setValue(f.join("\t") + value.substring(end));
46449                 this.el.dom.setSelectionRange(start - 1, start - 1);
46450                 
46451             },
46452             
46453             "home" : function(e){
46454                 e.preventDefault();
46455                 
46456                 var curr = this.el.dom.selectionStart;
46457                 var lines = this.getValue().split("\n");
46458                 
46459                 if(!lines.length){
46460                     return;
46461                 }
46462                 
46463                 if(e.ctrlKey){
46464                     this.el.dom.setSelectionRange(0, 0);
46465                     return;
46466                 }
46467                 
46468                 var pos = 0;
46469                 
46470                 for (var i = 0; i < lines.length;i++) {
46471                     pos += lines[i].length;
46472                     
46473                     if(i != 0){
46474                         pos += 1;
46475                     }
46476                     
46477                     if(pos < curr){
46478                         continue;
46479                     }
46480                     
46481                     pos -= lines[i].length;
46482                     
46483                     break;
46484                 }
46485                 
46486                 if(!e.shiftKey){
46487                     this.el.dom.setSelectionRange(pos, pos);
46488                     return;
46489                 }
46490                 
46491                 this.el.dom.selectionStart = pos;
46492                 this.el.dom.selectionEnd = curr;
46493             },
46494             
46495             "end" : function(e){
46496                 e.preventDefault();
46497                 
46498                 var curr = this.el.dom.selectionStart;
46499                 var lines = this.getValue().split("\n");
46500                 
46501                 if(!lines.length){
46502                     return;
46503                 }
46504                 
46505                 if(e.ctrlKey){
46506                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46507                     return;
46508                 }
46509                 
46510                 var pos = 0;
46511                 
46512                 for (var i = 0; i < lines.length;i++) {
46513                     
46514                     pos += lines[i].length;
46515                     
46516                     if(i != 0){
46517                         pos += 1;
46518                     }
46519                     
46520                     if(pos < curr){
46521                         continue;
46522                     }
46523                     
46524                     break;
46525                 }
46526                 
46527                 if(!e.shiftKey){
46528                     this.el.dom.setSelectionRange(pos, pos);
46529                     return;
46530                 }
46531                 
46532                 this.el.dom.selectionStart = curr;
46533                 this.el.dom.selectionEnd = pos;
46534             },
46535
46536             scope : this,
46537
46538             doRelay : function(foo, bar, hname){
46539                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46540             },
46541
46542             forceKeyDown: true
46543         });
46544         
46545 //        if(this.autosave && this.w){
46546 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46547 //        }
46548     },
46549
46550     // private
46551     onResize : function(w, h)
46552     {
46553         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
46554         var ew = false;
46555         var eh = false;
46556         
46557         if(this.el ){
46558             if(typeof w == 'number'){
46559                 var aw = w - this.wrap.getFrameWidth('lr');
46560                 this.el.setWidth(this.adjustWidth('textarea', aw));
46561                 ew = aw;
46562             }
46563             if(typeof h == 'number'){
46564                 var tbh = 0;
46565                 for (var i =0; i < this.toolbars.length;i++) {
46566                     // fixme - ask toolbars for heights?
46567                     tbh += this.toolbars[i].tb.el.getHeight();
46568                     if (this.toolbars[i].footer) {
46569                         tbh += this.toolbars[i].footer.el.getHeight();
46570                     }
46571                 }
46572                 
46573                 
46574                 
46575                 
46576                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
46577                 ah -= 5; // knock a few pixes off for look..
46578 //                Roo.log(ah);
46579                 this.el.setHeight(this.adjustWidth('textarea', ah));
46580                 var eh = ah;
46581             }
46582         }
46583         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
46584         this.editorcore.onResize(ew,eh);
46585         
46586     },
46587
46588     /**
46589      * Toggles the editor between standard and source edit mode.
46590      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46591      */
46592     toggleSourceEdit : function(sourceEditMode)
46593     {
46594         this.editorcore.toggleSourceEdit(sourceEditMode);
46595         
46596         if(this.editorcore.sourceEditMode){
46597             Roo.log('editor - showing textarea');
46598             
46599 //            Roo.log('in');
46600 //            Roo.log(this.syncValue());
46601             this.editorcore.syncValue();
46602             this.el.removeClass('x-hidden');
46603             this.el.dom.removeAttribute('tabIndex');
46604             this.el.focus();
46605             
46606             for (var i = 0; i < this.toolbars.length; i++) {
46607                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46608                     this.toolbars[i].tb.hide();
46609                     this.toolbars[i].footer.hide();
46610                 }
46611             }
46612             
46613         }else{
46614             Roo.log('editor - hiding textarea');
46615 //            Roo.log('out')
46616 //            Roo.log(this.pushValue()); 
46617             this.editorcore.pushValue();
46618             
46619             this.el.addClass('x-hidden');
46620             this.el.dom.setAttribute('tabIndex', -1);
46621             
46622             for (var i = 0; i < this.toolbars.length; i++) {
46623                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46624                     this.toolbars[i].tb.show();
46625                     this.toolbars[i].footer.show();
46626                 }
46627             }
46628             
46629             //this.deferFocus();
46630         }
46631         
46632         this.setSize(this.wrap.getSize());
46633         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
46634         
46635         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
46636     },
46637  
46638     // private (for BoxComponent)
46639     adjustSize : Roo.BoxComponent.prototype.adjustSize,
46640
46641     // private (for BoxComponent)
46642     getResizeEl : function(){
46643         return this.wrap;
46644     },
46645
46646     // private (for BoxComponent)
46647     getPositionEl : function(){
46648         return this.wrap;
46649     },
46650
46651     // private
46652     initEvents : function(){
46653         this.originalValue = this.getValue();
46654     },
46655
46656     /**
46657      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46658      * @method
46659      */
46660     markInvalid : Roo.emptyFn,
46661     /**
46662      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46663      * @method
46664      */
46665     clearInvalid : Roo.emptyFn,
46666
46667     setValue : function(v){
46668         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
46669         this.editorcore.pushValue();
46670     },
46671
46672      
46673     // private
46674     deferFocus : function(){
46675         this.focus.defer(10, this);
46676     },
46677
46678     // doc'ed in Field
46679     focus : function(){
46680         this.editorcore.focus();
46681         
46682     },
46683       
46684
46685     // private
46686     onDestroy : function(){
46687         
46688         
46689         
46690         if(this.rendered){
46691             
46692             for (var i =0; i < this.toolbars.length;i++) {
46693                 // fixme - ask toolbars for heights?
46694                 this.toolbars[i].onDestroy();
46695             }
46696             
46697             this.wrap.dom.innerHTML = '';
46698             this.wrap.remove();
46699         }
46700     },
46701
46702     // private
46703     onFirstFocus : function(){
46704         //Roo.log("onFirstFocus");
46705         this.editorcore.onFirstFocus();
46706          for (var i =0; i < this.toolbars.length;i++) {
46707             this.toolbars[i].onFirstFocus();
46708         }
46709         
46710     },
46711     
46712     // private
46713     syncValue : function()
46714     {
46715         this.editorcore.syncValue();
46716     },
46717     
46718     pushValue : function()
46719     {
46720         this.editorcore.pushValue();
46721     },
46722     
46723     setStylesheets : function(stylesheets)
46724     {
46725         this.editorcore.setStylesheets(stylesheets);
46726     },
46727     
46728     removeStylesheets : function()
46729     {
46730         this.editorcore.removeStylesheets();
46731     }
46732      
46733     
46734     // hide stuff that is not compatible
46735     /**
46736      * @event blur
46737      * @hide
46738      */
46739     /**
46740      * @event change
46741      * @hide
46742      */
46743     /**
46744      * @event focus
46745      * @hide
46746      */
46747     /**
46748      * @event specialkey
46749      * @hide
46750      */
46751     /**
46752      * @cfg {String} fieldClass @hide
46753      */
46754     /**
46755      * @cfg {String} focusClass @hide
46756      */
46757     /**
46758      * @cfg {String} autoCreate @hide
46759      */
46760     /**
46761      * @cfg {String} inputType @hide
46762      */
46763     /**
46764      * @cfg {String} invalidClass @hide
46765      */
46766     /**
46767      * @cfg {String} invalidText @hide
46768      */
46769     /**
46770      * @cfg {String} msgFx @hide
46771      */
46772     /**
46773      * @cfg {String} validateOnBlur @hide
46774      */
46775 });
46776  
46777     // <script type="text/javascript">
46778 /*
46779  * Based on
46780  * Ext JS Library 1.1.1
46781  * Copyright(c) 2006-2007, Ext JS, LLC.
46782  *  
46783  
46784  */
46785
46786 /**
46787  * @class Roo.form.HtmlEditorToolbar1
46788  * Basic Toolbar
46789  * 
46790  * Usage:
46791  *
46792  new Roo.form.HtmlEditor({
46793     ....
46794     toolbars : [
46795         new Roo.form.HtmlEditorToolbar1({
46796             disable : { fonts: 1 , format: 1, ..., ... , ...],
46797             btns : [ .... ]
46798         })
46799     }
46800      
46801  * 
46802  * @cfg {Object} disable List of elements to disable..
46803  * @cfg {Array} btns List of additional buttons.
46804  * 
46805  * 
46806  * NEEDS Extra CSS? 
46807  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46808  */
46809  
46810 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46811 {
46812     
46813     Roo.apply(this, config);
46814     
46815     // default disabled, based on 'good practice'..
46816     this.disable = this.disable || {};
46817     Roo.applyIf(this.disable, {
46818         fontSize : true,
46819         colors : true,
46820         specialElements : true
46821     });
46822     
46823     
46824     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46825     // dont call parent... till later.
46826 }
46827
46828 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46829     
46830     tb: false,
46831     
46832     rendered: false,
46833     
46834     editor : false,
46835     editorcore : false,
46836     /**
46837      * @cfg {Object} disable  List of toolbar elements to disable
46838          
46839      */
46840     disable : false,
46841     
46842     
46843      /**
46844      * @cfg {String} createLinkText The default text for the create link prompt
46845      */
46846     createLinkText : 'Please enter the URL for the link:',
46847     /**
46848      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46849      */
46850     defaultLinkValue : 'http:/'+'/',
46851    
46852     
46853       /**
46854      * @cfg {Array} fontFamilies An array of available font families
46855      */
46856     fontFamilies : [
46857         'Arial',
46858         'Courier New',
46859         'Tahoma',
46860         'Times New Roman',
46861         'Verdana'
46862     ],
46863     
46864     specialChars : [
46865            "&#169;",
46866           "&#174;",     
46867           "&#8482;",    
46868           "&#163;" ,    
46869          // "&#8212;",    
46870           "&#8230;",    
46871           "&#247;" ,    
46872         //  "&#225;" ,     ?? a acute?
46873            "&#8364;"    , //Euro
46874        //   "&#8220;"    ,
46875         //  "&#8221;"    ,
46876         //  "&#8226;"    ,
46877           "&#176;"  //   , // degrees
46878
46879          // "&#233;"     , // e ecute
46880          // "&#250;"     , // u ecute?
46881     ],
46882     
46883     specialElements : [
46884         {
46885             text: "Insert Table",
46886             xtype: 'MenuItem',
46887             xns : Roo.Menu,
46888             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46889                 
46890         },
46891         {    
46892             text: "Insert Image",
46893             xtype: 'MenuItem',
46894             xns : Roo.Menu,
46895             ihtml : '<img src="about:blank"/>'
46896             
46897         }
46898         
46899          
46900     ],
46901     
46902     
46903     inputElements : [ 
46904             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46905             "input:submit", "input:button", "select", "textarea", "label" ],
46906     formats : [
46907         ["p"] ,  
46908         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46909         ["pre"],[ "code"], 
46910         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46911         ['div'],['span'],
46912         ['sup'],['sub']
46913     ],
46914     
46915     cleanStyles : [
46916         "font-size"
46917     ],
46918      /**
46919      * @cfg {String} defaultFont default font to use.
46920      */
46921     defaultFont: 'tahoma',
46922    
46923     fontSelect : false,
46924     
46925     
46926     formatCombo : false,
46927     
46928     init : function(editor)
46929     {
46930         this.editor = editor;
46931         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46932         var editorcore = this.editorcore;
46933         
46934         var _t = this;
46935         
46936         var fid = editorcore.frameId;
46937         var etb = this;
46938         function btn(id, toggle, handler){
46939             var xid = fid + '-'+ id ;
46940             return {
46941                 id : xid,
46942                 cmd : id,
46943                 cls : 'x-btn-icon x-edit-'+id,
46944                 enableToggle:toggle !== false,
46945                 scope: _t, // was editor...
46946                 handler:handler||_t.relayBtnCmd,
46947                 clickEvent:'mousedown',
46948                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46949                 tabIndex:-1
46950             };
46951         }
46952         
46953         
46954         
46955         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46956         this.tb = tb;
46957          // stop form submits
46958         tb.el.on('click', function(e){
46959             e.preventDefault(); // what does this do?
46960         });
46961
46962         if(!this.disable.font) { // && !Roo.isSafari){
46963             /* why no safari for fonts 
46964             editor.fontSelect = tb.el.createChild({
46965                 tag:'select',
46966                 tabIndex: -1,
46967                 cls:'x-font-select',
46968                 html: this.createFontOptions()
46969             });
46970             
46971             editor.fontSelect.on('change', function(){
46972                 var font = editor.fontSelect.dom.value;
46973                 editor.relayCmd('fontname', font);
46974                 editor.deferFocus();
46975             }, editor);
46976             
46977             tb.add(
46978                 editor.fontSelect.dom,
46979                 '-'
46980             );
46981             */
46982             
46983         };
46984         if(!this.disable.formats){
46985             this.formatCombo = new Roo.form.ComboBox({
46986                 store: new Roo.data.SimpleStore({
46987                     id : 'tag',
46988                     fields: ['tag'],
46989                     data : this.formats // from states.js
46990                 }),
46991                 blockFocus : true,
46992                 name : '',
46993                 //autoCreate : {tag: "div",  size: "20"},
46994                 displayField:'tag',
46995                 typeAhead: false,
46996                 mode: 'local',
46997                 editable : false,
46998                 triggerAction: 'all',
46999                 emptyText:'Add tag',
47000                 selectOnFocus:true,
47001                 width:135,
47002                 listeners : {
47003                     'select': function(c, r, i) {
47004                         editorcore.insertTag(r.get('tag'));
47005                         editor.focus();
47006                     }
47007                 }
47008
47009             });
47010             tb.addField(this.formatCombo);
47011             
47012         }
47013         
47014         if(!this.disable.format){
47015             tb.add(
47016                 btn('bold'),
47017                 btn('italic'),
47018                 btn('underline'),
47019                 btn('strikethrough')
47020             );
47021         };
47022         if(!this.disable.fontSize){
47023             tb.add(
47024                 '-',
47025                 
47026                 
47027                 btn('increasefontsize', false, editorcore.adjustFont),
47028                 btn('decreasefontsize', false, editorcore.adjustFont)
47029             );
47030         };
47031         
47032         
47033         if(!this.disable.colors){
47034             tb.add(
47035                 '-', {
47036                     id:editorcore.frameId +'-forecolor',
47037                     cls:'x-btn-icon x-edit-forecolor',
47038                     clickEvent:'mousedown',
47039                     tooltip: this.buttonTips['forecolor'] || undefined,
47040                     tabIndex:-1,
47041                     menu : new Roo.menu.ColorMenu({
47042                         allowReselect: true,
47043                         focus: Roo.emptyFn,
47044                         value:'000000',
47045                         plain:true,
47046                         selectHandler: function(cp, color){
47047                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47048                             editor.deferFocus();
47049                         },
47050                         scope: editorcore,
47051                         clickEvent:'mousedown'
47052                     })
47053                 }, {
47054                     id:editorcore.frameId +'backcolor',
47055                     cls:'x-btn-icon x-edit-backcolor',
47056                     clickEvent:'mousedown',
47057                     tooltip: this.buttonTips['backcolor'] || undefined,
47058                     tabIndex:-1,
47059                     menu : new Roo.menu.ColorMenu({
47060                         focus: Roo.emptyFn,
47061                         value:'FFFFFF',
47062                         plain:true,
47063                         allowReselect: true,
47064                         selectHandler: function(cp, color){
47065                             if(Roo.isGecko){
47066                                 editorcore.execCmd('useCSS', false);
47067                                 editorcore.execCmd('hilitecolor', color);
47068                                 editorcore.execCmd('useCSS', true);
47069                                 editor.deferFocus();
47070                             }else{
47071                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47072                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47073                                 editor.deferFocus();
47074                             }
47075                         },
47076                         scope:editorcore,
47077                         clickEvent:'mousedown'
47078                     })
47079                 }
47080             );
47081         };
47082         // now add all the items...
47083         
47084
47085         if(!this.disable.alignments){
47086             tb.add(
47087                 '-',
47088                 btn('justifyleft'),
47089                 btn('justifycenter'),
47090                 btn('justifyright')
47091             );
47092         };
47093
47094         //if(!Roo.isSafari){
47095             if(!this.disable.links){
47096                 tb.add(
47097                     '-',
47098                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47099                 );
47100             };
47101
47102             if(!this.disable.lists){
47103                 tb.add(
47104                     '-',
47105                     btn('insertorderedlist'),
47106                     btn('insertunorderedlist')
47107                 );
47108             }
47109             if(!this.disable.sourceEdit){
47110                 tb.add(
47111                     '-',
47112                     btn('sourceedit', true, function(btn){
47113                         this.toggleSourceEdit(btn.pressed);
47114                     })
47115                 );
47116             }
47117         //}
47118         
47119         var smenu = { };
47120         // special menu.. - needs to be tidied up..
47121         if (!this.disable.special) {
47122             smenu = {
47123                 text: "&#169;",
47124                 cls: 'x-edit-none',
47125                 
47126                 menu : {
47127                     items : []
47128                 }
47129             };
47130             for (var i =0; i < this.specialChars.length; i++) {
47131                 smenu.menu.items.push({
47132                     
47133                     html: this.specialChars[i],
47134                     handler: function(a,b) {
47135                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47136                         //editor.insertAtCursor(a.html);
47137                         
47138                     },
47139                     tabIndex:-1
47140                 });
47141             }
47142             
47143             
47144             tb.add(smenu);
47145             
47146             
47147         }
47148         
47149         var cmenu = { };
47150         if (!this.disable.cleanStyles) {
47151             cmenu = {
47152                 cls: 'x-btn-icon x-btn-clear',
47153                 
47154                 menu : {
47155                     items : []
47156                 }
47157             };
47158             for (var i =0; i < this.cleanStyles.length; i++) {
47159                 cmenu.menu.items.push({
47160                     actiontype : this.cleanStyles[i],
47161                     html: 'Remove ' + this.cleanStyles[i],
47162                     handler: function(a,b) {
47163 //                        Roo.log(a);
47164 //                        Roo.log(b);
47165                         var c = Roo.get(editorcore.doc.body);
47166                         c.select('[style]').each(function(s) {
47167                             s.dom.style.removeProperty(a.actiontype);
47168                         });
47169                         editorcore.syncValue();
47170                     },
47171                     tabIndex:-1
47172                 });
47173             }
47174              cmenu.menu.items.push({
47175                 actiontype : 'tablewidths',
47176                 html: 'Remove Table Widths',
47177                 handler: function(a,b) {
47178                     editorcore.cleanTableWidths();
47179                     editorcore.syncValue();
47180                 },
47181                 tabIndex:-1
47182             });
47183             cmenu.menu.items.push({
47184                 actiontype : 'word',
47185                 html: 'Remove MS Word Formating',
47186                 handler: function(a,b) {
47187                     editorcore.cleanWord();
47188                     editorcore.syncValue();
47189                 },
47190                 tabIndex:-1
47191             });
47192             
47193             cmenu.menu.items.push({
47194                 actiontype : 'all',
47195                 html: 'Remove All Styles',
47196                 handler: function(a,b) {
47197                     
47198                     var c = Roo.get(editorcore.doc.body);
47199                     c.select('[style]').each(function(s) {
47200                         s.dom.removeAttribute('style');
47201                     });
47202                     editorcore.syncValue();
47203                 },
47204                 tabIndex:-1
47205             });
47206             
47207             cmenu.menu.items.push({
47208                 actiontype : 'all',
47209                 html: 'Remove All CSS Classes',
47210                 handler: function(a,b) {
47211                     
47212                     var c = Roo.get(editorcore.doc.body);
47213                     c.select('[class]').each(function(s) {
47214                         s.dom.removeAttribute('class');
47215                     });
47216                     editorcore.cleanWord();
47217                     editorcore.syncValue();
47218                 },
47219                 tabIndex:-1
47220             });
47221             
47222              cmenu.menu.items.push({
47223                 actiontype : 'tidy',
47224                 html: 'Tidy HTML Source',
47225                 handler: function(a,b) {
47226                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
47227                     editorcore.syncValue();
47228                 },
47229                 tabIndex:-1
47230             });
47231             
47232             
47233             tb.add(cmenu);
47234         }
47235          
47236         if (!this.disable.specialElements) {
47237             var semenu = {
47238                 text: "Other;",
47239                 cls: 'x-edit-none',
47240                 menu : {
47241                     items : []
47242                 }
47243             };
47244             for (var i =0; i < this.specialElements.length; i++) {
47245                 semenu.menu.items.push(
47246                     Roo.apply({ 
47247                         handler: function(a,b) {
47248                             editor.insertAtCursor(this.ihtml);
47249                         }
47250                     }, this.specialElements[i])
47251                 );
47252                     
47253             }
47254             
47255             tb.add(semenu);
47256             
47257             
47258         }
47259          
47260         
47261         if (this.btns) {
47262             for(var i =0; i< this.btns.length;i++) {
47263                 var b = Roo.factory(this.btns[i],Roo.form);
47264                 b.cls =  'x-edit-none';
47265                 
47266                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47267                     b.cls += ' x-init-enable';
47268                 }
47269                 
47270                 b.scope = editorcore;
47271                 tb.add(b);
47272             }
47273         
47274         }
47275         
47276         
47277         
47278         // disable everything...
47279         
47280         this.tb.items.each(function(item){
47281             
47282            if(
47283                 item.id != editorcore.frameId+ '-sourceedit' && 
47284                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47285             ){
47286                 
47287                 item.disable();
47288             }
47289         });
47290         this.rendered = true;
47291         
47292         // the all the btns;
47293         editor.on('editorevent', this.updateToolbar, this);
47294         // other toolbars need to implement this..
47295         //editor.on('editmodechange', this.updateToolbar, this);
47296     },
47297     
47298     
47299     relayBtnCmd : function(btn) {
47300         this.editorcore.relayCmd(btn.cmd);
47301     },
47302     // private used internally
47303     createLink : function(){
47304         Roo.log("create link?");
47305         var url = prompt(this.createLinkText, this.defaultLinkValue);
47306         if(url && url != 'http:/'+'/'){
47307             this.editorcore.relayCmd('createlink', url);
47308         }
47309     },
47310
47311     
47312     /**
47313      * Protected method that will not generally be called directly. It triggers
47314      * a toolbar update by reading the markup state of the current selection in the editor.
47315      */
47316     updateToolbar: function(){
47317
47318         if(!this.editorcore.activated){
47319             this.editor.onFirstFocus();
47320             return;
47321         }
47322
47323         var btns = this.tb.items.map, 
47324             doc = this.editorcore.doc,
47325             frameId = this.editorcore.frameId;
47326
47327         if(!this.disable.font && !Roo.isSafari){
47328             /*
47329             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47330             if(name != this.fontSelect.dom.value){
47331                 this.fontSelect.dom.value = name;
47332             }
47333             */
47334         }
47335         if(!this.disable.format){
47336             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47337             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47338             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47339             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47340         }
47341         if(!this.disable.alignments){
47342             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47343             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47344             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47345         }
47346         if(!Roo.isSafari && !this.disable.lists){
47347             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47348             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47349         }
47350         
47351         var ans = this.editorcore.getAllAncestors();
47352         if (this.formatCombo) {
47353             
47354             
47355             var store = this.formatCombo.store;
47356             this.formatCombo.setValue("");
47357             for (var i =0; i < ans.length;i++) {
47358                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47359                     // select it..
47360                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47361                     break;
47362                 }
47363             }
47364         }
47365         
47366         
47367         
47368         // hides menus... - so this cant be on a menu...
47369         Roo.menu.MenuMgr.hideAll();
47370
47371         //this.editorsyncValue();
47372     },
47373    
47374     
47375     createFontOptions : function(){
47376         var buf = [], fs = this.fontFamilies, ff, lc;
47377         
47378         
47379         
47380         for(var i = 0, len = fs.length; i< len; i++){
47381             ff = fs[i];
47382             lc = ff.toLowerCase();
47383             buf.push(
47384                 '<option value="',lc,'" style="font-family:',ff,';"',
47385                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47386                     ff,
47387                 '</option>'
47388             );
47389         }
47390         return buf.join('');
47391     },
47392     
47393     toggleSourceEdit : function(sourceEditMode){
47394         
47395         Roo.log("toolbar toogle");
47396         if(sourceEditMode === undefined){
47397             sourceEditMode = !this.sourceEditMode;
47398         }
47399         this.sourceEditMode = sourceEditMode === true;
47400         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47401         // just toggle the button?
47402         if(btn.pressed !== this.sourceEditMode){
47403             btn.toggle(this.sourceEditMode);
47404             return;
47405         }
47406         
47407         if(sourceEditMode){
47408             Roo.log("disabling buttons");
47409             this.tb.items.each(function(item){
47410                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47411                     item.disable();
47412                 }
47413             });
47414           
47415         }else{
47416             Roo.log("enabling buttons");
47417             if(this.editorcore.initialized){
47418                 this.tb.items.each(function(item){
47419                     item.enable();
47420                 });
47421             }
47422             
47423         }
47424         Roo.log("calling toggole on editor");
47425         // tell the editor that it's been pressed..
47426         this.editor.toggleSourceEdit(sourceEditMode);
47427        
47428     },
47429      /**
47430      * Object collection of toolbar tooltips for the buttons in the editor. The key
47431      * is the command id associated with that button and the value is a valid QuickTips object.
47432      * For example:
47433 <pre><code>
47434 {
47435     bold : {
47436         title: 'Bold (Ctrl+B)',
47437         text: 'Make the selected text bold.',
47438         cls: 'x-html-editor-tip'
47439     },
47440     italic : {
47441         title: 'Italic (Ctrl+I)',
47442         text: 'Make the selected text italic.',
47443         cls: 'x-html-editor-tip'
47444     },
47445     ...
47446 </code></pre>
47447     * @type Object
47448      */
47449     buttonTips : {
47450         bold : {
47451             title: 'Bold (Ctrl+B)',
47452             text: 'Make the selected text bold.',
47453             cls: 'x-html-editor-tip'
47454         },
47455         italic : {
47456             title: 'Italic (Ctrl+I)',
47457             text: 'Make the selected text italic.',
47458             cls: 'x-html-editor-tip'
47459         },
47460         underline : {
47461             title: 'Underline (Ctrl+U)',
47462             text: 'Underline the selected text.',
47463             cls: 'x-html-editor-tip'
47464         },
47465         strikethrough : {
47466             title: 'Strikethrough',
47467             text: 'Strikethrough the selected text.',
47468             cls: 'x-html-editor-tip'
47469         },
47470         increasefontsize : {
47471             title: 'Grow Text',
47472             text: 'Increase the font size.',
47473             cls: 'x-html-editor-tip'
47474         },
47475         decreasefontsize : {
47476             title: 'Shrink Text',
47477             text: 'Decrease the font size.',
47478             cls: 'x-html-editor-tip'
47479         },
47480         backcolor : {
47481             title: 'Text Highlight Color',
47482             text: 'Change the background color of the selected text.',
47483             cls: 'x-html-editor-tip'
47484         },
47485         forecolor : {
47486             title: 'Font Color',
47487             text: 'Change the color of the selected text.',
47488             cls: 'x-html-editor-tip'
47489         },
47490         justifyleft : {
47491             title: 'Align Text Left',
47492             text: 'Align text to the left.',
47493             cls: 'x-html-editor-tip'
47494         },
47495         justifycenter : {
47496             title: 'Center Text',
47497             text: 'Center text in the editor.',
47498             cls: 'x-html-editor-tip'
47499         },
47500         justifyright : {
47501             title: 'Align Text Right',
47502             text: 'Align text to the right.',
47503             cls: 'x-html-editor-tip'
47504         },
47505         insertunorderedlist : {
47506             title: 'Bullet List',
47507             text: 'Start a bulleted list.',
47508             cls: 'x-html-editor-tip'
47509         },
47510         insertorderedlist : {
47511             title: 'Numbered List',
47512             text: 'Start a numbered list.',
47513             cls: 'x-html-editor-tip'
47514         },
47515         createlink : {
47516             title: 'Hyperlink',
47517             text: 'Make the selected text a hyperlink.',
47518             cls: 'x-html-editor-tip'
47519         },
47520         sourceedit : {
47521             title: 'Source Edit',
47522             text: 'Switch to source editing mode.',
47523             cls: 'x-html-editor-tip'
47524         }
47525     },
47526     // private
47527     onDestroy : function(){
47528         if(this.rendered){
47529             
47530             this.tb.items.each(function(item){
47531                 if(item.menu){
47532                     item.menu.removeAll();
47533                     if(item.menu.el){
47534                         item.menu.el.destroy();
47535                     }
47536                 }
47537                 item.destroy();
47538             });
47539              
47540         }
47541     },
47542     onFirstFocus: function() {
47543         this.tb.items.each(function(item){
47544            item.enable();
47545         });
47546     }
47547 });
47548
47549
47550
47551
47552 // <script type="text/javascript">
47553 /*
47554  * Based on
47555  * Ext JS Library 1.1.1
47556  * Copyright(c) 2006-2007, Ext JS, LLC.
47557  *  
47558  
47559  */
47560
47561  
47562 /**
47563  * @class Roo.form.HtmlEditor.ToolbarContext
47564  * Context Toolbar
47565  * 
47566  * Usage:
47567  *
47568  new Roo.form.HtmlEditor({
47569     ....
47570     toolbars : [
47571         { xtype: 'ToolbarStandard', styles : {} }
47572         { xtype: 'ToolbarContext', disable : {} }
47573     ]
47574 })
47575
47576      
47577  * 
47578  * @config : {Object} disable List of elements to disable.. (not done yet.)
47579  * @config : {Object} styles  Map of styles available.
47580  * 
47581  */
47582
47583 Roo.form.HtmlEditor.ToolbarContext = function(config)
47584 {
47585     
47586     Roo.apply(this, config);
47587     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47588     // dont call parent... till later.
47589     this.styles = this.styles || {};
47590 }
47591
47592  
47593
47594 Roo.form.HtmlEditor.ToolbarContext.types = {
47595     'IMG' : {
47596         width : {
47597             title: "Width",
47598             width: 40
47599         },
47600         height:  {
47601             title: "Height",
47602             width: 40
47603         },
47604         align: {
47605             title: "Align",
47606             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47607             width : 80
47608             
47609         },
47610         border: {
47611             title: "Border",
47612             width: 40
47613         },
47614         alt: {
47615             title: "Alt",
47616             width: 120
47617         },
47618         src : {
47619             title: "Src",
47620             width: 220
47621         }
47622         
47623     },
47624     'A' : {
47625         name : {
47626             title: "Name",
47627             width: 50
47628         },
47629         target:  {
47630             title: "Target",
47631             width: 120
47632         },
47633         href:  {
47634             title: "Href",
47635             width: 220
47636         } // border?
47637         
47638     },
47639     'TABLE' : {
47640         rows : {
47641             title: "Rows",
47642             width: 20
47643         },
47644         cols : {
47645             title: "Cols",
47646             width: 20
47647         },
47648         width : {
47649             title: "Width",
47650             width: 40
47651         },
47652         height : {
47653             title: "Height",
47654             width: 40
47655         },
47656         border : {
47657             title: "Border",
47658             width: 20
47659         }
47660     },
47661     'TD' : {
47662         width : {
47663             title: "Width",
47664             width: 40
47665         },
47666         height : {
47667             title: "Height",
47668             width: 40
47669         },   
47670         align: {
47671             title: "Align",
47672             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
47673             width: 80
47674         },
47675         valign: {
47676             title: "Valign",
47677             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
47678             width: 80
47679         },
47680         colspan: {
47681             title: "Colspan",
47682             width: 20
47683             
47684         },
47685          'font-family'  : {
47686             title : "Font",
47687             style : 'fontFamily',
47688             displayField: 'display',
47689             optname : 'font-family',
47690             width: 140
47691         }
47692     },
47693     'INPUT' : {
47694         name : {
47695             title: "name",
47696             width: 120
47697         },
47698         value : {
47699             title: "Value",
47700             width: 120
47701         },
47702         width : {
47703             title: "Width",
47704             width: 40
47705         }
47706     },
47707     'LABEL' : {
47708         'for' : {
47709             title: "For",
47710             width: 120
47711         }
47712     },
47713     'TEXTAREA' : {
47714           name : {
47715             title: "name",
47716             width: 120
47717         },
47718         rows : {
47719             title: "Rows",
47720             width: 20
47721         },
47722         cols : {
47723             title: "Cols",
47724             width: 20
47725         }
47726     },
47727     'SELECT' : {
47728         name : {
47729             title: "name",
47730             width: 120
47731         },
47732         selectoptions : {
47733             title: "Options",
47734             width: 200
47735         }
47736     },
47737     
47738     // should we really allow this??
47739     // should this just be 
47740     'BODY' : {
47741         title : {
47742             title: "Title",
47743             width: 200,
47744             disabled : true
47745         }
47746     },
47747     'SPAN' : {
47748         'font-family'  : {
47749             title : "Font",
47750             style : 'fontFamily',
47751             displayField: 'display',
47752             optname : 'font-family',
47753             width: 140
47754         }
47755     },
47756     'DIV' : {
47757         'font-family'  : {
47758             title : "Font",
47759             style : 'fontFamily',
47760             displayField: 'display',
47761             optname : 'font-family',
47762             width: 140
47763         }
47764     },
47765      'P' : {
47766         'font-family'  : {
47767             title : "Font",
47768             style : 'fontFamily',
47769             displayField: 'display',
47770             optname : 'font-family',
47771             width: 140
47772         }
47773     },
47774     
47775     '*' : {
47776         // empty..
47777     }
47778
47779 };
47780
47781 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47782 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47783
47784 Roo.form.HtmlEditor.ToolbarContext.options = {
47785         'font-family'  : [ 
47786                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47787                 [ 'Courier New', 'Courier New'],
47788                 [ 'Tahoma', 'Tahoma'],
47789                 [ 'Times New Roman,serif', 'Times'],
47790                 [ 'Verdana','Verdana' ]
47791         ]
47792 };
47793
47794 // fixme - these need to be configurable..
47795  
47796
47797 //Roo.form.HtmlEditor.ToolbarContext.types
47798
47799
47800 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47801     
47802     tb: false,
47803     
47804     rendered: false,
47805     
47806     editor : false,
47807     editorcore : false,
47808     /**
47809      * @cfg {Object} disable  List of toolbar elements to disable
47810          
47811      */
47812     disable : false,
47813     /**
47814      * @cfg {Object} styles List of styles 
47815      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47816      *
47817      * These must be defined in the page, so they get rendered correctly..
47818      * .headline { }
47819      * TD.underline { }
47820      * 
47821      */
47822     styles : false,
47823     
47824     options: false,
47825     
47826     toolbars : false,
47827     
47828     init : function(editor)
47829     {
47830         this.editor = editor;
47831         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47832         var editorcore = this.editorcore;
47833         
47834         var fid = editorcore.frameId;
47835         var etb = this;
47836         function btn(id, toggle, handler){
47837             var xid = fid + '-'+ id ;
47838             return {
47839                 id : xid,
47840                 cmd : id,
47841                 cls : 'x-btn-icon x-edit-'+id,
47842                 enableToggle:toggle !== false,
47843                 scope: editorcore, // was editor...
47844                 handler:handler||editorcore.relayBtnCmd,
47845                 clickEvent:'mousedown',
47846                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47847                 tabIndex:-1
47848             };
47849         }
47850         // create a new element.
47851         var wdiv = editor.wrap.createChild({
47852                 tag: 'div'
47853             }, editor.wrap.dom.firstChild.nextSibling, true);
47854         
47855         // can we do this more than once??
47856         
47857          // stop form submits
47858       
47859  
47860         // disable everything...
47861         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47862         this.toolbars = {};
47863            
47864         for (var i in  ty) {
47865           
47866             this.toolbars[i] = this.buildToolbar(ty[i],i);
47867         }
47868         this.tb = this.toolbars.BODY;
47869         this.tb.el.show();
47870         this.buildFooter();
47871         this.footer.show();
47872         editor.on('hide', function( ) { this.footer.hide() }, this);
47873         editor.on('show', function( ) { this.footer.show() }, this);
47874         
47875          
47876         this.rendered = true;
47877         
47878         // the all the btns;
47879         editor.on('editorevent', this.updateToolbar, this);
47880         // other toolbars need to implement this..
47881         //editor.on('editmodechange', this.updateToolbar, this);
47882     },
47883     
47884     
47885     
47886     /**
47887      * Protected method that will not generally be called directly. It triggers
47888      * a toolbar update by reading the markup state of the current selection in the editor.
47889      *
47890      * Note you can force an update by calling on('editorevent', scope, false)
47891      */
47892     updateToolbar: function(editor,ev,sel){
47893
47894         //Roo.log(ev);
47895         // capture mouse up - this is handy for selecting images..
47896         // perhaps should go somewhere else...
47897         if(!this.editorcore.activated){
47898              this.editor.onFirstFocus();
47899             return;
47900         }
47901         
47902         
47903         
47904         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47905         // selectNode - might want to handle IE?
47906         if (ev &&
47907             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47908             ev.target && ev.target.tagName == 'IMG') {
47909             // they have click on an image...
47910             // let's see if we can change the selection...
47911             sel = ev.target;
47912          
47913               var nodeRange = sel.ownerDocument.createRange();
47914             try {
47915                 nodeRange.selectNode(sel);
47916             } catch (e) {
47917                 nodeRange.selectNodeContents(sel);
47918             }
47919             //nodeRange.collapse(true);
47920             var s = this.editorcore.win.getSelection();
47921             s.removeAllRanges();
47922             s.addRange(nodeRange);
47923         }  
47924         
47925       
47926         var updateFooter = sel ? false : true;
47927         
47928         
47929         var ans = this.editorcore.getAllAncestors();
47930         
47931         // pick
47932         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47933         
47934         if (!sel) { 
47935             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47936             sel = sel ? sel : this.editorcore.doc.body;
47937             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47938             
47939         }
47940         // pick a menu that exists..
47941         var tn = sel.tagName.toUpperCase();
47942         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47943         
47944         tn = sel.tagName.toUpperCase();
47945         
47946         var lastSel = this.tb.selectedNode;
47947         
47948         this.tb.selectedNode = sel;
47949         
47950         // if current menu does not match..
47951         
47952         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47953                 
47954             this.tb.el.hide();
47955             ///console.log("show: " + tn);
47956             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47957             this.tb.el.show();
47958             // update name
47959             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47960             
47961             
47962             // update attributes
47963             if (this.tb.fields) {
47964                 this.tb.fields.each(function(e) {
47965                     if (e.stylename) {
47966                         e.setValue(sel.style[e.stylename]);
47967                         return;
47968                     } 
47969                    e.setValue(sel.getAttribute(e.attrname));
47970                 });
47971             }
47972             
47973             var hasStyles = false;
47974             for(var i in this.styles) {
47975                 hasStyles = true;
47976                 break;
47977             }
47978             
47979             // update styles
47980             if (hasStyles) { 
47981                 var st = this.tb.fields.item(0);
47982                 
47983                 st.store.removeAll();
47984                
47985                 
47986                 var cn = sel.className.split(/\s+/);
47987                 
47988                 var avs = [];
47989                 if (this.styles['*']) {
47990                     
47991                     Roo.each(this.styles['*'], function(v) {
47992                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47993                     });
47994                 }
47995                 if (this.styles[tn]) { 
47996                     Roo.each(this.styles[tn], function(v) {
47997                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47998                     });
47999                 }
48000                 
48001                 st.store.loadData(avs);
48002                 st.collapse();
48003                 st.setValue(cn);
48004             }
48005             // flag our selected Node.
48006             this.tb.selectedNode = sel;
48007            
48008            
48009             Roo.menu.MenuMgr.hideAll();
48010
48011         }
48012         
48013         if (!updateFooter) {
48014             //this.footDisp.dom.innerHTML = ''; 
48015             return;
48016         }
48017         // update the footer
48018         //
48019         var html = '';
48020         
48021         this.footerEls = ans.reverse();
48022         Roo.each(this.footerEls, function(a,i) {
48023             if (!a) { return; }
48024             html += html.length ? ' &gt; '  :  '';
48025             
48026             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48027             
48028         });
48029        
48030         // 
48031         var sz = this.footDisp.up('td').getSize();
48032         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48033         this.footDisp.dom.style.marginLeft = '5px';
48034         
48035         this.footDisp.dom.style.overflow = 'hidden';
48036         
48037         this.footDisp.dom.innerHTML = html;
48038             
48039         //this.editorsyncValue();
48040     },
48041      
48042     
48043    
48044        
48045     // private
48046     onDestroy : function(){
48047         if(this.rendered){
48048             
48049             this.tb.items.each(function(item){
48050                 if(item.menu){
48051                     item.menu.removeAll();
48052                     if(item.menu.el){
48053                         item.menu.el.destroy();
48054                     }
48055                 }
48056                 item.destroy();
48057             });
48058              
48059         }
48060     },
48061     onFirstFocus: function() {
48062         // need to do this for all the toolbars..
48063         this.tb.items.each(function(item){
48064            item.enable();
48065         });
48066     },
48067     buildToolbar: function(tlist, nm)
48068     {
48069         var editor = this.editor;
48070         var editorcore = this.editorcore;
48071          // create a new element.
48072         var wdiv = editor.wrap.createChild({
48073                 tag: 'div'
48074             }, editor.wrap.dom.firstChild.nextSibling, true);
48075         
48076        
48077         var tb = new Roo.Toolbar(wdiv);
48078         // add the name..
48079         
48080         tb.add(nm+ ":&nbsp;");
48081         
48082         var styles = [];
48083         for(var i in this.styles) {
48084             styles.push(i);
48085         }
48086         
48087         // styles...
48088         if (styles && styles.length) {
48089             
48090             // this needs a multi-select checkbox...
48091             tb.addField( new Roo.form.ComboBox({
48092                 store: new Roo.data.SimpleStore({
48093                     id : 'val',
48094                     fields: ['val', 'selected'],
48095                     data : [] 
48096                 }),
48097                 name : '-roo-edit-className',
48098                 attrname : 'className',
48099                 displayField: 'val',
48100                 typeAhead: false,
48101                 mode: 'local',
48102                 editable : false,
48103                 triggerAction: 'all',
48104                 emptyText:'Select Style',
48105                 selectOnFocus:true,
48106                 width: 130,
48107                 listeners : {
48108                     'select': function(c, r, i) {
48109                         // initial support only for on class per el..
48110                         tb.selectedNode.className =  r ? r.get('val') : '';
48111                         editorcore.syncValue();
48112                     }
48113                 }
48114     
48115             }));
48116         }
48117         
48118         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48119         var tbops = tbc.options;
48120         
48121         for (var i in tlist) {
48122             
48123             var item = tlist[i];
48124             tb.add(item.title + ":&nbsp;");
48125             
48126             
48127             //optname == used so you can configure the options available..
48128             var opts = item.opts ? item.opts : false;
48129             if (item.optname) {
48130                 opts = tbops[item.optname];
48131            
48132             }
48133             
48134             if (opts) {
48135                 // opts == pulldown..
48136                 tb.addField( new Roo.form.ComboBox({
48137                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48138                         id : 'val',
48139                         fields: ['val', 'display'],
48140                         data : opts  
48141                     }),
48142                     name : '-roo-edit-' + i,
48143                     attrname : i,
48144                     stylename : item.style ? item.style : false,
48145                     displayField: item.displayField ? item.displayField : 'val',
48146                     valueField :  'val',
48147                     typeAhead: false,
48148                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48149                     editable : false,
48150                     triggerAction: 'all',
48151                     emptyText:'Select',
48152                     selectOnFocus:true,
48153                     width: item.width ? item.width  : 130,
48154                     listeners : {
48155                         'select': function(c, r, i) {
48156                             if (c.stylename) {
48157                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48158                                 return;
48159                             }
48160                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48161                         }
48162                     }
48163
48164                 }));
48165                 continue;
48166                     
48167                  
48168                 
48169                 tb.addField( new Roo.form.TextField({
48170                     name: i,
48171                     width: 100,
48172                     //allowBlank:false,
48173                     value: ''
48174                 }));
48175                 continue;
48176             }
48177             tb.addField( new Roo.form.TextField({
48178                 name: '-roo-edit-' + i,
48179                 attrname : i,
48180                 
48181                 width: item.width,
48182                 //allowBlank:true,
48183                 value: '',
48184                 listeners: {
48185                     'change' : function(f, nv, ov) {
48186                         tb.selectedNode.setAttribute(f.attrname, nv);
48187                         editorcore.syncValue();
48188                     }
48189                 }
48190             }));
48191              
48192         }
48193         
48194         var _this = this;
48195         
48196         if(nm == 'BODY'){
48197             tb.addSeparator();
48198         
48199             tb.addButton( {
48200                 text: 'Stylesheets',
48201
48202                 listeners : {
48203                     click : function ()
48204                     {
48205                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48206                     }
48207                 }
48208             });
48209         }
48210         
48211         tb.addFill();
48212         tb.addButton( {
48213             text: 'Remove Tag',
48214     
48215             listeners : {
48216                 click : function ()
48217                 {
48218                     // remove
48219                     // undo does not work.
48220                      
48221                     var sn = tb.selectedNode;
48222                     
48223                     var pn = sn.parentNode;
48224                     
48225                     var stn =  sn.childNodes[0];
48226                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48227                     while (sn.childNodes.length) {
48228                         var node = sn.childNodes[0];
48229                         sn.removeChild(node);
48230                         //Roo.log(node);
48231                         pn.insertBefore(node, sn);
48232                         
48233                     }
48234                     pn.removeChild(sn);
48235                     var range = editorcore.createRange();
48236         
48237                     range.setStart(stn,0);
48238                     range.setEnd(en,0); //????
48239                     //range.selectNode(sel);
48240                     
48241                     
48242                     var selection = editorcore.getSelection();
48243                     selection.removeAllRanges();
48244                     selection.addRange(range);
48245                     
48246                     
48247                     
48248                     //_this.updateToolbar(null, null, pn);
48249                     _this.updateToolbar(null, null, null);
48250                     _this.footDisp.dom.innerHTML = ''; 
48251                 }
48252             }
48253             
48254                     
48255                 
48256             
48257         });
48258         
48259         
48260         tb.el.on('click', function(e){
48261             e.preventDefault(); // what does this do?
48262         });
48263         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48264         tb.el.hide();
48265         tb.name = nm;
48266         // dont need to disable them... as they will get hidden
48267         return tb;
48268          
48269         
48270     },
48271     buildFooter : function()
48272     {
48273         
48274         var fel = this.editor.wrap.createChild();
48275         this.footer = new Roo.Toolbar(fel);
48276         // toolbar has scrolly on left / right?
48277         var footDisp= new Roo.Toolbar.Fill();
48278         var _t = this;
48279         this.footer.add(
48280             {
48281                 text : '&lt;',
48282                 xtype: 'Button',
48283                 handler : function() {
48284                     _t.footDisp.scrollTo('left',0,true)
48285                 }
48286             }
48287         );
48288         this.footer.add( footDisp );
48289         this.footer.add( 
48290             {
48291                 text : '&gt;',
48292                 xtype: 'Button',
48293                 handler : function() {
48294                     // no animation..
48295                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48296                 }
48297             }
48298         );
48299         var fel = Roo.get(footDisp.el);
48300         fel.addClass('x-editor-context');
48301         this.footDispWrap = fel; 
48302         this.footDispWrap.overflow  = 'hidden';
48303         
48304         this.footDisp = fel.createChild();
48305         this.footDispWrap.on('click', this.onContextClick, this)
48306         
48307         
48308     },
48309     onContextClick : function (ev,dom)
48310     {
48311         ev.preventDefault();
48312         var  cn = dom.className;
48313         //Roo.log(cn);
48314         if (!cn.match(/x-ed-loc-/)) {
48315             return;
48316         }
48317         var n = cn.split('-').pop();
48318         var ans = this.footerEls;
48319         var sel = ans[n];
48320         
48321          // pick
48322         var range = this.editorcore.createRange();
48323         
48324         range.selectNodeContents(sel);
48325         //range.selectNode(sel);
48326         
48327         
48328         var selection = this.editorcore.getSelection();
48329         selection.removeAllRanges();
48330         selection.addRange(range);
48331         
48332         
48333         
48334         this.updateToolbar(null, null, sel);
48335         
48336         
48337     }
48338     
48339     
48340     
48341     
48342     
48343 });
48344
48345
48346
48347
48348
48349 /*
48350  * Based on:
48351  * Ext JS Library 1.1.1
48352  * Copyright(c) 2006-2007, Ext JS, LLC.
48353  *
48354  * Originally Released Under LGPL - original licence link has changed is not relivant.
48355  *
48356  * Fork - LGPL
48357  * <script type="text/javascript">
48358  */
48359  
48360 /**
48361  * @class Roo.form.BasicForm
48362  * @extends Roo.util.Observable
48363  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48364  * @constructor
48365  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48366  * @param {Object} config Configuration options
48367  */
48368 Roo.form.BasicForm = function(el, config){
48369     this.allItems = [];
48370     this.childForms = [];
48371     Roo.apply(this, config);
48372     /*
48373      * The Roo.form.Field items in this form.
48374      * @type MixedCollection
48375      */
48376      
48377      
48378     this.items = new Roo.util.MixedCollection(false, function(o){
48379         return o.id || (o.id = Roo.id());
48380     });
48381     this.addEvents({
48382         /**
48383          * @event beforeaction
48384          * Fires before any action is performed. Return false to cancel the action.
48385          * @param {Form} this
48386          * @param {Action} action The action to be performed
48387          */
48388         beforeaction: true,
48389         /**
48390          * @event actionfailed
48391          * Fires when an action fails.
48392          * @param {Form} this
48393          * @param {Action} action The action that failed
48394          */
48395         actionfailed : true,
48396         /**
48397          * @event actioncomplete
48398          * Fires when an action is completed.
48399          * @param {Form} this
48400          * @param {Action} action The action that completed
48401          */
48402         actioncomplete : true
48403     });
48404     if(el){
48405         this.initEl(el);
48406     }
48407     Roo.form.BasicForm.superclass.constructor.call(this);
48408     
48409     Roo.form.BasicForm.popover.apply();
48410 };
48411
48412 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48413     /**
48414      * @cfg {String} method
48415      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48416      */
48417     /**
48418      * @cfg {DataReader} reader
48419      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48420      * This is optional as there is built-in support for processing JSON.
48421      */
48422     /**
48423      * @cfg {DataReader} errorReader
48424      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48425      * This is completely optional as there is built-in support for processing JSON.
48426      */
48427     /**
48428      * @cfg {String} url
48429      * The URL to use for form actions if one isn't supplied in the action options.
48430      */
48431     /**
48432      * @cfg {Boolean} fileUpload
48433      * Set to true if this form is a file upload.
48434      */
48435      
48436     /**
48437      * @cfg {Object} baseParams
48438      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48439      */
48440      /**
48441      
48442     /**
48443      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48444      */
48445     timeout: 30,
48446
48447     // private
48448     activeAction : null,
48449
48450     /**
48451      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48452      * or setValues() data instead of when the form was first created.
48453      */
48454     trackResetOnLoad : false,
48455     
48456     
48457     /**
48458      * childForms - used for multi-tab forms
48459      * @type {Array}
48460      */
48461     childForms : false,
48462     
48463     /**
48464      * allItems - full list of fields.
48465      * @type {Array}
48466      */
48467     allItems : false,
48468     
48469     /**
48470      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48471      * element by passing it or its id or mask the form itself by passing in true.
48472      * @type Mixed
48473      */
48474     waitMsgTarget : false,
48475     
48476     /**
48477      * @type Boolean
48478      */
48479     disableMask : false,
48480     
48481     /**
48482      * @cfg {Boolean} errorMask (true|false) default false
48483      */
48484     errorMask : false,
48485     
48486     /**
48487      * @cfg {Number} maskOffset Default 100
48488      */
48489     maskOffset : 100,
48490
48491     // private
48492     initEl : function(el){
48493         this.el = Roo.get(el);
48494         this.id = this.el.id || Roo.id();
48495         this.el.on('submit', this.onSubmit, this);
48496         this.el.addClass('x-form');
48497     },
48498
48499     // private
48500     onSubmit : function(e){
48501         e.stopEvent();
48502     },
48503
48504     /**
48505      * Returns true if client-side validation on the form is successful.
48506      * @return Boolean
48507      */
48508     isValid : function(){
48509         var valid = true;
48510         var target = false;
48511         this.items.each(function(f){
48512             if(f.validate()){
48513                 return;
48514             }
48515             
48516             valid = false;
48517                 
48518             if(!target && f.el.isVisible(true)){
48519                 target = f;
48520             }
48521         });
48522         
48523         if(this.errorMask && !valid){
48524             Roo.form.BasicForm.popover.mask(this, target);
48525         }
48526         
48527         return valid;
48528     },
48529     /**
48530      * Returns array of invalid form fields.
48531      * @return Array
48532      */
48533     
48534     invalidFields : function()
48535     {
48536         var ret = [];
48537         this.items.each(function(f){
48538             if(f.validate()){
48539                 return;
48540             }
48541             ret.push(f);
48542             
48543         });
48544         
48545         return ret;
48546     },
48547     
48548     
48549     /**
48550      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48551      * @return Boolean
48552      */
48553     isDirty : function(){
48554         var dirty = false;
48555         this.items.each(function(f){
48556            if(f.isDirty()){
48557                dirty = true;
48558                return false;
48559            }
48560         });
48561         return dirty;
48562     },
48563     
48564     /**
48565      * Returns true if any fields in this form have changed since their original load. (New version)
48566      * @return Boolean
48567      */
48568     
48569     hasChanged : function()
48570     {
48571         var dirty = false;
48572         this.items.each(function(f){
48573            if(f.hasChanged()){
48574                dirty = true;
48575                return false;
48576            }
48577         });
48578         return dirty;
48579         
48580     },
48581     /**
48582      * Resets all hasChanged to 'false' -
48583      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
48584      * So hasChanged storage is only to be used for this purpose
48585      * @return Boolean
48586      */
48587     resetHasChanged : function()
48588     {
48589         this.items.each(function(f){
48590            f.resetHasChanged();
48591         });
48592         
48593     },
48594     
48595     
48596     /**
48597      * Performs a predefined action (submit or load) or custom actions you define on this form.
48598      * @param {String} actionName The name of the action type
48599      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
48600      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
48601      * accept other config options):
48602      * <pre>
48603 Property          Type             Description
48604 ----------------  ---------------  ----------------------------------------------------------------------------------
48605 url               String           The url for the action (defaults to the form's url)
48606 method            String           The form method to use (defaults to the form's method, or POST if not defined)
48607 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
48608 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
48609                                    validate the form on the client (defaults to false)
48610      * </pre>
48611      * @return {BasicForm} this
48612      */
48613     doAction : function(action, options){
48614         if(typeof action == 'string'){
48615             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
48616         }
48617         if(this.fireEvent('beforeaction', this, action) !== false){
48618             this.beforeAction(action);
48619             action.run.defer(100, action);
48620         }
48621         return this;
48622     },
48623
48624     /**
48625      * Shortcut to do a submit action.
48626      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48627      * @return {BasicForm} this
48628      */
48629     submit : function(options){
48630         this.doAction('submit', options);
48631         return this;
48632     },
48633
48634     /**
48635      * Shortcut to do a load action.
48636      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48637      * @return {BasicForm} this
48638      */
48639     load : function(options){
48640         this.doAction('load', options);
48641         return this;
48642     },
48643
48644     /**
48645      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
48646      * @param {Record} record The record to edit
48647      * @return {BasicForm} this
48648      */
48649     updateRecord : function(record){
48650         record.beginEdit();
48651         var fs = record.fields;
48652         fs.each(function(f){
48653             var field = this.findField(f.name);
48654             if(field){
48655                 record.set(f.name, field.getValue());
48656             }
48657         }, this);
48658         record.endEdit();
48659         return this;
48660     },
48661
48662     /**
48663      * Loads an Roo.data.Record into this form.
48664      * @param {Record} record The record to load
48665      * @return {BasicForm} this
48666      */
48667     loadRecord : function(record){
48668         this.setValues(record.data);
48669         return this;
48670     },
48671
48672     // private
48673     beforeAction : function(action){
48674         var o = action.options;
48675         
48676         if(!this.disableMask) {
48677             if(this.waitMsgTarget === true){
48678                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
48679             }else if(this.waitMsgTarget){
48680                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
48681                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
48682             }else {
48683                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
48684             }
48685         }
48686         
48687          
48688     },
48689
48690     // private
48691     afterAction : function(action, success){
48692         this.activeAction = null;
48693         var o = action.options;
48694         
48695         if(!this.disableMask) {
48696             if(this.waitMsgTarget === true){
48697                 this.el.unmask();
48698             }else if(this.waitMsgTarget){
48699                 this.waitMsgTarget.unmask();
48700             }else{
48701                 Roo.MessageBox.updateProgress(1);
48702                 Roo.MessageBox.hide();
48703             }
48704         }
48705         
48706         if(success){
48707             if(o.reset){
48708                 this.reset();
48709             }
48710             Roo.callback(o.success, o.scope, [this, action]);
48711             this.fireEvent('actioncomplete', this, action);
48712             
48713         }else{
48714             
48715             // failure condition..
48716             // we have a scenario where updates need confirming.
48717             // eg. if a locking scenario exists..
48718             // we look for { errors : { needs_confirm : true }} in the response.
48719             if (
48720                 (typeof(action.result) != 'undefined')  &&
48721                 (typeof(action.result.errors) != 'undefined')  &&
48722                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48723            ){
48724                 var _t = this;
48725                 Roo.MessageBox.confirm(
48726                     "Change requires confirmation",
48727                     action.result.errorMsg,
48728                     function(r) {
48729                         if (r != 'yes') {
48730                             return;
48731                         }
48732                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48733                     }
48734                     
48735                 );
48736                 
48737                 
48738                 
48739                 return;
48740             }
48741             
48742             Roo.callback(o.failure, o.scope, [this, action]);
48743             // show an error message if no failed handler is set..
48744             if (!this.hasListener('actionfailed')) {
48745                 Roo.MessageBox.alert("Error",
48746                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48747                         action.result.errorMsg :
48748                         "Saving Failed, please check your entries or try again"
48749                 );
48750             }
48751             
48752             this.fireEvent('actionfailed', this, action);
48753         }
48754         
48755     },
48756
48757     /**
48758      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48759      * @param {String} id The value to search for
48760      * @return Field
48761      */
48762     findField : function(id){
48763         var field = this.items.get(id);
48764         if(!field){
48765             this.items.each(function(f){
48766                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48767                     field = f;
48768                     return false;
48769                 }
48770             });
48771         }
48772         return field || null;
48773     },
48774
48775     /**
48776      * Add a secondary form to this one, 
48777      * Used to provide tabbed forms. One form is primary, with hidden values 
48778      * which mirror the elements from the other forms.
48779      * 
48780      * @param {Roo.form.Form} form to add.
48781      * 
48782      */
48783     addForm : function(form)
48784     {
48785        
48786         if (this.childForms.indexOf(form) > -1) {
48787             // already added..
48788             return;
48789         }
48790         this.childForms.push(form);
48791         var n = '';
48792         Roo.each(form.allItems, function (fe) {
48793             
48794             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48795             if (this.findField(n)) { // already added..
48796                 return;
48797             }
48798             var add = new Roo.form.Hidden({
48799                 name : n
48800             });
48801             add.render(this.el);
48802             
48803             this.add( add );
48804         }, this);
48805         
48806     },
48807     /**
48808      * Mark fields in this form invalid in bulk.
48809      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48810      * @return {BasicForm} this
48811      */
48812     markInvalid : function(errors){
48813         if(errors instanceof Array){
48814             for(var i = 0, len = errors.length; i < len; i++){
48815                 var fieldError = errors[i];
48816                 var f = this.findField(fieldError.id);
48817                 if(f){
48818                     f.markInvalid(fieldError.msg);
48819                 }
48820             }
48821         }else{
48822             var field, id;
48823             for(id in errors){
48824                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48825                     field.markInvalid(errors[id]);
48826                 }
48827             }
48828         }
48829         Roo.each(this.childForms || [], function (f) {
48830             f.markInvalid(errors);
48831         });
48832         
48833         return this;
48834     },
48835
48836     /**
48837      * Set values for fields in this form in bulk.
48838      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48839      * @return {BasicForm} this
48840      */
48841     setValues : function(values){
48842         if(values instanceof Array){ // array of objects
48843             for(var i = 0, len = values.length; i < len; i++){
48844                 var v = values[i];
48845                 var f = this.findField(v.id);
48846                 if(f){
48847                     f.setValue(v.value);
48848                     if(this.trackResetOnLoad){
48849                         f.originalValue = f.getValue();
48850                     }
48851                 }
48852             }
48853         }else{ // object hash
48854             var field, id;
48855             for(id in values){
48856                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48857                     
48858                     if (field.setFromData && 
48859                         field.valueField && 
48860                         field.displayField &&
48861                         // combos' with local stores can 
48862                         // be queried via setValue()
48863                         // to set their value..
48864                         (field.store && !field.store.isLocal)
48865                         ) {
48866                         // it's a combo
48867                         var sd = { };
48868                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48869                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48870                         field.setFromData(sd);
48871                         
48872                     } else {
48873                         field.setValue(values[id]);
48874                     }
48875                     
48876                     
48877                     if(this.trackResetOnLoad){
48878                         field.originalValue = field.getValue();
48879                     }
48880                 }
48881             }
48882         }
48883         this.resetHasChanged();
48884         
48885         
48886         Roo.each(this.childForms || [], function (f) {
48887             f.setValues(values);
48888             f.resetHasChanged();
48889         });
48890                 
48891         return this;
48892     },
48893  
48894     /**
48895      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48896      * they are returned as an array.
48897      * @param {Boolean} asString
48898      * @return {Object}
48899      */
48900     getValues : function(asString){
48901         if (this.childForms) {
48902             // copy values from the child forms
48903             Roo.each(this.childForms, function (f) {
48904                 this.setValues(f.getValues());
48905             }, this);
48906         }
48907         
48908         // use formdata
48909         if (typeof(FormData) != 'undefined' && asString !== true) {
48910             // this relies on a 'recent' version of chrome apparently...
48911             try {
48912                 var fd = (new FormData(this.el.dom)).entries();
48913                 var ret = {};
48914                 var ent = fd.next();
48915                 while (!ent.done) {
48916                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48917                     ent = fd.next();
48918                 };
48919                 return ret;
48920             } catch(e) {
48921                 
48922             }
48923             
48924         }
48925         
48926         
48927         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48928         if(asString === true){
48929             return fs;
48930         }
48931         return Roo.urlDecode(fs);
48932     },
48933     
48934     /**
48935      * Returns the fields in this form as an object with key/value pairs. 
48936      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48937      * @return {Object}
48938      */
48939     getFieldValues : function(with_hidden)
48940     {
48941         if (this.childForms) {
48942             // copy values from the child forms
48943             // should this call getFieldValues - probably not as we do not currently copy
48944             // hidden fields when we generate..
48945             Roo.each(this.childForms, function (f) {
48946                 this.setValues(f.getValues());
48947             }, this);
48948         }
48949         
48950         var ret = {};
48951         this.items.each(function(f){
48952             if (!f.getName()) {
48953                 return;
48954             }
48955             var v = f.getValue();
48956             if (f.inputType =='radio') {
48957                 if (typeof(ret[f.getName()]) == 'undefined') {
48958                     ret[f.getName()] = ''; // empty..
48959                 }
48960                 
48961                 if (!f.el.dom.checked) {
48962                     return;
48963                     
48964                 }
48965                 v = f.el.dom.value;
48966                 
48967             }
48968             
48969             // not sure if this supported any more..
48970             if ((typeof(v) == 'object') && f.getRawValue) {
48971                 v = f.getRawValue() ; // dates..
48972             }
48973             // combo boxes where name != hiddenName...
48974             if (f.name != f.getName()) {
48975                 ret[f.name] = f.getRawValue();
48976             }
48977             ret[f.getName()] = v;
48978         });
48979         
48980         return ret;
48981     },
48982
48983     /**
48984      * Clears all invalid messages in this form.
48985      * @return {BasicForm} this
48986      */
48987     clearInvalid : function(){
48988         this.items.each(function(f){
48989            f.clearInvalid();
48990         });
48991         
48992         Roo.each(this.childForms || [], function (f) {
48993             f.clearInvalid();
48994         });
48995         
48996         
48997         return this;
48998     },
48999
49000     /**
49001      * Resets this form.
49002      * @return {BasicForm} this
49003      */
49004     reset : function(){
49005         this.items.each(function(f){
49006             f.reset();
49007         });
49008         
49009         Roo.each(this.childForms || [], function (f) {
49010             f.reset();
49011         });
49012         this.resetHasChanged();
49013         
49014         return this;
49015     },
49016
49017     /**
49018      * Add Roo.form components to this form.
49019      * @param {Field} field1
49020      * @param {Field} field2 (optional)
49021      * @param {Field} etc (optional)
49022      * @return {BasicForm} this
49023      */
49024     add : function(){
49025         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49026         return this;
49027     },
49028
49029
49030     /**
49031      * Removes a field from the items collection (does NOT remove its markup).
49032      * @param {Field} field
49033      * @return {BasicForm} this
49034      */
49035     remove : function(field){
49036         this.items.remove(field);
49037         return this;
49038     },
49039
49040     /**
49041      * Looks at the fields in this form, checks them for an id attribute,
49042      * and calls applyTo on the existing dom element with that id.
49043      * @return {BasicForm} this
49044      */
49045     render : function(){
49046         this.items.each(function(f){
49047             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49048                 f.applyTo(f.id);
49049             }
49050         });
49051         return this;
49052     },
49053
49054     /**
49055      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49056      * @param {Object} values
49057      * @return {BasicForm} this
49058      */
49059     applyToFields : function(o){
49060         this.items.each(function(f){
49061            Roo.apply(f, o);
49062         });
49063         return this;
49064     },
49065
49066     /**
49067      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49068      * @param {Object} values
49069      * @return {BasicForm} this
49070      */
49071     applyIfToFields : function(o){
49072         this.items.each(function(f){
49073            Roo.applyIf(f, o);
49074         });
49075         return this;
49076     }
49077 });
49078
49079 // back compat
49080 Roo.BasicForm = Roo.form.BasicForm;
49081
49082 Roo.apply(Roo.form.BasicForm, {
49083     
49084     popover : {
49085         
49086         padding : 5,
49087         
49088         isApplied : false,
49089         
49090         isMasked : false,
49091         
49092         form : false,
49093         
49094         target : false,
49095         
49096         intervalID : false,
49097         
49098         maskEl : false,
49099         
49100         apply : function()
49101         {
49102             if(this.isApplied){
49103                 return;
49104             }
49105             
49106             this.maskEl = {
49107                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49108                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49109                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49110                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49111             };
49112             
49113             this.maskEl.top.enableDisplayMode("block");
49114             this.maskEl.left.enableDisplayMode("block");
49115             this.maskEl.bottom.enableDisplayMode("block");
49116             this.maskEl.right.enableDisplayMode("block");
49117             
49118             Roo.get(document.body).on('click', function(){
49119                 this.unmask();
49120             }, this);
49121             
49122             Roo.get(document.body).on('touchstart', function(){
49123                 this.unmask();
49124             }, this);
49125             
49126             this.isApplied = true
49127         },
49128         
49129         mask : function(form, target)
49130         {
49131             this.form = form;
49132             
49133             this.target = target;
49134             
49135             if(!this.form.errorMask || !target.el){
49136                 return;
49137             }
49138             
49139             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49140             
49141             var ot = this.target.el.calcOffsetsTo(scrollable);
49142             
49143             var scrollTo = ot[1] - this.form.maskOffset;
49144             
49145             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49146             
49147             scrollable.scrollTo('top', scrollTo);
49148             
49149             var el = this.target.wrap || this.target.el;
49150             
49151             var box = el.getBox();
49152             
49153             this.maskEl.top.setStyle('position', 'absolute');
49154             this.maskEl.top.setStyle('z-index', 10000);
49155             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49156             this.maskEl.top.setLeft(0);
49157             this.maskEl.top.setTop(0);
49158             this.maskEl.top.show();
49159             
49160             this.maskEl.left.setStyle('position', 'absolute');
49161             this.maskEl.left.setStyle('z-index', 10000);
49162             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49163             this.maskEl.left.setLeft(0);
49164             this.maskEl.left.setTop(box.y - this.padding);
49165             this.maskEl.left.show();
49166
49167             this.maskEl.bottom.setStyle('position', 'absolute');
49168             this.maskEl.bottom.setStyle('z-index', 10000);
49169             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49170             this.maskEl.bottom.setLeft(0);
49171             this.maskEl.bottom.setTop(box.bottom + this.padding);
49172             this.maskEl.bottom.show();
49173
49174             this.maskEl.right.setStyle('position', 'absolute');
49175             this.maskEl.right.setStyle('z-index', 10000);
49176             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49177             this.maskEl.right.setLeft(box.right + this.padding);
49178             this.maskEl.right.setTop(box.y - this.padding);
49179             this.maskEl.right.show();
49180
49181             this.intervalID = window.setInterval(function() {
49182                 Roo.form.BasicForm.popover.unmask();
49183             }, 10000);
49184
49185             window.onwheel = function(){ return false;};
49186             
49187             (function(){ this.isMasked = true; }).defer(500, this);
49188             
49189         },
49190         
49191         unmask : function()
49192         {
49193             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49194                 return;
49195             }
49196             
49197             this.maskEl.top.setStyle('position', 'absolute');
49198             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49199             this.maskEl.top.hide();
49200
49201             this.maskEl.left.setStyle('position', 'absolute');
49202             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49203             this.maskEl.left.hide();
49204
49205             this.maskEl.bottom.setStyle('position', 'absolute');
49206             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49207             this.maskEl.bottom.hide();
49208
49209             this.maskEl.right.setStyle('position', 'absolute');
49210             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49211             this.maskEl.right.hide();
49212             
49213             window.onwheel = function(){ return true;};
49214             
49215             if(this.intervalID){
49216                 window.clearInterval(this.intervalID);
49217                 this.intervalID = false;
49218             }
49219             
49220             this.isMasked = false;
49221             
49222         }
49223         
49224     }
49225     
49226 });/*
49227  * Based on:
49228  * Ext JS Library 1.1.1
49229  * Copyright(c) 2006-2007, Ext JS, LLC.
49230  *
49231  * Originally Released Under LGPL - original licence link has changed is not relivant.
49232  *
49233  * Fork - LGPL
49234  * <script type="text/javascript">
49235  */
49236
49237 /**
49238  * @class Roo.form.Form
49239  * @extends Roo.form.BasicForm
49240  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49241  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49242  * @constructor
49243  * @param {Object} config Configuration options
49244  */
49245 Roo.form.Form = function(config){
49246     var xitems =  [];
49247     if (config.items) {
49248         xitems = config.items;
49249         delete config.items;
49250     }
49251    
49252     
49253     Roo.form.Form.superclass.constructor.call(this, null, config);
49254     this.url = this.url || this.action;
49255     if(!this.root){
49256         this.root = new Roo.form.Layout(Roo.applyIf({
49257             id: Roo.id()
49258         }, config));
49259     }
49260     this.active = this.root;
49261     /**
49262      * Array of all the buttons that have been added to this form via {@link addButton}
49263      * @type Array
49264      */
49265     this.buttons = [];
49266     this.allItems = [];
49267     this.addEvents({
49268         /**
49269          * @event clientvalidation
49270          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49271          * @param {Form} this
49272          * @param {Boolean} valid true if the form has passed client-side validation
49273          */
49274         clientvalidation: true,
49275         /**
49276          * @event rendered
49277          * Fires when the form is rendered
49278          * @param {Roo.form.Form} form
49279          */
49280         rendered : true
49281     });
49282     
49283     if (this.progressUrl) {
49284             // push a hidden field onto the list of fields..
49285             this.addxtype( {
49286                     xns: Roo.form, 
49287                     xtype : 'Hidden', 
49288                     name : 'UPLOAD_IDENTIFIER' 
49289             });
49290         }
49291         
49292     
49293     Roo.each(xitems, this.addxtype, this);
49294     
49295 };
49296
49297 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49298      /**
49299      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49300      */
49301     
49302     /**
49303      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49304      */
49305     /**
49306      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49307      */
49308     /**
49309      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49310      */
49311     buttonAlign:'center',
49312
49313     /**
49314      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49315      */
49316     minButtonWidth:75,
49317
49318     /**
49319      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49320      * This property cascades to child containers if not set.
49321      */
49322     labelAlign:'left',
49323
49324     /**
49325      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49326      * fires a looping event with that state. This is required to bind buttons to the valid
49327      * state using the config value formBind:true on the button.
49328      */
49329     monitorValid : false,
49330
49331     /**
49332      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49333      */
49334     monitorPoll : 200,
49335     
49336     /**
49337      * @cfg {String} progressUrl - Url to return progress data 
49338      */
49339     
49340     progressUrl : false,
49341     /**
49342      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49343      * sending a formdata with extra parameters - eg uploaded elements.
49344      */
49345     
49346     formData : false,
49347     
49348     /**
49349      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49350      * fields are added and the column is closed. If no fields are passed the column remains open
49351      * until end() is called.
49352      * @param {Object} config The config to pass to the column
49353      * @param {Field} field1 (optional)
49354      * @param {Field} field2 (optional)
49355      * @param {Field} etc (optional)
49356      * @return Column The column container object
49357      */
49358     column : function(c){
49359         var col = new Roo.form.Column(c);
49360         this.start(col);
49361         if(arguments.length > 1){ // duplicate code required because of Opera
49362             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49363             this.end();
49364         }
49365         return col;
49366     },
49367
49368     /**
49369      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49370      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49371      * until end() is called.
49372      * @param {Object} config The config to pass to the fieldset
49373      * @param {Field} field1 (optional)
49374      * @param {Field} field2 (optional)
49375      * @param {Field} etc (optional)
49376      * @return FieldSet The fieldset container object
49377      */
49378     fieldset : function(c){
49379         var fs = new Roo.form.FieldSet(c);
49380         this.start(fs);
49381         if(arguments.length > 1){ // duplicate code required because of Opera
49382             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49383             this.end();
49384         }
49385         return fs;
49386     },
49387
49388     /**
49389      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49390      * fields are added and the container is closed. If no fields are passed the container remains open
49391      * until end() is called.
49392      * @param {Object} config The config to pass to the Layout
49393      * @param {Field} field1 (optional)
49394      * @param {Field} field2 (optional)
49395      * @param {Field} etc (optional)
49396      * @return Layout The container object
49397      */
49398     container : function(c){
49399         var l = new Roo.form.Layout(c);
49400         this.start(l);
49401         if(arguments.length > 1){ // duplicate code required because of Opera
49402             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49403             this.end();
49404         }
49405         return l;
49406     },
49407
49408     /**
49409      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49410      * @param {Object} container A Roo.form.Layout or subclass of Layout
49411      * @return {Form} this
49412      */
49413     start : function(c){
49414         // cascade label info
49415         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49416         this.active.stack.push(c);
49417         c.ownerCt = this.active;
49418         this.active = c;
49419         return this;
49420     },
49421
49422     /**
49423      * Closes the current open container
49424      * @return {Form} this
49425      */
49426     end : function(){
49427         if(this.active == this.root){
49428             return this;
49429         }
49430         this.active = this.active.ownerCt;
49431         return this;
49432     },
49433
49434     /**
49435      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49436      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49437      * as the label of the field.
49438      * @param {Field} field1
49439      * @param {Field} field2 (optional)
49440      * @param {Field} etc. (optional)
49441      * @return {Form} this
49442      */
49443     add : function(){
49444         this.active.stack.push.apply(this.active.stack, arguments);
49445         this.allItems.push.apply(this.allItems,arguments);
49446         var r = [];
49447         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49448             if(a[i].isFormField){
49449                 r.push(a[i]);
49450             }
49451         }
49452         if(r.length > 0){
49453             Roo.form.Form.superclass.add.apply(this, r);
49454         }
49455         return this;
49456     },
49457     
49458
49459     
49460     
49461     
49462      /**
49463      * Find any element that has been added to a form, using it's ID or name
49464      * This can include framesets, columns etc. along with regular fields..
49465      * @param {String} id - id or name to find.
49466      
49467      * @return {Element} e - or false if nothing found.
49468      */
49469     findbyId : function(id)
49470     {
49471         var ret = false;
49472         if (!id) {
49473             return ret;
49474         }
49475         Roo.each(this.allItems, function(f){
49476             if (f.id == id || f.name == id ){
49477                 ret = f;
49478                 return false;
49479             }
49480         });
49481         return ret;
49482     },
49483
49484     
49485     
49486     /**
49487      * Render this form into the passed container. This should only be called once!
49488      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49489      * @return {Form} this
49490      */
49491     render : function(ct)
49492     {
49493         
49494         
49495         
49496         ct = Roo.get(ct);
49497         var o = this.autoCreate || {
49498             tag: 'form',
49499             method : this.method || 'POST',
49500             id : this.id || Roo.id()
49501         };
49502         this.initEl(ct.createChild(o));
49503
49504         this.root.render(this.el);
49505         
49506        
49507              
49508         this.items.each(function(f){
49509             f.render('x-form-el-'+f.id);
49510         });
49511
49512         if(this.buttons.length > 0){
49513             // tables are required to maintain order and for correct IE layout
49514             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49515                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49516                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49517             }}, null, true);
49518             var tr = tb.getElementsByTagName('tr')[0];
49519             for(var i = 0, len = this.buttons.length; i < len; i++) {
49520                 var b = this.buttons[i];
49521                 var td = document.createElement('td');
49522                 td.className = 'x-form-btn-td';
49523                 b.render(tr.appendChild(td));
49524             }
49525         }
49526         if(this.monitorValid){ // initialize after render
49527             this.startMonitoring();
49528         }
49529         this.fireEvent('rendered', this);
49530         return this;
49531     },
49532
49533     /**
49534      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49535      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49536      * object or a valid Roo.DomHelper element config
49537      * @param {Function} handler The function called when the button is clicked
49538      * @param {Object} scope (optional) The scope of the handler function
49539      * @return {Roo.Button}
49540      */
49541     addButton : function(config, handler, scope){
49542         var bc = {
49543             handler: handler,
49544             scope: scope,
49545             minWidth: this.minButtonWidth,
49546             hideParent:true
49547         };
49548         if(typeof config == "string"){
49549             bc.text = config;
49550         }else{
49551             Roo.apply(bc, config);
49552         }
49553         var btn = new Roo.Button(null, bc);
49554         this.buttons.push(btn);
49555         return btn;
49556     },
49557
49558      /**
49559      * Adds a series of form elements (using the xtype property as the factory method.
49560      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
49561      * @param {Object} config 
49562      */
49563     
49564     addxtype : function()
49565     {
49566         var ar = Array.prototype.slice.call(arguments, 0);
49567         var ret = false;
49568         for(var i = 0; i < ar.length; i++) {
49569             if (!ar[i]) {
49570                 continue; // skip -- if this happends something invalid got sent, we 
49571                 // should ignore it, as basically that interface element will not show up
49572                 // and that should be pretty obvious!!
49573             }
49574             
49575             if (Roo.form[ar[i].xtype]) {
49576                 ar[i].form = this;
49577                 var fe = Roo.factory(ar[i], Roo.form);
49578                 if (!ret) {
49579                     ret = fe;
49580                 }
49581                 fe.form = this;
49582                 if (fe.store) {
49583                     fe.store.form = this;
49584                 }
49585                 if (fe.isLayout) {  
49586                          
49587                     this.start(fe);
49588                     this.allItems.push(fe);
49589                     if (fe.items && fe.addxtype) {
49590                         fe.addxtype.apply(fe, fe.items);
49591                         delete fe.items;
49592                     }
49593                      this.end();
49594                     continue;
49595                 }
49596                 
49597                 
49598                  
49599                 this.add(fe);
49600               //  console.log('adding ' + ar[i].xtype);
49601             }
49602             if (ar[i].xtype == 'Button') {  
49603                 //console.log('adding button');
49604                 //console.log(ar[i]);
49605                 this.addButton(ar[i]);
49606                 this.allItems.push(fe);
49607                 continue;
49608             }
49609             
49610             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
49611                 alert('end is not supported on xtype any more, use items');
49612             //    this.end();
49613             //    //console.log('adding end');
49614             }
49615             
49616         }
49617         return ret;
49618     },
49619     
49620     /**
49621      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
49622      * option "monitorValid"
49623      */
49624     startMonitoring : function(){
49625         if(!this.bound){
49626             this.bound = true;
49627             Roo.TaskMgr.start({
49628                 run : this.bindHandler,
49629                 interval : this.monitorPoll || 200,
49630                 scope: this
49631             });
49632         }
49633     },
49634
49635     /**
49636      * Stops monitoring of the valid state of this form
49637      */
49638     stopMonitoring : function(){
49639         this.bound = false;
49640     },
49641
49642     // private
49643     bindHandler : function(){
49644         if(!this.bound){
49645             return false; // stops binding
49646         }
49647         var valid = true;
49648         this.items.each(function(f){
49649             if(!f.isValid(true)){
49650                 valid = false;
49651                 return false;
49652             }
49653         });
49654         for(var i = 0, len = this.buttons.length; i < len; i++){
49655             var btn = this.buttons[i];
49656             if(btn.formBind === true && btn.disabled === valid){
49657                 btn.setDisabled(!valid);
49658             }
49659         }
49660         this.fireEvent('clientvalidation', this, valid);
49661     }
49662     
49663     
49664     
49665     
49666     
49667     
49668     
49669     
49670 });
49671
49672
49673 // back compat
49674 Roo.Form = Roo.form.Form;
49675 /*
49676  * Based on:
49677  * Ext JS Library 1.1.1
49678  * Copyright(c) 2006-2007, Ext JS, LLC.
49679  *
49680  * Originally Released Under LGPL - original licence link has changed is not relivant.
49681  *
49682  * Fork - LGPL
49683  * <script type="text/javascript">
49684  */
49685
49686 // as we use this in bootstrap.
49687 Roo.namespace('Roo.form');
49688  /**
49689  * @class Roo.form.Action
49690  * Internal Class used to handle form actions
49691  * @constructor
49692  * @param {Roo.form.BasicForm} el The form element or its id
49693  * @param {Object} config Configuration options
49694  */
49695
49696  
49697  
49698 // define the action interface
49699 Roo.form.Action = function(form, options){
49700     this.form = form;
49701     this.options = options || {};
49702 };
49703 /**
49704  * Client Validation Failed
49705  * @const 
49706  */
49707 Roo.form.Action.CLIENT_INVALID = 'client';
49708 /**
49709  * Server Validation Failed
49710  * @const 
49711  */
49712 Roo.form.Action.SERVER_INVALID = 'server';
49713  /**
49714  * Connect to Server Failed
49715  * @const 
49716  */
49717 Roo.form.Action.CONNECT_FAILURE = 'connect';
49718 /**
49719  * Reading Data from Server Failed
49720  * @const 
49721  */
49722 Roo.form.Action.LOAD_FAILURE = 'load';
49723
49724 Roo.form.Action.prototype = {
49725     type : 'default',
49726     failureType : undefined,
49727     response : undefined,
49728     result : undefined,
49729
49730     // interface method
49731     run : function(options){
49732
49733     },
49734
49735     // interface method
49736     success : function(response){
49737
49738     },
49739
49740     // interface method
49741     handleResponse : function(response){
49742
49743     },
49744
49745     // default connection failure
49746     failure : function(response){
49747         
49748         this.response = response;
49749         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49750         this.form.afterAction(this, false);
49751     },
49752
49753     processResponse : function(response){
49754         this.response = response;
49755         if(!response.responseText){
49756             return true;
49757         }
49758         this.result = this.handleResponse(response);
49759         return this.result;
49760     },
49761
49762     // utility functions used internally
49763     getUrl : function(appendParams){
49764         var url = this.options.url || this.form.url || this.form.el.dom.action;
49765         if(appendParams){
49766             var p = this.getParams();
49767             if(p){
49768                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49769             }
49770         }
49771         return url;
49772     },
49773
49774     getMethod : function(){
49775         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49776     },
49777
49778     getParams : function(){
49779         var bp = this.form.baseParams;
49780         var p = this.options.params;
49781         if(p){
49782             if(typeof p == "object"){
49783                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49784             }else if(typeof p == 'string' && bp){
49785                 p += '&' + Roo.urlEncode(bp);
49786             }
49787         }else if(bp){
49788             p = Roo.urlEncode(bp);
49789         }
49790         return p;
49791     },
49792
49793     createCallback : function(){
49794         return {
49795             success: this.success,
49796             failure: this.failure,
49797             scope: this,
49798             timeout: (this.form.timeout*1000),
49799             upload: this.form.fileUpload ? this.success : undefined
49800         };
49801     }
49802 };
49803
49804 Roo.form.Action.Submit = function(form, options){
49805     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49806 };
49807
49808 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49809     type : 'submit',
49810
49811     haveProgress : false,
49812     uploadComplete : false,
49813     
49814     // uploadProgress indicator.
49815     uploadProgress : function()
49816     {
49817         if (!this.form.progressUrl) {
49818             return;
49819         }
49820         
49821         if (!this.haveProgress) {
49822             Roo.MessageBox.progress("Uploading", "Uploading");
49823         }
49824         if (this.uploadComplete) {
49825            Roo.MessageBox.hide();
49826            return;
49827         }
49828         
49829         this.haveProgress = true;
49830    
49831         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49832         
49833         var c = new Roo.data.Connection();
49834         c.request({
49835             url : this.form.progressUrl,
49836             params: {
49837                 id : uid
49838             },
49839             method: 'GET',
49840             success : function(req){
49841                //console.log(data);
49842                 var rdata = false;
49843                 var edata;
49844                 try  {
49845                    rdata = Roo.decode(req.responseText)
49846                 } catch (e) {
49847                     Roo.log("Invalid data from server..");
49848                     Roo.log(edata);
49849                     return;
49850                 }
49851                 if (!rdata || !rdata.success) {
49852                     Roo.log(rdata);
49853                     Roo.MessageBox.alert(Roo.encode(rdata));
49854                     return;
49855                 }
49856                 var data = rdata.data;
49857                 
49858                 if (this.uploadComplete) {
49859                    Roo.MessageBox.hide();
49860                    return;
49861                 }
49862                    
49863                 if (data){
49864                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49865                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49866                     );
49867                 }
49868                 this.uploadProgress.defer(2000,this);
49869             },
49870        
49871             failure: function(data) {
49872                 Roo.log('progress url failed ');
49873                 Roo.log(data);
49874             },
49875             scope : this
49876         });
49877            
49878     },
49879     
49880     
49881     run : function()
49882     {
49883         // run get Values on the form, so it syncs any secondary forms.
49884         this.form.getValues();
49885         
49886         var o = this.options;
49887         var method = this.getMethod();
49888         var isPost = method == 'POST';
49889         if(o.clientValidation === false || this.form.isValid()){
49890             
49891             if (this.form.progressUrl) {
49892                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49893                     (new Date() * 1) + '' + Math.random());
49894                     
49895             } 
49896             
49897             
49898             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49899                 form:this.form.el.dom,
49900                 url:this.getUrl(!isPost),
49901                 method: method,
49902                 params:isPost ? this.getParams() : null,
49903                 isUpload: this.form.fileUpload,
49904                 formData : this.form.formData
49905             }));
49906             
49907             this.uploadProgress();
49908
49909         }else if (o.clientValidation !== false){ // client validation failed
49910             this.failureType = Roo.form.Action.CLIENT_INVALID;
49911             this.form.afterAction(this, false);
49912         }
49913     },
49914
49915     success : function(response)
49916     {
49917         this.uploadComplete= true;
49918         if (this.haveProgress) {
49919             Roo.MessageBox.hide();
49920         }
49921         
49922         
49923         var result = this.processResponse(response);
49924         if(result === true || result.success){
49925             this.form.afterAction(this, true);
49926             return;
49927         }
49928         if(result.errors){
49929             this.form.markInvalid(result.errors);
49930             this.failureType = Roo.form.Action.SERVER_INVALID;
49931         }
49932         this.form.afterAction(this, false);
49933     },
49934     failure : function(response)
49935     {
49936         this.uploadComplete= true;
49937         if (this.haveProgress) {
49938             Roo.MessageBox.hide();
49939         }
49940         
49941         this.response = response;
49942         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49943         this.form.afterAction(this, false);
49944     },
49945     
49946     handleResponse : function(response){
49947         if(this.form.errorReader){
49948             var rs = this.form.errorReader.read(response);
49949             var errors = [];
49950             if(rs.records){
49951                 for(var i = 0, len = rs.records.length; i < len; i++) {
49952                     var r = rs.records[i];
49953                     errors[i] = r.data;
49954                 }
49955             }
49956             if(errors.length < 1){
49957                 errors = null;
49958             }
49959             return {
49960                 success : rs.success,
49961                 errors : errors
49962             };
49963         }
49964         var ret = false;
49965         try {
49966             ret = Roo.decode(response.responseText);
49967         } catch (e) {
49968             ret = {
49969                 success: false,
49970                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49971                 errors : []
49972             };
49973         }
49974         return ret;
49975         
49976     }
49977 });
49978
49979
49980 Roo.form.Action.Load = function(form, options){
49981     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49982     this.reader = this.form.reader;
49983 };
49984
49985 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49986     type : 'load',
49987
49988     run : function(){
49989         
49990         Roo.Ajax.request(Roo.apply(
49991                 this.createCallback(), {
49992                     method:this.getMethod(),
49993                     url:this.getUrl(false),
49994                     params:this.getParams()
49995         }));
49996     },
49997
49998     success : function(response){
49999         
50000         var result = this.processResponse(response);
50001         if(result === true || !result.success || !result.data){
50002             this.failureType = Roo.form.Action.LOAD_FAILURE;
50003             this.form.afterAction(this, false);
50004             return;
50005         }
50006         this.form.clearInvalid();
50007         this.form.setValues(result.data);
50008         this.form.afterAction(this, true);
50009     },
50010
50011     handleResponse : function(response){
50012         if(this.form.reader){
50013             var rs = this.form.reader.read(response);
50014             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50015             return {
50016                 success : rs.success,
50017                 data : data
50018             };
50019         }
50020         return Roo.decode(response.responseText);
50021     }
50022 });
50023
50024 Roo.form.Action.ACTION_TYPES = {
50025     'load' : Roo.form.Action.Load,
50026     'submit' : Roo.form.Action.Submit
50027 };/*
50028  * Based on:
50029  * Ext JS Library 1.1.1
50030  * Copyright(c) 2006-2007, Ext JS, LLC.
50031  *
50032  * Originally Released Under LGPL - original licence link has changed is not relivant.
50033  *
50034  * Fork - LGPL
50035  * <script type="text/javascript">
50036  */
50037  
50038 /**
50039  * @class Roo.form.Layout
50040  * @extends Roo.Component
50041  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50042  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50043  * @constructor
50044  * @param {Object} config Configuration options
50045  */
50046 Roo.form.Layout = function(config){
50047     var xitems = [];
50048     if (config.items) {
50049         xitems = config.items;
50050         delete config.items;
50051     }
50052     Roo.form.Layout.superclass.constructor.call(this, config);
50053     this.stack = [];
50054     Roo.each(xitems, this.addxtype, this);
50055      
50056 };
50057
50058 Roo.extend(Roo.form.Layout, Roo.Component, {
50059     /**
50060      * @cfg {String/Object} autoCreate
50061      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50062      */
50063     /**
50064      * @cfg {String/Object/Function} style
50065      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50066      * a function which returns such a specification.
50067      */
50068     /**
50069      * @cfg {String} labelAlign
50070      * Valid values are "left," "top" and "right" (defaults to "left")
50071      */
50072     /**
50073      * @cfg {Number} labelWidth
50074      * Fixed width in pixels of all field labels (defaults to undefined)
50075      */
50076     /**
50077      * @cfg {Boolean} clear
50078      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50079      */
50080     clear : true,
50081     /**
50082      * @cfg {String} labelSeparator
50083      * The separator to use after field labels (defaults to ':')
50084      */
50085     labelSeparator : ':',
50086     /**
50087      * @cfg {Boolean} hideLabels
50088      * True to suppress the display of field labels in this layout (defaults to false)
50089      */
50090     hideLabels : false,
50091
50092     // private
50093     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50094     
50095     isLayout : true,
50096     
50097     // private
50098     onRender : function(ct, position){
50099         if(this.el){ // from markup
50100             this.el = Roo.get(this.el);
50101         }else {  // generate
50102             var cfg = this.getAutoCreate();
50103             this.el = ct.createChild(cfg, position);
50104         }
50105         if(this.style){
50106             this.el.applyStyles(this.style);
50107         }
50108         if(this.labelAlign){
50109             this.el.addClass('x-form-label-'+this.labelAlign);
50110         }
50111         if(this.hideLabels){
50112             this.labelStyle = "display:none";
50113             this.elementStyle = "padding-left:0;";
50114         }else{
50115             if(typeof this.labelWidth == 'number'){
50116                 this.labelStyle = "width:"+this.labelWidth+"px;";
50117                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50118             }
50119             if(this.labelAlign == 'top'){
50120                 this.labelStyle = "width:auto;";
50121                 this.elementStyle = "padding-left:0;";
50122             }
50123         }
50124         var stack = this.stack;
50125         var slen = stack.length;
50126         if(slen > 0){
50127             if(!this.fieldTpl){
50128                 var t = new Roo.Template(
50129                     '<div class="x-form-item {5}">',
50130                         '<label for="{0}" style="{2}">{1}{4}</label>',
50131                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50132                         '</div>',
50133                     '</div><div class="x-form-clear-left"></div>'
50134                 );
50135                 t.disableFormats = true;
50136                 t.compile();
50137                 Roo.form.Layout.prototype.fieldTpl = t;
50138             }
50139             for(var i = 0; i < slen; i++) {
50140                 if(stack[i].isFormField){
50141                     this.renderField(stack[i]);
50142                 }else{
50143                     this.renderComponent(stack[i]);
50144                 }
50145             }
50146         }
50147         if(this.clear){
50148             this.el.createChild({cls:'x-form-clear'});
50149         }
50150     },
50151
50152     // private
50153     renderField : function(f){
50154         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50155                f.id, //0
50156                f.fieldLabel, //1
50157                f.labelStyle||this.labelStyle||'', //2
50158                this.elementStyle||'', //3
50159                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50160                f.itemCls||this.itemCls||''  //5
50161        ], true).getPrevSibling());
50162     },
50163
50164     // private
50165     renderComponent : function(c){
50166         c.render(c.isLayout ? this.el : this.el.createChild());    
50167     },
50168     /**
50169      * Adds a object form elements (using the xtype property as the factory method.)
50170      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50171      * @param {Object} config 
50172      */
50173     addxtype : function(o)
50174     {
50175         // create the lement.
50176         o.form = this.form;
50177         var fe = Roo.factory(o, Roo.form);
50178         this.form.allItems.push(fe);
50179         this.stack.push(fe);
50180         
50181         if (fe.isFormField) {
50182             this.form.items.add(fe);
50183         }
50184          
50185         return fe;
50186     }
50187 });
50188
50189 /**
50190  * @class Roo.form.Column
50191  * @extends Roo.form.Layout
50192  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50193  * @constructor
50194  * @param {Object} config Configuration options
50195  */
50196 Roo.form.Column = function(config){
50197     Roo.form.Column.superclass.constructor.call(this, config);
50198 };
50199
50200 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50201     /**
50202      * @cfg {Number/String} width
50203      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50204      */
50205     /**
50206      * @cfg {String/Object} autoCreate
50207      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50208      */
50209
50210     // private
50211     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50212
50213     // private
50214     onRender : function(ct, position){
50215         Roo.form.Column.superclass.onRender.call(this, ct, position);
50216         if(this.width){
50217             this.el.setWidth(this.width);
50218         }
50219     }
50220 });
50221
50222
50223 /**
50224  * @class Roo.form.Row
50225  * @extends Roo.form.Layout
50226  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50227  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50228  * @constructor
50229  * @param {Object} config Configuration options
50230  */
50231
50232  
50233 Roo.form.Row = function(config){
50234     Roo.form.Row.superclass.constructor.call(this, config);
50235 };
50236  
50237 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50238       /**
50239      * @cfg {Number/String} width
50240      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50241      */
50242     /**
50243      * @cfg {Number/String} height
50244      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50245      */
50246     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50247     
50248     padWidth : 20,
50249     // private
50250     onRender : function(ct, position){
50251         //console.log('row render');
50252         if(!this.rowTpl){
50253             var t = new Roo.Template(
50254                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50255                     '<label for="{0}" style="{2}">{1}{4}</label>',
50256                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50257                     '</div>',
50258                 '</div>'
50259             );
50260             t.disableFormats = true;
50261             t.compile();
50262             Roo.form.Layout.prototype.rowTpl = t;
50263         }
50264         this.fieldTpl = this.rowTpl;
50265         
50266         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50267         var labelWidth = 100;
50268         
50269         if ((this.labelAlign != 'top')) {
50270             if (typeof this.labelWidth == 'number') {
50271                 labelWidth = this.labelWidth
50272             }
50273             this.padWidth =  20 + labelWidth;
50274             
50275         }
50276         
50277         Roo.form.Column.superclass.onRender.call(this, ct, position);
50278         if(this.width){
50279             this.el.setWidth(this.width);
50280         }
50281         if(this.height){
50282             this.el.setHeight(this.height);
50283         }
50284     },
50285     
50286     // private
50287     renderField : function(f){
50288         f.fieldEl = this.fieldTpl.append(this.el, [
50289                f.id, f.fieldLabel,
50290                f.labelStyle||this.labelStyle||'',
50291                this.elementStyle||'',
50292                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50293                f.itemCls||this.itemCls||'',
50294                f.width ? f.width + this.padWidth : 160 + this.padWidth
50295        ],true);
50296     }
50297 });
50298  
50299
50300 /**
50301  * @class Roo.form.FieldSet
50302  * @extends Roo.form.Layout
50303  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50304  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50305  * @constructor
50306  * @param {Object} config Configuration options
50307  */
50308 Roo.form.FieldSet = function(config){
50309     Roo.form.FieldSet.superclass.constructor.call(this, config);
50310 };
50311
50312 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50313     /**
50314      * @cfg {String} legend
50315      * The text to display as the legend for the FieldSet (defaults to '')
50316      */
50317     /**
50318      * @cfg {String/Object} autoCreate
50319      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50320      */
50321
50322     // private
50323     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50324
50325     // private
50326     onRender : function(ct, position){
50327         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50328         if(this.legend){
50329             this.setLegend(this.legend);
50330         }
50331     },
50332
50333     // private
50334     setLegend : function(text){
50335         if(this.rendered){
50336             this.el.child('legend').update(text);
50337         }
50338     }
50339 });/*
50340  * Based on:
50341  * Ext JS Library 1.1.1
50342  * Copyright(c) 2006-2007, Ext JS, LLC.
50343  *
50344  * Originally Released Under LGPL - original licence link has changed is not relivant.
50345  *
50346  * Fork - LGPL
50347  * <script type="text/javascript">
50348  */
50349 /**
50350  * @class Roo.form.VTypes
50351  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50352  * @static
50353  */
50354 Roo.form.VTypes = function(){
50355     // closure these in so they are only created once.
50356     var alpha = /^[a-zA-Z_]+$/;
50357     var alphanum = /^[a-zA-Z0-9_]+$/;
50358     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50359     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50360
50361     // All these messages and functions are configurable
50362     return {
50363         /**
50364          * The function used to validate email addresses
50365          * @param {String} value The email address
50366          */
50367         'email' : function(v){
50368             return email.test(v);
50369         },
50370         /**
50371          * The error text to display when the email validation function returns false
50372          * @type String
50373          */
50374         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50375         /**
50376          * The keystroke filter mask to be applied on email input
50377          * @type RegExp
50378          */
50379         'emailMask' : /[a-z0-9_\.\-@]/i,
50380
50381         /**
50382          * The function used to validate URLs
50383          * @param {String} value The URL
50384          */
50385         'url' : function(v){
50386             return url.test(v);
50387         },
50388         /**
50389          * The error text to display when the url validation function returns false
50390          * @type String
50391          */
50392         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50393         
50394         /**
50395          * The function used to validate alpha values
50396          * @param {String} value The value
50397          */
50398         'alpha' : function(v){
50399             return alpha.test(v);
50400         },
50401         /**
50402          * The error text to display when the alpha validation function returns false
50403          * @type String
50404          */
50405         'alphaText' : 'This field should only contain letters and _',
50406         /**
50407          * The keystroke filter mask to be applied on alpha input
50408          * @type RegExp
50409          */
50410         'alphaMask' : /[a-z_]/i,
50411
50412         /**
50413          * The function used to validate alphanumeric values
50414          * @param {String} value The value
50415          */
50416         'alphanum' : function(v){
50417             return alphanum.test(v);
50418         },
50419         /**
50420          * The error text to display when the alphanumeric validation function returns false
50421          * @type String
50422          */
50423         'alphanumText' : 'This field should only contain letters, numbers and _',
50424         /**
50425          * The keystroke filter mask to be applied on alphanumeric input
50426          * @type RegExp
50427          */
50428         'alphanumMask' : /[a-z0-9_]/i
50429     };
50430 }();//<script type="text/javascript">
50431
50432 /**
50433  * @class Roo.form.FCKeditor
50434  * @extends Roo.form.TextArea
50435  * Wrapper around the FCKEditor http://www.fckeditor.net
50436  * @constructor
50437  * Creates a new FCKeditor
50438  * @param {Object} config Configuration options
50439  */
50440 Roo.form.FCKeditor = function(config){
50441     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50442     this.addEvents({
50443          /**
50444          * @event editorinit
50445          * Fired when the editor is initialized - you can add extra handlers here..
50446          * @param {FCKeditor} this
50447          * @param {Object} the FCK object.
50448          */
50449         editorinit : true
50450     });
50451     
50452     
50453 };
50454 Roo.form.FCKeditor.editors = { };
50455 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50456 {
50457     //defaultAutoCreate : {
50458     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50459     //},
50460     // private
50461     /**
50462      * @cfg {Object} fck options - see fck manual for details.
50463      */
50464     fckconfig : false,
50465     
50466     /**
50467      * @cfg {Object} fck toolbar set (Basic or Default)
50468      */
50469     toolbarSet : 'Basic',
50470     /**
50471      * @cfg {Object} fck BasePath
50472      */ 
50473     basePath : '/fckeditor/',
50474     
50475     
50476     frame : false,
50477     
50478     value : '',
50479     
50480    
50481     onRender : function(ct, position)
50482     {
50483         if(!this.el){
50484             this.defaultAutoCreate = {
50485                 tag: "textarea",
50486                 style:"width:300px;height:60px;",
50487                 autocomplete: "new-password"
50488             };
50489         }
50490         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50491         /*
50492         if(this.grow){
50493             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50494             if(this.preventScrollbars){
50495                 this.el.setStyle("overflow", "hidden");
50496             }
50497             this.el.setHeight(this.growMin);
50498         }
50499         */
50500         //console.log('onrender' + this.getId() );
50501         Roo.form.FCKeditor.editors[this.getId()] = this;
50502          
50503
50504         this.replaceTextarea() ;
50505         
50506     },
50507     
50508     getEditor : function() {
50509         return this.fckEditor;
50510     },
50511     /**
50512      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50513      * @param {Mixed} value The value to set
50514      */
50515     
50516     
50517     setValue : function(value)
50518     {
50519         //console.log('setValue: ' + value);
50520         
50521         if(typeof(value) == 'undefined') { // not sure why this is happending...
50522             return;
50523         }
50524         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50525         
50526         //if(!this.el || !this.getEditor()) {
50527         //    this.value = value;
50528             //this.setValue.defer(100,this,[value]);    
50529         //    return;
50530         //} 
50531         
50532         if(!this.getEditor()) {
50533             return;
50534         }
50535         
50536         this.getEditor().SetData(value);
50537         
50538         //
50539
50540     },
50541
50542     /**
50543      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50544      * @return {Mixed} value The field value
50545      */
50546     getValue : function()
50547     {
50548         
50549         if (this.frame && this.frame.dom.style.display == 'none') {
50550             return Roo.form.FCKeditor.superclass.getValue.call(this);
50551         }
50552         
50553         if(!this.el || !this.getEditor()) {
50554            
50555            // this.getValue.defer(100,this); 
50556             return this.value;
50557         }
50558        
50559         
50560         var value=this.getEditor().GetData();
50561         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50562         return Roo.form.FCKeditor.superclass.getValue.call(this);
50563         
50564
50565     },
50566
50567     /**
50568      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
50569      * @return {Mixed} value The field value
50570      */
50571     getRawValue : function()
50572     {
50573         if (this.frame && this.frame.dom.style.display == 'none') {
50574             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50575         }
50576         
50577         if(!this.el || !this.getEditor()) {
50578             //this.getRawValue.defer(100,this); 
50579             return this.value;
50580             return;
50581         }
50582         
50583         
50584         
50585         var value=this.getEditor().GetData();
50586         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
50587         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50588          
50589     },
50590     
50591     setSize : function(w,h) {
50592         
50593         
50594         
50595         //if (this.frame && this.frame.dom.style.display == 'none') {
50596         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50597         //    return;
50598         //}
50599         //if(!this.el || !this.getEditor()) {
50600         //    this.setSize.defer(100,this, [w,h]); 
50601         //    return;
50602         //}
50603         
50604         
50605         
50606         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50607         
50608         this.frame.dom.setAttribute('width', w);
50609         this.frame.dom.setAttribute('height', h);
50610         this.frame.setSize(w,h);
50611         
50612     },
50613     
50614     toggleSourceEdit : function(value) {
50615         
50616       
50617          
50618         this.el.dom.style.display = value ? '' : 'none';
50619         this.frame.dom.style.display = value ?  'none' : '';
50620         
50621     },
50622     
50623     
50624     focus: function(tag)
50625     {
50626         if (this.frame.dom.style.display == 'none') {
50627             return Roo.form.FCKeditor.superclass.focus.call(this);
50628         }
50629         if(!this.el || !this.getEditor()) {
50630             this.focus.defer(100,this, [tag]); 
50631             return;
50632         }
50633         
50634         
50635         
50636         
50637         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
50638         this.getEditor().Focus();
50639         if (tgs.length) {
50640             if (!this.getEditor().Selection.GetSelection()) {
50641                 this.focus.defer(100,this, [tag]); 
50642                 return;
50643             }
50644             
50645             
50646             var r = this.getEditor().EditorDocument.createRange();
50647             r.setStart(tgs[0],0);
50648             r.setEnd(tgs[0],0);
50649             this.getEditor().Selection.GetSelection().removeAllRanges();
50650             this.getEditor().Selection.GetSelection().addRange(r);
50651             this.getEditor().Focus();
50652         }
50653         
50654     },
50655     
50656     
50657     
50658     replaceTextarea : function()
50659     {
50660         if ( document.getElementById( this.getId() + '___Frame' ) ) {
50661             return ;
50662         }
50663         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
50664         //{
50665             // We must check the elements firstly using the Id and then the name.
50666         var oTextarea = document.getElementById( this.getId() );
50667         
50668         var colElementsByName = document.getElementsByName( this.getId() ) ;
50669          
50670         oTextarea.style.display = 'none' ;
50671
50672         if ( oTextarea.tabIndex ) {            
50673             this.TabIndex = oTextarea.tabIndex ;
50674         }
50675         
50676         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
50677         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
50678         this.frame = Roo.get(this.getId() + '___Frame')
50679     },
50680     
50681     _getConfigHtml : function()
50682     {
50683         var sConfig = '' ;
50684
50685         for ( var o in this.fckconfig ) {
50686             sConfig += sConfig.length > 0  ? '&amp;' : '';
50687             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
50688         }
50689
50690         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
50691     },
50692     
50693     
50694     _getIFrameHtml : function()
50695     {
50696         var sFile = 'fckeditor.html' ;
50697         /* no idea what this is about..
50698         try
50699         {
50700             if ( (/fcksource=true/i).test( window.top.location.search ) )
50701                 sFile = 'fckeditor.original.html' ;
50702         }
50703         catch (e) { 
50704         */
50705
50706         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
50707         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
50708         
50709         
50710         var html = '<iframe id="' + this.getId() +
50711             '___Frame" src="' + sLink +
50712             '" width="' + this.width +
50713             '" height="' + this.height + '"' +
50714             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50715             ' frameborder="0" scrolling="no"></iframe>' ;
50716
50717         return html ;
50718     },
50719     
50720     _insertHtmlBefore : function( html, element )
50721     {
50722         if ( element.insertAdjacentHTML )       {
50723             // IE
50724             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50725         } else { // Gecko
50726             var oRange = document.createRange() ;
50727             oRange.setStartBefore( element ) ;
50728             var oFragment = oRange.createContextualFragment( html );
50729             element.parentNode.insertBefore( oFragment, element ) ;
50730         }
50731     }
50732     
50733     
50734   
50735     
50736     
50737     
50738     
50739
50740 });
50741
50742 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50743
50744 function FCKeditor_OnComplete(editorInstance){
50745     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50746     f.fckEditor = editorInstance;
50747     //console.log("loaded");
50748     f.fireEvent('editorinit', f, editorInstance);
50749
50750   
50751
50752  
50753
50754
50755
50756
50757
50758
50759
50760
50761
50762
50763
50764
50765
50766
50767
50768 //<script type="text/javascript">
50769 /**
50770  * @class Roo.form.GridField
50771  * @extends Roo.form.Field
50772  * Embed a grid (or editable grid into a form)
50773  * STATUS ALPHA
50774  * 
50775  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50776  * it needs 
50777  * xgrid.store = Roo.data.Store
50778  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50779  * xgrid.store.reader = Roo.data.JsonReader 
50780  * 
50781  * 
50782  * @constructor
50783  * Creates a new GridField
50784  * @param {Object} config Configuration options
50785  */
50786 Roo.form.GridField = function(config){
50787     Roo.form.GridField.superclass.constructor.call(this, config);
50788      
50789 };
50790
50791 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50792     /**
50793      * @cfg {Number} width  - used to restrict width of grid..
50794      */
50795     width : 100,
50796     /**
50797      * @cfg {Number} height - used to restrict height of grid..
50798      */
50799     height : 50,
50800      /**
50801      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50802          * 
50803          *}
50804      */
50805     xgrid : false, 
50806     /**
50807      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50808      * {tag: "input", type: "checkbox", autocomplete: "off"})
50809      */
50810    // defaultAutoCreate : { tag: 'div' },
50811     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50812     /**
50813      * @cfg {String} addTitle Text to include for adding a title.
50814      */
50815     addTitle : false,
50816     //
50817     onResize : function(){
50818         Roo.form.Field.superclass.onResize.apply(this, arguments);
50819     },
50820
50821     initEvents : function(){
50822         // Roo.form.Checkbox.superclass.initEvents.call(this);
50823         // has no events...
50824        
50825     },
50826
50827
50828     getResizeEl : function(){
50829         return this.wrap;
50830     },
50831
50832     getPositionEl : function(){
50833         return this.wrap;
50834     },
50835
50836     // private
50837     onRender : function(ct, position){
50838         
50839         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50840         var style = this.style;
50841         delete this.style;
50842         
50843         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50844         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50845         this.viewEl = this.wrap.createChild({ tag: 'div' });
50846         if (style) {
50847             this.viewEl.applyStyles(style);
50848         }
50849         if (this.width) {
50850             this.viewEl.setWidth(this.width);
50851         }
50852         if (this.height) {
50853             this.viewEl.setHeight(this.height);
50854         }
50855         //if(this.inputValue !== undefined){
50856         //this.setValue(this.value);
50857         
50858         
50859         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50860         
50861         
50862         this.grid.render();
50863         this.grid.getDataSource().on('remove', this.refreshValue, this);
50864         this.grid.getDataSource().on('update', this.refreshValue, this);
50865         this.grid.on('afteredit', this.refreshValue, this);
50866  
50867     },
50868      
50869     
50870     /**
50871      * Sets the value of the item. 
50872      * @param {String} either an object  or a string..
50873      */
50874     setValue : function(v){
50875         //this.value = v;
50876         v = v || []; // empty set..
50877         // this does not seem smart - it really only affects memoryproxy grids..
50878         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50879             var ds = this.grid.getDataSource();
50880             // assumes a json reader..
50881             var data = {}
50882             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50883             ds.loadData( data);
50884         }
50885         // clear selection so it does not get stale.
50886         if (this.grid.sm) { 
50887             this.grid.sm.clearSelections();
50888         }
50889         
50890         Roo.form.GridField.superclass.setValue.call(this, v);
50891         this.refreshValue();
50892         // should load data in the grid really....
50893     },
50894     
50895     // private
50896     refreshValue: function() {
50897          var val = [];
50898         this.grid.getDataSource().each(function(r) {
50899             val.push(r.data);
50900         });
50901         this.el.dom.value = Roo.encode(val);
50902     }
50903     
50904      
50905     
50906     
50907 });/*
50908  * Based on:
50909  * Ext JS Library 1.1.1
50910  * Copyright(c) 2006-2007, Ext JS, LLC.
50911  *
50912  * Originally Released Under LGPL - original licence link has changed is not relivant.
50913  *
50914  * Fork - LGPL
50915  * <script type="text/javascript">
50916  */
50917 /**
50918  * @class Roo.form.DisplayField
50919  * @extends Roo.form.Field
50920  * A generic Field to display non-editable data.
50921  * @cfg {Boolean} closable (true|false) default false
50922  * @constructor
50923  * Creates a new Display Field item.
50924  * @param {Object} config Configuration options
50925  */
50926 Roo.form.DisplayField = function(config){
50927     Roo.form.DisplayField.superclass.constructor.call(this, config);
50928     
50929     this.addEvents({
50930         /**
50931          * @event close
50932          * Fires after the click the close btn
50933              * @param {Roo.form.DisplayField} this
50934              */
50935         close : true
50936     });
50937 };
50938
50939 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50940     inputType:      'hidden',
50941     allowBlank:     true,
50942     readOnly:         true,
50943     
50944  
50945     /**
50946      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50947      */
50948     focusClass : undefined,
50949     /**
50950      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50951      */
50952     fieldClass: 'x-form-field',
50953     
50954      /**
50955      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50956      */
50957     valueRenderer: undefined,
50958     
50959     width: 100,
50960     /**
50961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50962      * {tag: "input", type: "checkbox", autocomplete: "off"})
50963      */
50964      
50965  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50966  
50967     closable : false,
50968     
50969     onResize : function(){
50970         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50971         
50972     },
50973
50974     initEvents : function(){
50975         // Roo.form.Checkbox.superclass.initEvents.call(this);
50976         // has no events...
50977         
50978         if(this.closable){
50979             this.closeEl.on('click', this.onClose, this);
50980         }
50981        
50982     },
50983
50984
50985     getResizeEl : function(){
50986         return this.wrap;
50987     },
50988
50989     getPositionEl : function(){
50990         return this.wrap;
50991     },
50992
50993     // private
50994     onRender : function(ct, position){
50995         
50996         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50997         //if(this.inputValue !== undefined){
50998         this.wrap = this.el.wrap();
50999         
51000         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51001         
51002         if(this.closable){
51003             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51004         }
51005         
51006         if (this.bodyStyle) {
51007             this.viewEl.applyStyles(this.bodyStyle);
51008         }
51009         //this.viewEl.setStyle('padding', '2px');
51010         
51011         this.setValue(this.value);
51012         
51013     },
51014 /*
51015     // private
51016     initValue : Roo.emptyFn,
51017
51018   */
51019
51020         // private
51021     onClick : function(){
51022         
51023     },
51024
51025     /**
51026      * Sets the checked state of the checkbox.
51027      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51028      */
51029     setValue : function(v){
51030         this.value = v;
51031         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51032         // this might be called before we have a dom element..
51033         if (!this.viewEl) {
51034             return;
51035         }
51036         this.viewEl.dom.innerHTML = html;
51037         Roo.form.DisplayField.superclass.setValue.call(this, v);
51038
51039     },
51040     
51041     onClose : function(e)
51042     {
51043         e.preventDefault();
51044         
51045         this.fireEvent('close', this);
51046     }
51047 });/*
51048  * 
51049  * Licence- LGPL
51050  * 
51051  */
51052
51053 /**
51054  * @class Roo.form.DayPicker
51055  * @extends Roo.form.Field
51056  * A Day picker show [M] [T] [W] ....
51057  * @constructor
51058  * Creates a new Day Picker
51059  * @param {Object} config Configuration options
51060  */
51061 Roo.form.DayPicker= function(config){
51062     Roo.form.DayPicker.superclass.constructor.call(this, config);
51063      
51064 };
51065
51066 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51067     /**
51068      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51069      */
51070     focusClass : undefined,
51071     /**
51072      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51073      */
51074     fieldClass: "x-form-field",
51075    
51076     /**
51077      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51078      * {tag: "input", type: "checkbox", autocomplete: "off"})
51079      */
51080     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51081     
51082    
51083     actionMode : 'viewEl', 
51084     //
51085     // private
51086  
51087     inputType : 'hidden',
51088     
51089      
51090     inputElement: false, // real input element?
51091     basedOn: false, // ????
51092     
51093     isFormField: true, // not sure where this is needed!!!!
51094
51095     onResize : function(){
51096         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51097         if(!this.boxLabel){
51098             this.el.alignTo(this.wrap, 'c-c');
51099         }
51100     },
51101
51102     initEvents : function(){
51103         Roo.form.Checkbox.superclass.initEvents.call(this);
51104         this.el.on("click", this.onClick,  this);
51105         this.el.on("change", this.onClick,  this);
51106     },
51107
51108
51109     getResizeEl : function(){
51110         return this.wrap;
51111     },
51112
51113     getPositionEl : function(){
51114         return this.wrap;
51115     },
51116
51117     
51118     // private
51119     onRender : function(ct, position){
51120         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51121        
51122         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51123         
51124         var r1 = '<table><tr>';
51125         var r2 = '<tr class="x-form-daypick-icons">';
51126         for (var i=0; i < 7; i++) {
51127             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51128             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51129         }
51130         
51131         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51132         viewEl.select('img').on('click', this.onClick, this);
51133         this.viewEl = viewEl;   
51134         
51135         
51136         // this will not work on Chrome!!!
51137         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51138         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51139         
51140         
51141           
51142
51143     },
51144
51145     // private
51146     initValue : Roo.emptyFn,
51147
51148     /**
51149      * Returns the checked state of the checkbox.
51150      * @return {Boolean} True if checked, else false
51151      */
51152     getValue : function(){
51153         return this.el.dom.value;
51154         
51155     },
51156
51157         // private
51158     onClick : function(e){ 
51159         //this.setChecked(!this.checked);
51160         Roo.get(e.target).toggleClass('x-menu-item-checked');
51161         this.refreshValue();
51162         //if(this.el.dom.checked != this.checked){
51163         //    this.setValue(this.el.dom.checked);
51164        // }
51165     },
51166     
51167     // private
51168     refreshValue : function()
51169     {
51170         var val = '';
51171         this.viewEl.select('img',true).each(function(e,i,n)  {
51172             val += e.is(".x-menu-item-checked") ? String(n) : '';
51173         });
51174         this.setValue(val, true);
51175     },
51176
51177     /**
51178      * Sets the checked state of the checkbox.
51179      * On is always based on a string comparison between inputValue and the param.
51180      * @param {Boolean/String} value - the value to set 
51181      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51182      */
51183     setValue : function(v,suppressEvent){
51184         if (!this.el.dom) {
51185             return;
51186         }
51187         var old = this.el.dom.value ;
51188         this.el.dom.value = v;
51189         if (suppressEvent) {
51190             return ;
51191         }
51192          
51193         // update display..
51194         this.viewEl.select('img',true).each(function(e,i,n)  {
51195             
51196             var on = e.is(".x-menu-item-checked");
51197             var newv = v.indexOf(String(n)) > -1;
51198             if (on != newv) {
51199                 e.toggleClass('x-menu-item-checked');
51200             }
51201             
51202         });
51203         
51204         
51205         this.fireEvent('change', this, v, old);
51206         
51207         
51208     },
51209    
51210     // handle setting of hidden value by some other method!!?!?
51211     setFromHidden: function()
51212     {
51213         if(!this.el){
51214             return;
51215         }
51216         //console.log("SET FROM HIDDEN");
51217         //alert('setFrom hidden');
51218         this.setValue(this.el.dom.value);
51219     },
51220     
51221     onDestroy : function()
51222     {
51223         if(this.viewEl){
51224             Roo.get(this.viewEl).remove();
51225         }
51226          
51227         Roo.form.DayPicker.superclass.onDestroy.call(this);
51228     }
51229
51230 });/*
51231  * RooJS Library 1.1.1
51232  * Copyright(c) 2008-2011  Alan Knowles
51233  *
51234  * License - LGPL
51235  */
51236  
51237
51238 /**
51239  * @class Roo.form.ComboCheck
51240  * @extends Roo.form.ComboBox
51241  * A combobox for multiple select items.
51242  *
51243  * FIXME - could do with a reset button..
51244  * 
51245  * @constructor
51246  * Create a new ComboCheck
51247  * @param {Object} config Configuration options
51248  */
51249 Roo.form.ComboCheck = function(config){
51250     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51251     // should verify some data...
51252     // like
51253     // hiddenName = required..
51254     // displayField = required
51255     // valudField == required
51256     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51257     var _t = this;
51258     Roo.each(req, function(e) {
51259         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51260             throw "Roo.form.ComboCheck : missing value for: " + e;
51261         }
51262     });
51263     
51264     
51265 };
51266
51267 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51268      
51269      
51270     editable : false,
51271      
51272     selectedClass: 'x-menu-item-checked', 
51273     
51274     // private
51275     onRender : function(ct, position){
51276         var _t = this;
51277         
51278         
51279         
51280         if(!this.tpl){
51281             var cls = 'x-combo-list';
51282
51283             
51284             this.tpl =  new Roo.Template({
51285                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51286                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51287                    '<span>{' + this.displayField + '}</span>' +
51288                     '</div>' 
51289                 
51290             });
51291         }
51292  
51293         
51294         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51295         this.view.singleSelect = false;
51296         this.view.multiSelect = true;
51297         this.view.toggleSelect = true;
51298         this.pageTb.add(new Roo.Toolbar.Fill(), {
51299             
51300             text: 'Done',
51301             handler: function()
51302             {
51303                 _t.collapse();
51304             }
51305         });
51306     },
51307     
51308     onViewOver : function(e, t){
51309         // do nothing...
51310         return;
51311         
51312     },
51313     
51314     onViewClick : function(doFocus,index){
51315         return;
51316         
51317     },
51318     select: function () {
51319         //Roo.log("SELECT CALLED");
51320     },
51321      
51322     selectByValue : function(xv, scrollIntoView){
51323         var ar = this.getValueArray();
51324         var sels = [];
51325         
51326         Roo.each(ar, function(v) {
51327             if(v === undefined || v === null){
51328                 return;
51329             }
51330             var r = this.findRecord(this.valueField, v);
51331             if(r){
51332                 sels.push(this.store.indexOf(r))
51333                 
51334             }
51335         },this);
51336         this.view.select(sels);
51337         return false;
51338     },
51339     
51340     
51341     
51342     onSelect : function(record, index){
51343        // Roo.log("onselect Called");
51344        // this is only called by the clear button now..
51345         this.view.clearSelections();
51346         this.setValue('[]');
51347         if (this.value != this.valueBefore) {
51348             this.fireEvent('change', this, this.value, this.valueBefore);
51349             this.valueBefore = this.value;
51350         }
51351     },
51352     getValueArray : function()
51353     {
51354         var ar = [] ;
51355         
51356         try {
51357             //Roo.log(this.value);
51358             if (typeof(this.value) == 'undefined') {
51359                 return [];
51360             }
51361             var ar = Roo.decode(this.value);
51362             return  ar instanceof Array ? ar : []; //?? valid?
51363             
51364         } catch(e) {
51365             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51366             return [];
51367         }
51368          
51369     },
51370     expand : function ()
51371     {
51372         
51373         Roo.form.ComboCheck.superclass.expand.call(this);
51374         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51375         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51376         
51377
51378     },
51379     
51380     collapse : function(){
51381         Roo.form.ComboCheck.superclass.collapse.call(this);
51382         var sl = this.view.getSelectedIndexes();
51383         var st = this.store;
51384         var nv = [];
51385         var tv = [];
51386         var r;
51387         Roo.each(sl, function(i) {
51388             r = st.getAt(i);
51389             nv.push(r.get(this.valueField));
51390         },this);
51391         this.setValue(Roo.encode(nv));
51392         if (this.value != this.valueBefore) {
51393
51394             this.fireEvent('change', this, this.value, this.valueBefore);
51395             this.valueBefore = this.value;
51396         }
51397         
51398     },
51399     
51400     setValue : function(v){
51401         // Roo.log(v);
51402         this.value = v;
51403         
51404         var vals = this.getValueArray();
51405         var tv = [];
51406         Roo.each(vals, function(k) {
51407             var r = this.findRecord(this.valueField, k);
51408             if(r){
51409                 tv.push(r.data[this.displayField]);
51410             }else if(this.valueNotFoundText !== undefined){
51411                 tv.push( this.valueNotFoundText );
51412             }
51413         },this);
51414        // Roo.log(tv);
51415         
51416         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51417         this.hiddenField.value = v;
51418         this.value = v;
51419     }
51420     
51421 });/*
51422  * Based on:
51423  * Ext JS Library 1.1.1
51424  * Copyright(c) 2006-2007, Ext JS, LLC.
51425  *
51426  * Originally Released Under LGPL - original licence link has changed is not relivant.
51427  *
51428  * Fork - LGPL
51429  * <script type="text/javascript">
51430  */
51431  
51432 /**
51433  * @class Roo.form.Signature
51434  * @extends Roo.form.Field
51435  * Signature field.  
51436  * @constructor
51437  * 
51438  * @param {Object} config Configuration options
51439  */
51440
51441 Roo.form.Signature = function(config){
51442     Roo.form.Signature.superclass.constructor.call(this, config);
51443     
51444     this.addEvents({// not in used??
51445          /**
51446          * @event confirm
51447          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51448              * @param {Roo.form.Signature} combo This combo box
51449              */
51450         'confirm' : true,
51451         /**
51452          * @event reset
51453          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51454              * @param {Roo.form.ComboBox} combo This combo box
51455              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51456              */
51457         'reset' : true
51458     });
51459 };
51460
51461 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51462     /**
51463      * @cfg {Object} labels Label to use when rendering a form.
51464      * defaults to 
51465      * labels : { 
51466      *      clear : "Clear",
51467      *      confirm : "Confirm"
51468      *  }
51469      */
51470     labels : { 
51471         clear : "Clear",
51472         confirm : "Confirm"
51473     },
51474     /**
51475      * @cfg {Number} width The signature panel width (defaults to 300)
51476      */
51477     width: 300,
51478     /**
51479      * @cfg {Number} height The signature panel height (defaults to 100)
51480      */
51481     height : 100,
51482     /**
51483      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51484      */
51485     allowBlank : false,
51486     
51487     //private
51488     // {Object} signPanel The signature SVG panel element (defaults to {})
51489     signPanel : {},
51490     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51491     isMouseDown : false,
51492     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51493     isConfirmed : false,
51494     // {String} signatureTmp SVG mapping string (defaults to empty string)
51495     signatureTmp : '',
51496     
51497     
51498     defaultAutoCreate : { // modified by initCompnoent..
51499         tag: "input",
51500         type:"hidden"
51501     },
51502
51503     // private
51504     onRender : function(ct, position){
51505         
51506         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51507         
51508         this.wrap = this.el.wrap({
51509             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51510         });
51511         
51512         this.createToolbar(this);
51513         this.signPanel = this.wrap.createChild({
51514                 tag: 'div',
51515                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51516             }, this.el
51517         );
51518             
51519         this.svgID = Roo.id();
51520         this.svgEl = this.signPanel.createChild({
51521               xmlns : 'http://www.w3.org/2000/svg',
51522               tag : 'svg',
51523               id : this.svgID + "-svg",
51524               width: this.width,
51525               height: this.height,
51526               viewBox: '0 0 '+this.width+' '+this.height,
51527               cn : [
51528                 {
51529                     tag: "rect",
51530                     id: this.svgID + "-svg-r",
51531                     width: this.width,
51532                     height: this.height,
51533                     fill: "#ffa"
51534                 },
51535                 {
51536                     tag: "line",
51537                     id: this.svgID + "-svg-l",
51538                     x1: "0", // start
51539                     y1: (this.height*0.8), // start set the line in 80% of height
51540                     x2: this.width, // end
51541                     y2: (this.height*0.8), // end set the line in 80% of height
51542                     'stroke': "#666",
51543                     'stroke-width': "1",
51544                     'stroke-dasharray': "3",
51545                     'shape-rendering': "crispEdges",
51546                     'pointer-events': "none"
51547                 },
51548                 {
51549                     tag: "path",
51550                     id: this.svgID + "-svg-p",
51551                     'stroke': "navy",
51552                     'stroke-width': "3",
51553                     'fill': "none",
51554                     'pointer-events': 'none'
51555                 }
51556               ]
51557         });
51558         this.createSVG();
51559         this.svgBox = this.svgEl.dom.getScreenCTM();
51560     },
51561     createSVG : function(){ 
51562         var svg = this.signPanel;
51563         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
51564         var t = this;
51565
51566         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
51567         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
51568         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
51569         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
51570         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
51571         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
51572         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
51573         
51574     },
51575     isTouchEvent : function(e){
51576         return e.type.match(/^touch/);
51577     },
51578     getCoords : function (e) {
51579         var pt    = this.svgEl.dom.createSVGPoint();
51580         pt.x = e.clientX; 
51581         pt.y = e.clientY;
51582         if (this.isTouchEvent(e)) {
51583             pt.x =  e.targetTouches[0].clientX;
51584             pt.y = e.targetTouches[0].clientY;
51585         }
51586         var a = this.svgEl.dom.getScreenCTM();
51587         var b = a.inverse();
51588         var mx = pt.matrixTransform(b);
51589         return mx.x + ',' + mx.y;
51590     },
51591     //mouse event headler 
51592     down : function (e) {
51593         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
51594         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
51595         
51596         this.isMouseDown = true;
51597         
51598         e.preventDefault();
51599     },
51600     move : function (e) {
51601         if (this.isMouseDown) {
51602             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
51603             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
51604         }
51605         
51606         e.preventDefault();
51607     },
51608     up : function (e) {
51609         this.isMouseDown = false;
51610         var sp = this.signatureTmp.split(' ');
51611         
51612         if(sp.length > 1){
51613             if(!sp[sp.length-2].match(/^L/)){
51614                 sp.pop();
51615                 sp.pop();
51616                 sp.push("");
51617                 this.signatureTmp = sp.join(" ");
51618             }
51619         }
51620         if(this.getValue() != this.signatureTmp){
51621             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51622             this.isConfirmed = false;
51623         }
51624         e.preventDefault();
51625     },
51626     
51627     /**
51628      * Protected method that will not generally be called directly. It
51629      * is called when the editor creates its toolbar. Override this method if you need to
51630      * add custom toolbar buttons.
51631      * @param {HtmlEditor} editor
51632      */
51633     createToolbar : function(editor){
51634          function btn(id, toggle, handler){
51635             var xid = fid + '-'+ id ;
51636             return {
51637                 id : xid,
51638                 cmd : id,
51639                 cls : 'x-btn-icon x-edit-'+id,
51640                 enableToggle:toggle !== false,
51641                 scope: editor, // was editor...
51642                 handler:handler||editor.relayBtnCmd,
51643                 clickEvent:'mousedown',
51644                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51645                 tabIndex:-1
51646             };
51647         }
51648         
51649         
51650         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51651         this.tb = tb;
51652         this.tb.add(
51653            {
51654                 cls : ' x-signature-btn x-signature-'+id,
51655                 scope: editor, // was editor...
51656                 handler: this.reset,
51657                 clickEvent:'mousedown',
51658                 text: this.labels.clear
51659             },
51660             {
51661                  xtype : 'Fill',
51662                  xns: Roo.Toolbar
51663             }, 
51664             {
51665                 cls : '  x-signature-btn x-signature-'+id,
51666                 scope: editor, // was editor...
51667                 handler: this.confirmHandler,
51668                 clickEvent:'mousedown',
51669                 text: this.labels.confirm
51670             }
51671         );
51672     
51673     },
51674     //public
51675     /**
51676      * when user is clicked confirm then show this image.....
51677      * 
51678      * @return {String} Image Data URI
51679      */
51680     getImageDataURI : function(){
51681         var svg = this.svgEl.dom.parentNode.innerHTML;
51682         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
51683         return src; 
51684     },
51685     /**
51686      * 
51687      * @return {Boolean} this.isConfirmed
51688      */
51689     getConfirmed : function(){
51690         return this.isConfirmed;
51691     },
51692     /**
51693      * 
51694      * @return {Number} this.width
51695      */
51696     getWidth : function(){
51697         return this.width;
51698     },
51699     /**
51700      * 
51701      * @return {Number} this.height
51702      */
51703     getHeight : function(){
51704         return this.height;
51705     },
51706     // private
51707     getSignature : function(){
51708         return this.signatureTmp;
51709     },
51710     // private
51711     reset : function(){
51712         this.signatureTmp = '';
51713         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51714         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51715         this.isConfirmed = false;
51716         Roo.form.Signature.superclass.reset.call(this);
51717     },
51718     setSignature : function(s){
51719         this.signatureTmp = s;
51720         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51721         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51722         this.setValue(s);
51723         this.isConfirmed = false;
51724         Roo.form.Signature.superclass.reset.call(this);
51725     }, 
51726     test : function(){
51727 //        Roo.log(this.signPanel.dom.contentWindow.up())
51728     },
51729     //private
51730     setConfirmed : function(){
51731         
51732         
51733         
51734 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51735     },
51736     // private
51737     confirmHandler : function(){
51738         if(!this.getSignature()){
51739             return;
51740         }
51741         
51742         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51743         this.setValue(this.getSignature());
51744         this.isConfirmed = true;
51745         
51746         this.fireEvent('confirm', this);
51747     },
51748     // private
51749     // Subclasses should provide the validation implementation by overriding this
51750     validateValue : function(value){
51751         if(this.allowBlank){
51752             return true;
51753         }
51754         
51755         if(this.isConfirmed){
51756             return true;
51757         }
51758         return false;
51759     }
51760 });/*
51761  * Based on:
51762  * Ext JS Library 1.1.1
51763  * Copyright(c) 2006-2007, Ext JS, LLC.
51764  *
51765  * Originally Released Under LGPL - original licence link has changed is not relivant.
51766  *
51767  * Fork - LGPL
51768  * <script type="text/javascript">
51769  */
51770  
51771
51772 /**
51773  * @class Roo.form.ComboBox
51774  * @extends Roo.form.TriggerField
51775  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51776  * @constructor
51777  * Create a new ComboBox.
51778  * @param {Object} config Configuration options
51779  */
51780 Roo.form.Select = function(config){
51781     Roo.form.Select.superclass.constructor.call(this, config);
51782      
51783 };
51784
51785 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51786     /**
51787      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51788      */
51789     /**
51790      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51791      * rendering into an Roo.Editor, defaults to false)
51792      */
51793     /**
51794      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51795      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51796      */
51797     /**
51798      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51799      */
51800     /**
51801      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51802      * the dropdown list (defaults to undefined, with no header element)
51803      */
51804
51805      /**
51806      * @cfg {String/Roo.Template} tpl The template to use to render the output
51807      */
51808      
51809     // private
51810     defaultAutoCreate : {tag: "select"  },
51811     /**
51812      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51813      */
51814     listWidth: undefined,
51815     /**
51816      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51817      * mode = 'remote' or 'text' if mode = 'local')
51818      */
51819     displayField: undefined,
51820     /**
51821      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51822      * mode = 'remote' or 'value' if mode = 'local'). 
51823      * Note: use of a valueField requires the user make a selection
51824      * in order for a value to be mapped.
51825      */
51826     valueField: undefined,
51827     
51828     
51829     /**
51830      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51831      * field's data value (defaults to the underlying DOM element's name)
51832      */
51833     hiddenName: undefined,
51834     /**
51835      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51836      */
51837     listClass: '',
51838     /**
51839      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51840      */
51841     selectedClass: 'x-combo-selected',
51842     /**
51843      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51844      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51845      * which displays a downward arrow icon).
51846      */
51847     triggerClass : 'x-form-arrow-trigger',
51848     /**
51849      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51850      */
51851     shadow:'sides',
51852     /**
51853      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51854      * anchor positions (defaults to 'tl-bl')
51855      */
51856     listAlign: 'tl-bl?',
51857     /**
51858      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51859      */
51860     maxHeight: 300,
51861     /**
51862      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51863      * query specified by the allQuery config option (defaults to 'query')
51864      */
51865     triggerAction: 'query',
51866     /**
51867      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51868      * (defaults to 4, does not apply if editable = false)
51869      */
51870     minChars : 4,
51871     /**
51872      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51873      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51874      */
51875     typeAhead: false,
51876     /**
51877      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51878      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51879      */
51880     queryDelay: 500,
51881     /**
51882      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51883      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51884      */
51885     pageSize: 0,
51886     /**
51887      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51888      * when editable = true (defaults to false)
51889      */
51890     selectOnFocus:false,
51891     /**
51892      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51893      */
51894     queryParam: 'query',
51895     /**
51896      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51897      * when mode = 'remote' (defaults to 'Loading...')
51898      */
51899     loadingText: 'Loading...',
51900     /**
51901      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51902      */
51903     resizable: false,
51904     /**
51905      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51906      */
51907     handleHeight : 8,
51908     /**
51909      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51910      * traditional select (defaults to true)
51911      */
51912     editable: true,
51913     /**
51914      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51915      */
51916     allQuery: '',
51917     /**
51918      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51919      */
51920     mode: 'remote',
51921     /**
51922      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51923      * listWidth has a higher value)
51924      */
51925     minListWidth : 70,
51926     /**
51927      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51928      * allow the user to set arbitrary text into the field (defaults to false)
51929      */
51930     forceSelection:false,
51931     /**
51932      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51933      * if typeAhead = true (defaults to 250)
51934      */
51935     typeAheadDelay : 250,
51936     /**
51937      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51938      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51939      */
51940     valueNotFoundText : undefined,
51941     
51942     /**
51943      * @cfg {String} defaultValue The value displayed after loading the store.
51944      */
51945     defaultValue: '',
51946     
51947     /**
51948      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51949      */
51950     blockFocus : false,
51951     
51952     /**
51953      * @cfg {Boolean} disableClear Disable showing of clear button.
51954      */
51955     disableClear : false,
51956     /**
51957      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51958      */
51959     alwaysQuery : false,
51960     
51961     //private
51962     addicon : false,
51963     editicon: false,
51964     
51965     // element that contains real text value.. (when hidden is used..)
51966      
51967     // private
51968     onRender : function(ct, position){
51969         Roo.form.Field.prototype.onRender.call(this, ct, position);
51970         
51971         if(this.store){
51972             this.store.on('beforeload', this.onBeforeLoad, this);
51973             this.store.on('load', this.onLoad, this);
51974             this.store.on('loadexception', this.onLoadException, this);
51975             this.store.load({});
51976         }
51977         
51978         
51979         
51980     },
51981
51982     // private
51983     initEvents : function(){
51984         //Roo.form.ComboBox.superclass.initEvents.call(this);
51985  
51986     },
51987
51988     onDestroy : function(){
51989        
51990         if(this.store){
51991             this.store.un('beforeload', this.onBeforeLoad, this);
51992             this.store.un('load', this.onLoad, this);
51993             this.store.un('loadexception', this.onLoadException, this);
51994         }
51995         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51996     },
51997
51998     // private
51999     fireKey : function(e){
52000         if(e.isNavKeyPress() && !this.list.isVisible()){
52001             this.fireEvent("specialkey", this, e);
52002         }
52003     },
52004
52005     // private
52006     onResize: function(w, h){
52007         
52008         return; 
52009     
52010         
52011     },
52012
52013     /**
52014      * Allow or prevent the user from directly editing the field text.  If false is passed,
52015      * the user will only be able to select from the items defined in the dropdown list.  This method
52016      * is the runtime equivalent of setting the 'editable' config option at config time.
52017      * @param {Boolean} value True to allow the user to directly edit the field text
52018      */
52019     setEditable : function(value){
52020          
52021     },
52022
52023     // private
52024     onBeforeLoad : function(){
52025         
52026         Roo.log("Select before load");
52027         return;
52028     
52029         this.innerList.update(this.loadingText ?
52030                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52031         //this.restrictHeight();
52032         this.selectedIndex = -1;
52033     },
52034
52035     // private
52036     onLoad : function(){
52037
52038     
52039         var dom = this.el.dom;
52040         dom.innerHTML = '';
52041          var od = dom.ownerDocument;
52042          
52043         if (this.emptyText) {
52044             var op = od.createElement('option');
52045             op.setAttribute('value', '');
52046             op.innerHTML = String.format('{0}', this.emptyText);
52047             dom.appendChild(op);
52048         }
52049         if(this.store.getCount() > 0){
52050            
52051             var vf = this.valueField;
52052             var df = this.displayField;
52053             this.store.data.each(function(r) {
52054                 // which colmsn to use... testing - cdoe / title..
52055                 var op = od.createElement('option');
52056                 op.setAttribute('value', r.data[vf]);
52057                 op.innerHTML = String.format('{0}', r.data[df]);
52058                 dom.appendChild(op);
52059             });
52060             if (typeof(this.defaultValue != 'undefined')) {
52061                 this.setValue(this.defaultValue);
52062             }
52063             
52064              
52065         }else{
52066             //this.onEmptyResults();
52067         }
52068         //this.el.focus();
52069     },
52070     // private
52071     onLoadException : function()
52072     {
52073         dom.innerHTML = '';
52074             
52075         Roo.log("Select on load exception");
52076         return;
52077     
52078         this.collapse();
52079         Roo.log(this.store.reader.jsonData);
52080         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52081             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52082         }
52083         
52084         
52085     },
52086     // private
52087     onTypeAhead : function(){
52088          
52089     },
52090
52091     // private
52092     onSelect : function(record, index){
52093         Roo.log('on select?');
52094         return;
52095         if(this.fireEvent('beforeselect', this, record, index) !== false){
52096             this.setFromData(index > -1 ? record.data : false);
52097             this.collapse();
52098             this.fireEvent('select', this, record, index);
52099         }
52100     },
52101
52102     /**
52103      * Returns the currently selected field value or empty string if no value is set.
52104      * @return {String} value The selected value
52105      */
52106     getValue : function(){
52107         var dom = this.el.dom;
52108         this.value = dom.options[dom.selectedIndex].value;
52109         return this.value;
52110         
52111     },
52112
52113     /**
52114      * Clears any text/value currently set in the field
52115      */
52116     clearValue : function(){
52117         this.value = '';
52118         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52119         
52120     },
52121
52122     /**
52123      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52124      * will be displayed in the field.  If the value does not match the data value of an existing item,
52125      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52126      * Otherwise the field will be blank (although the value will still be set).
52127      * @param {String} value The value to match
52128      */
52129     setValue : function(v){
52130         var d = this.el.dom;
52131         for (var i =0; i < d.options.length;i++) {
52132             if (v == d.options[i].value) {
52133                 d.selectedIndex = i;
52134                 this.value = v;
52135                 return;
52136             }
52137         }
52138         this.clearValue();
52139     },
52140     /**
52141      * @property {Object} the last set data for the element
52142      */
52143     
52144     lastData : false,
52145     /**
52146      * Sets the value of the field based on a object which is related to the record format for the store.
52147      * @param {Object} value the value to set as. or false on reset?
52148      */
52149     setFromData : function(o){
52150         Roo.log('setfrom data?');
52151          
52152         
52153         
52154     },
52155     // private
52156     reset : function(){
52157         this.clearValue();
52158     },
52159     // private
52160     findRecord : function(prop, value){
52161         
52162         return false;
52163     
52164         var record;
52165         if(this.store.getCount() > 0){
52166             this.store.each(function(r){
52167                 if(r.data[prop] == value){
52168                     record = r;
52169                     return false;
52170                 }
52171                 return true;
52172             });
52173         }
52174         return record;
52175     },
52176     
52177     getName: function()
52178     {
52179         // returns hidden if it's set..
52180         if (!this.rendered) {return ''};
52181         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52182         
52183     },
52184      
52185
52186     
52187
52188     // private
52189     onEmptyResults : function(){
52190         Roo.log('empty results');
52191         //this.collapse();
52192     },
52193
52194     /**
52195      * Returns true if the dropdown list is expanded, else false.
52196      */
52197     isExpanded : function(){
52198         return false;
52199     },
52200
52201     /**
52202      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52204      * @param {String} value The data value of the item to select
52205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52206      * selected item if it is not currently in view (defaults to true)
52207      * @return {Boolean} True if the value matched an item in the list, else false
52208      */
52209     selectByValue : function(v, scrollIntoView){
52210         Roo.log('select By Value');
52211         return false;
52212     
52213         if(v !== undefined && v !== null){
52214             var r = this.findRecord(this.valueField || this.displayField, v);
52215             if(r){
52216                 this.select(this.store.indexOf(r), scrollIntoView);
52217                 return true;
52218             }
52219         }
52220         return false;
52221     },
52222
52223     /**
52224      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52225      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52226      * @param {Number} index The zero-based index of the list item to select
52227      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52228      * selected item if it is not currently in view (defaults to true)
52229      */
52230     select : function(index, scrollIntoView){
52231         Roo.log('select ');
52232         return  ;
52233         
52234         this.selectedIndex = index;
52235         this.view.select(index);
52236         if(scrollIntoView !== false){
52237             var el = this.view.getNode(index);
52238             if(el){
52239                 this.innerList.scrollChildIntoView(el, false);
52240             }
52241         }
52242     },
52243
52244       
52245
52246     // private
52247     validateBlur : function(){
52248         
52249         return;
52250         
52251     },
52252
52253     // private
52254     initQuery : function(){
52255         this.doQuery(this.getRawValue());
52256     },
52257
52258     // private
52259     doForce : function(){
52260         if(this.el.dom.value.length > 0){
52261             this.el.dom.value =
52262                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52263              
52264         }
52265     },
52266
52267     /**
52268      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52269      * query allowing the query action to be canceled if needed.
52270      * @param {String} query The SQL query to execute
52271      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52272      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52273      * saved in the current store (defaults to false)
52274      */
52275     doQuery : function(q, forceAll){
52276         
52277         Roo.log('doQuery?');
52278         if(q === undefined || q === null){
52279             q = '';
52280         }
52281         var qe = {
52282             query: q,
52283             forceAll: forceAll,
52284             combo: this,
52285             cancel:false
52286         };
52287         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52288             return false;
52289         }
52290         q = qe.query;
52291         forceAll = qe.forceAll;
52292         if(forceAll === true || (q.length >= this.minChars)){
52293             if(this.lastQuery != q || this.alwaysQuery){
52294                 this.lastQuery = q;
52295                 if(this.mode == 'local'){
52296                     this.selectedIndex = -1;
52297                     if(forceAll){
52298                         this.store.clearFilter();
52299                     }else{
52300                         this.store.filter(this.displayField, q);
52301                     }
52302                     this.onLoad();
52303                 }else{
52304                     this.store.baseParams[this.queryParam] = q;
52305                     this.store.load({
52306                         params: this.getParams(q)
52307                     });
52308                     this.expand();
52309                 }
52310             }else{
52311                 this.selectedIndex = -1;
52312                 this.onLoad();   
52313             }
52314         }
52315     },
52316
52317     // private
52318     getParams : function(q){
52319         var p = {};
52320         //p[this.queryParam] = q;
52321         if(this.pageSize){
52322             p.start = 0;
52323             p.limit = this.pageSize;
52324         }
52325         return p;
52326     },
52327
52328     /**
52329      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52330      */
52331     collapse : function(){
52332         
52333     },
52334
52335     // private
52336     collapseIf : function(e){
52337         
52338     },
52339
52340     /**
52341      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52342      */
52343     expand : function(){
52344         
52345     } ,
52346
52347     // private
52348      
52349
52350     /** 
52351     * @cfg {Boolean} grow 
52352     * @hide 
52353     */
52354     /** 
52355     * @cfg {Number} growMin 
52356     * @hide 
52357     */
52358     /** 
52359     * @cfg {Number} growMax 
52360     * @hide 
52361     */
52362     /**
52363      * @hide
52364      * @method autoSize
52365      */
52366     
52367     setWidth : function()
52368     {
52369         
52370     },
52371     getResizeEl : function(){
52372         return this.el;
52373     }
52374 });//<script type="text/javasscript">
52375  
52376
52377 /**
52378  * @class Roo.DDView
52379  * A DnD enabled version of Roo.View.
52380  * @param {Element/String} container The Element in which to create the View.
52381  * @param {String} tpl The template string used to create the markup for each element of the View
52382  * @param {Object} config The configuration properties. These include all the config options of
52383  * {@link Roo.View} plus some specific to this class.<br>
52384  * <p>
52385  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52386  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52387  * <p>
52388  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52389 .x-view-drag-insert-above {
52390         border-top:1px dotted #3366cc;
52391 }
52392 .x-view-drag-insert-below {
52393         border-bottom:1px dotted #3366cc;
52394 }
52395 </code></pre>
52396  * 
52397  */
52398  
52399 Roo.DDView = function(container, tpl, config) {
52400     Roo.DDView.superclass.constructor.apply(this, arguments);
52401     this.getEl().setStyle("outline", "0px none");
52402     this.getEl().unselectable();
52403     if (this.dragGroup) {
52404         this.setDraggable(this.dragGroup.split(","));
52405     }
52406     if (this.dropGroup) {
52407         this.setDroppable(this.dropGroup.split(","));
52408     }
52409     if (this.deletable) {
52410         this.setDeletable();
52411     }
52412     this.isDirtyFlag = false;
52413         this.addEvents({
52414                 "drop" : true
52415         });
52416 };
52417
52418 Roo.extend(Roo.DDView, Roo.View, {
52419 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52420 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52421 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52422 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52423
52424         isFormField: true,
52425
52426         reset: Roo.emptyFn,
52427         
52428         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52429
52430         validate: function() {
52431                 return true;
52432         },
52433         
52434         destroy: function() {
52435                 this.purgeListeners();
52436                 this.getEl.removeAllListeners();
52437                 this.getEl().remove();
52438                 if (this.dragZone) {
52439                         if (this.dragZone.destroy) {
52440                                 this.dragZone.destroy();
52441                         }
52442                 }
52443                 if (this.dropZone) {
52444                         if (this.dropZone.destroy) {
52445                                 this.dropZone.destroy();
52446                         }
52447                 }
52448         },
52449
52450 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52451         getName: function() {
52452                 return this.name;
52453         },
52454
52455 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52456         setValue: function(v) {
52457                 if (!this.store) {
52458                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52459                 }
52460                 var data = {};
52461                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52462                 this.store.proxy = new Roo.data.MemoryProxy(data);
52463                 this.store.load();
52464         },
52465
52466 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52467         getValue: function() {
52468                 var result = '(';
52469                 this.store.each(function(rec) {
52470                         result += rec.id + ',';
52471                 });
52472                 return result.substr(0, result.length - 1) + ')';
52473         },
52474         
52475         getIds: function() {
52476                 var i = 0, result = new Array(this.store.getCount());
52477                 this.store.each(function(rec) {
52478                         result[i++] = rec.id;
52479                 });
52480                 return result;
52481         },
52482         
52483         isDirty: function() {
52484                 return this.isDirtyFlag;
52485         },
52486
52487 /**
52488  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52489  *      whole Element becomes the target, and this causes the drop gesture to append.
52490  */
52491     getTargetFromEvent : function(e) {
52492                 var target = e.getTarget();
52493                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52494                 target = target.parentNode;
52495                 }
52496                 if (!target) {
52497                         target = this.el.dom.lastChild || this.el.dom;
52498                 }
52499                 return target;
52500     },
52501
52502 /**
52503  *      Create the drag data which consists of an object which has the property "ddel" as
52504  *      the drag proxy element. 
52505  */
52506     getDragData : function(e) {
52507         var target = this.findItemFromChild(e.getTarget());
52508                 if(target) {
52509                         this.handleSelection(e);
52510                         var selNodes = this.getSelectedNodes();
52511             var dragData = {
52512                 source: this,
52513                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52514                 nodes: selNodes,
52515                 records: []
52516                         };
52517                         var selectedIndices = this.getSelectedIndexes();
52518                         for (var i = 0; i < selectedIndices.length; i++) {
52519                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52520                         }
52521                         if (selNodes.length == 1) {
52522                                 dragData.ddel = target.cloneNode(true); // the div element
52523                         } else {
52524                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52525                                 div.className = 'multi-proxy';
52526                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52527                                         div.appendChild(selNodes[i].cloneNode(true));
52528                                 }
52529                                 dragData.ddel = div;
52530                         }
52531             //console.log(dragData)
52532             //console.log(dragData.ddel.innerHTML)
52533                         return dragData;
52534                 }
52535         //console.log('nodragData')
52536                 return false;
52537     },
52538     
52539 /**     Specify to which ddGroup items in this DDView may be dragged. */
52540     setDraggable: function(ddGroup) {
52541         if (ddGroup instanceof Array) {
52542                 Roo.each(ddGroup, this.setDraggable, this);
52543                 return;
52544         }
52545         if (this.dragZone) {
52546                 this.dragZone.addToGroup(ddGroup);
52547         } else {
52548                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52549                                 containerScroll: true,
52550                                 ddGroup: ddGroup 
52551
52552                         });
52553 //                      Draggability implies selection. DragZone's mousedown selects the element.
52554                         if (!this.multiSelect) { this.singleSelect = true; }
52555
52556 //                      Wire the DragZone's handlers up to methods in *this*
52557                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
52558                 }
52559     },
52560
52561 /**     Specify from which ddGroup this DDView accepts drops. */
52562     setDroppable: function(ddGroup) {
52563         if (ddGroup instanceof Array) {
52564                 Roo.each(ddGroup, this.setDroppable, this);
52565                 return;
52566         }
52567         if (this.dropZone) {
52568                 this.dropZone.addToGroup(ddGroup);
52569         } else {
52570                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
52571                                 containerScroll: true,
52572                                 ddGroup: ddGroup
52573                         });
52574
52575 //                      Wire the DropZone's handlers up to methods in *this*
52576                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
52577                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
52578                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
52579                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
52580                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
52581                 }
52582     },
52583
52584 /**     Decide whether to drop above or below a View node. */
52585     getDropPoint : function(e, n, dd){
52586         if (n == this.el.dom) { return "above"; }
52587                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
52588                 var c = t + (b - t) / 2;
52589                 var y = Roo.lib.Event.getPageY(e);
52590                 if(y <= c) {
52591                         return "above";
52592                 }else{
52593                         return "below";
52594                 }
52595     },
52596
52597     onNodeEnter : function(n, dd, e, data){
52598                 return false;
52599     },
52600     
52601     onNodeOver : function(n, dd, e, data){
52602                 var pt = this.getDropPoint(e, n, dd);
52603                 // set the insert point style on the target node
52604                 var dragElClass = this.dropNotAllowed;
52605                 if (pt) {
52606                         var targetElClass;
52607                         if (pt == "above"){
52608                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
52609                                 targetElClass = "x-view-drag-insert-above";
52610                         } else {
52611                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
52612                                 targetElClass = "x-view-drag-insert-below";
52613                         }
52614                         if (this.lastInsertClass != targetElClass){
52615                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
52616                                 this.lastInsertClass = targetElClass;
52617                         }
52618                 }
52619                 return dragElClass;
52620         },
52621
52622     onNodeOut : function(n, dd, e, data){
52623                 this.removeDropIndicators(n);
52624     },
52625
52626     onNodeDrop : function(n, dd, e, data){
52627         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
52628                 return false;
52629         }
52630         var pt = this.getDropPoint(e, n, dd);
52631                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
52632                 if (pt == "below") { insertAt++; }
52633                 for (var i = 0; i < data.records.length; i++) {
52634                         var r = data.records[i];
52635                         var dup = this.store.getById(r.id);
52636                         if (dup && (dd != this.dragZone)) {
52637                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
52638                         } else {
52639                                 if (data.copy) {
52640                                         this.store.insert(insertAt++, r.copy());
52641                                 } else {
52642                                         data.source.isDirtyFlag = true;
52643                                         r.store.remove(r);
52644                                         this.store.insert(insertAt++, r);
52645                                 }
52646                                 this.isDirtyFlag = true;
52647                         }
52648                 }
52649                 this.dragZone.cachedTarget = null;
52650                 return true;
52651     },
52652
52653     removeDropIndicators : function(n){
52654                 if(n){
52655                         Roo.fly(n).removeClass([
52656                                 "x-view-drag-insert-above",
52657                                 "x-view-drag-insert-below"]);
52658                         this.lastInsertClass = "_noclass";
52659                 }
52660     },
52661
52662 /**
52663  *      Utility method. Add a delete option to the DDView's context menu.
52664  *      @param {String} imageUrl The URL of the "delete" icon image.
52665  */
52666         setDeletable: function(imageUrl) {
52667                 if (!this.singleSelect && !this.multiSelect) {
52668                         this.singleSelect = true;
52669                 }
52670                 var c = this.getContextMenu();
52671                 this.contextMenu.on("itemclick", function(item) {
52672                         switch (item.id) {
52673                                 case "delete":
52674                                         this.remove(this.getSelectedIndexes());
52675                                         break;
52676                         }
52677                 }, this);
52678                 this.contextMenu.add({
52679                         icon: imageUrl,
52680                         id: "delete",
52681                         text: 'Delete'
52682                 });
52683         },
52684         
52685 /**     Return the context menu for this DDView. */
52686         getContextMenu: function() {
52687                 if (!this.contextMenu) {
52688 //                      Create the View's context menu
52689                         this.contextMenu = new Roo.menu.Menu({
52690                                 id: this.id + "-contextmenu"
52691                         });
52692                         this.el.on("contextmenu", this.showContextMenu, this);
52693                 }
52694                 return this.contextMenu;
52695         },
52696         
52697         disableContextMenu: function() {
52698                 if (this.contextMenu) {
52699                         this.el.un("contextmenu", this.showContextMenu, this);
52700                 }
52701         },
52702
52703         showContextMenu: function(e, item) {
52704         item = this.findItemFromChild(e.getTarget());
52705                 if (item) {
52706                         e.stopEvent();
52707                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
52708                         this.contextMenu.showAt(e.getXY());
52709             }
52710     },
52711
52712 /**
52713  *      Remove {@link Roo.data.Record}s at the specified indices.
52714  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52715  */
52716     remove: function(selectedIndices) {
52717                 selectedIndices = [].concat(selectedIndices);
52718                 for (var i = 0; i < selectedIndices.length; i++) {
52719                         var rec = this.store.getAt(selectedIndices[i]);
52720                         this.store.remove(rec);
52721                 }
52722     },
52723
52724 /**
52725  *      Double click fires the event, but also, if this is draggable, and there is only one other
52726  *      related DropZone, it transfers the selected node.
52727  */
52728     onDblClick : function(e){
52729         var item = this.findItemFromChild(e.getTarget());
52730         if(item){
52731             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52732                 return false;
52733             }
52734             if (this.dragGroup) {
52735                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52736                     while (targets.indexOf(this.dropZone) > -1) {
52737                             targets.remove(this.dropZone);
52738                                 }
52739                     if (targets.length == 1) {
52740                                         this.dragZone.cachedTarget = null;
52741                         var el = Roo.get(targets[0].getEl());
52742                         var box = el.getBox(true);
52743                         targets[0].onNodeDrop(el.dom, {
52744                                 target: el.dom,
52745                                 xy: [box.x, box.y + box.height - 1]
52746                         }, null, this.getDragData(e));
52747                     }
52748                 }
52749         }
52750     },
52751     
52752     handleSelection: function(e) {
52753                 this.dragZone.cachedTarget = null;
52754         var item = this.findItemFromChild(e.getTarget());
52755         if (!item) {
52756                 this.clearSelections(true);
52757                 return;
52758         }
52759                 if (item && (this.multiSelect || this.singleSelect)){
52760                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52761                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52762                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52763                                 this.unselect(item);
52764                         } else {
52765                                 this.select(item, this.multiSelect && e.ctrlKey);
52766                                 this.lastSelection = item;
52767                         }
52768                 }
52769     },
52770
52771     onItemClick : function(item, index, e){
52772                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52773                         return false;
52774                 }
52775                 return true;
52776     },
52777
52778     unselect : function(nodeInfo, suppressEvent){
52779                 var node = this.getNode(nodeInfo);
52780                 if(node && this.isSelected(node)){
52781                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52782                                 Roo.fly(node).removeClass(this.selectedClass);
52783                                 this.selections.remove(node);
52784                                 if(!suppressEvent){
52785                                         this.fireEvent("selectionchange", this, this.selections);
52786                                 }
52787                         }
52788                 }
52789     }
52790 });
52791 /*
52792  * Based on:
52793  * Ext JS Library 1.1.1
52794  * Copyright(c) 2006-2007, Ext JS, LLC.
52795  *
52796  * Originally Released Under LGPL - original licence link has changed is not relivant.
52797  *
52798  * Fork - LGPL
52799  * <script type="text/javascript">
52800  */
52801  
52802 /**
52803  * @class Roo.LayoutManager
52804  * @extends Roo.util.Observable
52805  * Base class for layout managers.
52806  */
52807 Roo.LayoutManager = function(container, config){
52808     Roo.LayoutManager.superclass.constructor.call(this);
52809     this.el = Roo.get(container);
52810     // ie scrollbar fix
52811     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52812         document.body.scroll = "no";
52813     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52814         this.el.position('relative');
52815     }
52816     this.id = this.el.id;
52817     this.el.addClass("x-layout-container");
52818     /** false to disable window resize monitoring @type Boolean */
52819     this.monitorWindowResize = true;
52820     this.regions = {};
52821     this.addEvents({
52822         /**
52823          * @event layout
52824          * Fires when a layout is performed. 
52825          * @param {Roo.LayoutManager} this
52826          */
52827         "layout" : true,
52828         /**
52829          * @event regionresized
52830          * Fires when the user resizes a region. 
52831          * @param {Roo.LayoutRegion} region The resized region
52832          * @param {Number} newSize The new size (width for east/west, height for north/south)
52833          */
52834         "regionresized" : true,
52835         /**
52836          * @event regioncollapsed
52837          * Fires when a region is collapsed. 
52838          * @param {Roo.LayoutRegion} region The collapsed region
52839          */
52840         "regioncollapsed" : true,
52841         /**
52842          * @event regionexpanded
52843          * Fires when a region is expanded.  
52844          * @param {Roo.LayoutRegion} region The expanded region
52845          */
52846         "regionexpanded" : true
52847     });
52848     this.updating = false;
52849     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52850 };
52851
52852 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52853     /**
52854      * Returns true if this layout is currently being updated
52855      * @return {Boolean}
52856      */
52857     isUpdating : function(){
52858         return this.updating; 
52859     },
52860     
52861     /**
52862      * Suspend the LayoutManager from doing auto-layouts while
52863      * making multiple add or remove calls
52864      */
52865     beginUpdate : function(){
52866         this.updating = true;    
52867     },
52868     
52869     /**
52870      * Restore auto-layouts and optionally disable the manager from performing a layout
52871      * @param {Boolean} noLayout true to disable a layout update 
52872      */
52873     endUpdate : function(noLayout){
52874         this.updating = false;
52875         if(!noLayout){
52876             this.layout();
52877         }    
52878     },
52879     
52880     layout: function(){
52881         
52882     },
52883     
52884     onRegionResized : function(region, newSize){
52885         this.fireEvent("regionresized", region, newSize);
52886         this.layout();
52887     },
52888     
52889     onRegionCollapsed : function(region){
52890         this.fireEvent("regioncollapsed", region);
52891     },
52892     
52893     onRegionExpanded : function(region){
52894         this.fireEvent("regionexpanded", region);
52895     },
52896         
52897     /**
52898      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52899      * performs box-model adjustments.
52900      * @return {Object} The size as an object {width: (the width), height: (the height)}
52901      */
52902     getViewSize : function(){
52903         var size;
52904         if(this.el.dom != document.body){
52905             size = this.el.getSize();
52906         }else{
52907             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52908         }
52909         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52910         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52911         return size;
52912     },
52913     
52914     /**
52915      * Returns the Element this layout is bound to.
52916      * @return {Roo.Element}
52917      */
52918     getEl : function(){
52919         return this.el;
52920     },
52921     
52922     /**
52923      * Returns the specified region.
52924      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52925      * @return {Roo.LayoutRegion}
52926      */
52927     getRegion : function(target){
52928         return this.regions[target.toLowerCase()];
52929     },
52930     
52931     onWindowResize : function(){
52932         if(this.monitorWindowResize){
52933             this.layout();
52934         }
52935     }
52936 });/*
52937  * Based on:
52938  * Ext JS Library 1.1.1
52939  * Copyright(c) 2006-2007, Ext JS, LLC.
52940  *
52941  * Originally Released Under LGPL - original licence link has changed is not relivant.
52942  *
52943  * Fork - LGPL
52944  * <script type="text/javascript">
52945  */
52946 /**
52947  * @class Roo.BorderLayout
52948  * @extends Roo.LayoutManager
52949  * @children Roo.ContentPanel
52950  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52951  * please see: <br><br>
52952  * <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>
52953  * <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>
52954  * Example:
52955  <pre><code>
52956  var layout = new Roo.BorderLayout(document.body, {
52957     north: {
52958         initialSize: 25,
52959         titlebar: false
52960     },
52961     west: {
52962         split:true,
52963         initialSize: 200,
52964         minSize: 175,
52965         maxSize: 400,
52966         titlebar: true,
52967         collapsible: true
52968     },
52969     east: {
52970         split:true,
52971         initialSize: 202,
52972         minSize: 175,
52973         maxSize: 400,
52974         titlebar: true,
52975         collapsible: true
52976     },
52977     south: {
52978         split:true,
52979         initialSize: 100,
52980         minSize: 100,
52981         maxSize: 200,
52982         titlebar: true,
52983         collapsible: true
52984     },
52985     center: {
52986         titlebar: true,
52987         autoScroll:true,
52988         resizeTabs: true,
52989         minTabWidth: 50,
52990         preferredTabWidth: 150
52991     }
52992 });
52993
52994 // shorthand
52995 var CP = Roo.ContentPanel;
52996
52997 layout.beginUpdate();
52998 layout.add("north", new CP("north", "North"));
52999 layout.add("south", new CP("south", {title: "South", closable: true}));
53000 layout.add("west", new CP("west", {title: "West"}));
53001 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53002 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53003 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53004 layout.getRegion("center").showPanel("center1");
53005 layout.endUpdate();
53006 </code></pre>
53007
53008 <b>The container the layout is rendered into can be either the body element or any other element.
53009 If it is not the body element, the container needs to either be an absolute positioned element,
53010 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53011 the container size if it is not the body element.</b>
53012
53013 * @constructor
53014 * Create a new BorderLayout
53015 * @param {String/HTMLElement/Element} container The container this layout is bound to
53016 * @param {Object} config Configuration options
53017  */
53018 Roo.BorderLayout = function(container, config){
53019     config = config || {};
53020     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53021     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53022     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53023         var target = this.factory.validRegions[i];
53024         if(config[target]){
53025             this.addRegion(target, config[target]);
53026         }
53027     }
53028 };
53029
53030 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53031         
53032         /**
53033          * @cfg {Roo.LayoutRegion} east
53034          */
53035         /**
53036          * @cfg {Roo.LayoutRegion} west
53037          */
53038         /**
53039          * @cfg {Roo.LayoutRegion} north
53040          */
53041         /**
53042          * @cfg {Roo.LayoutRegion} south
53043          */
53044         /**
53045          * @cfg {Roo.LayoutRegion} center
53046          */
53047     /**
53048      * Creates and adds a new region if it doesn't already exist.
53049      * @param {String} target The target region key (north, south, east, west or center).
53050      * @param {Object} config The regions config object
53051      * @return {BorderLayoutRegion} The new region
53052      */
53053     addRegion : function(target, config){
53054         if(!this.regions[target]){
53055             var r = this.factory.create(target, this, config);
53056             this.bindRegion(target, r);
53057         }
53058         return this.regions[target];
53059     },
53060
53061     // private (kinda)
53062     bindRegion : function(name, r){
53063         this.regions[name] = r;
53064         r.on("visibilitychange", this.layout, this);
53065         r.on("paneladded", this.layout, this);
53066         r.on("panelremoved", this.layout, this);
53067         r.on("invalidated", this.layout, this);
53068         r.on("resized", this.onRegionResized, this);
53069         r.on("collapsed", this.onRegionCollapsed, this);
53070         r.on("expanded", this.onRegionExpanded, this);
53071     },
53072
53073     /**
53074      * Performs a layout update.
53075      */
53076     layout : function(){
53077         if(this.updating) {
53078             return;
53079         }
53080         var size = this.getViewSize();
53081         var w = size.width;
53082         var h = size.height;
53083         var centerW = w;
53084         var centerH = h;
53085         var centerY = 0;
53086         var centerX = 0;
53087         //var x = 0, y = 0;
53088
53089         var rs = this.regions;
53090         var north = rs["north"];
53091         var south = rs["south"]; 
53092         var west = rs["west"];
53093         var east = rs["east"];
53094         var center = rs["center"];
53095         //if(this.hideOnLayout){ // not supported anymore
53096             //c.el.setStyle("display", "none");
53097         //}
53098         if(north && north.isVisible()){
53099             var b = north.getBox();
53100             var m = north.getMargins();
53101             b.width = w - (m.left+m.right);
53102             b.x = m.left;
53103             b.y = m.top;
53104             centerY = b.height + b.y + m.bottom;
53105             centerH -= centerY;
53106             north.updateBox(this.safeBox(b));
53107         }
53108         if(south && south.isVisible()){
53109             var b = south.getBox();
53110             var m = south.getMargins();
53111             b.width = w - (m.left+m.right);
53112             b.x = m.left;
53113             var totalHeight = (b.height + m.top + m.bottom);
53114             b.y = h - totalHeight + m.top;
53115             centerH -= totalHeight;
53116             south.updateBox(this.safeBox(b));
53117         }
53118         if(west && west.isVisible()){
53119             var b = west.getBox();
53120             var m = west.getMargins();
53121             b.height = centerH - (m.top+m.bottom);
53122             b.x = m.left;
53123             b.y = centerY + m.top;
53124             var totalWidth = (b.width + m.left + m.right);
53125             centerX += totalWidth;
53126             centerW -= totalWidth;
53127             west.updateBox(this.safeBox(b));
53128         }
53129         if(east && east.isVisible()){
53130             var b = east.getBox();
53131             var m = east.getMargins();
53132             b.height = centerH - (m.top+m.bottom);
53133             var totalWidth = (b.width + m.left + m.right);
53134             b.x = w - totalWidth + m.left;
53135             b.y = centerY + m.top;
53136             centerW -= totalWidth;
53137             east.updateBox(this.safeBox(b));
53138         }
53139         if(center){
53140             var m = center.getMargins();
53141             var centerBox = {
53142                 x: centerX + m.left,
53143                 y: centerY + m.top,
53144                 width: centerW - (m.left+m.right),
53145                 height: centerH - (m.top+m.bottom)
53146             };
53147             //if(this.hideOnLayout){
53148                 //center.el.setStyle("display", "block");
53149             //}
53150             center.updateBox(this.safeBox(centerBox));
53151         }
53152         this.el.repaint();
53153         this.fireEvent("layout", this);
53154     },
53155
53156     // private
53157     safeBox : function(box){
53158         box.width = Math.max(0, box.width);
53159         box.height = Math.max(0, box.height);
53160         return box;
53161     },
53162
53163     /**
53164      * Adds a ContentPanel (or subclass) to this layout.
53165      * @param {String} target The target region key (north, south, east, west or center).
53166      * @param {Roo.ContentPanel} panel The panel to add
53167      * @return {Roo.ContentPanel} The added panel
53168      */
53169     add : function(target, panel){
53170          
53171         target = target.toLowerCase();
53172         return this.regions[target].add(panel);
53173     },
53174
53175     /**
53176      * Remove a ContentPanel (or subclass) to this layout.
53177      * @param {String} target The target region key (north, south, east, west or center).
53178      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53179      * @return {Roo.ContentPanel} The removed panel
53180      */
53181     remove : function(target, panel){
53182         target = target.toLowerCase();
53183         return this.regions[target].remove(panel);
53184     },
53185
53186     /**
53187      * Searches all regions for a panel with the specified id
53188      * @param {String} panelId
53189      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53190      */
53191     findPanel : function(panelId){
53192         var rs = this.regions;
53193         for(var target in rs){
53194             if(typeof rs[target] != "function"){
53195                 var p = rs[target].getPanel(panelId);
53196                 if(p){
53197                     return p;
53198                 }
53199             }
53200         }
53201         return null;
53202     },
53203
53204     /**
53205      * Searches all regions for a panel with the specified id and activates (shows) it.
53206      * @param {String/ContentPanel} panelId The panels id or the panel itself
53207      * @return {Roo.ContentPanel} The shown panel or null
53208      */
53209     showPanel : function(panelId) {
53210       var rs = this.regions;
53211       for(var target in rs){
53212          var r = rs[target];
53213          if(typeof r != "function"){
53214             if(r.hasPanel(panelId)){
53215                return r.showPanel(panelId);
53216             }
53217          }
53218       }
53219       return null;
53220    },
53221
53222    /**
53223      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53224      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53225      */
53226     restoreState : function(provider){
53227         if(!provider){
53228             provider = Roo.state.Manager;
53229         }
53230         var sm = new Roo.LayoutStateManager();
53231         sm.init(this, provider);
53232     },
53233
53234     /**
53235      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53236      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53237      * a valid ContentPanel config object.  Example:
53238      * <pre><code>
53239 // Create the main layout
53240 var layout = new Roo.BorderLayout('main-ct', {
53241     west: {
53242         split:true,
53243         minSize: 175,
53244         titlebar: true
53245     },
53246     center: {
53247         title:'Components'
53248     }
53249 }, 'main-ct');
53250
53251 // Create and add multiple ContentPanels at once via configs
53252 layout.batchAdd({
53253    west: {
53254        id: 'source-files',
53255        autoCreate:true,
53256        title:'Ext Source Files',
53257        autoScroll:true,
53258        fitToFrame:true
53259    },
53260    center : {
53261        el: cview,
53262        autoScroll:true,
53263        fitToFrame:true,
53264        toolbar: tb,
53265        resizeEl:'cbody'
53266    }
53267 });
53268 </code></pre>
53269      * @param {Object} regions An object containing ContentPanel configs by region name
53270      */
53271     batchAdd : function(regions){
53272         this.beginUpdate();
53273         for(var rname in regions){
53274             var lr = this.regions[rname];
53275             if(lr){
53276                 this.addTypedPanels(lr, regions[rname]);
53277             }
53278         }
53279         this.endUpdate();
53280     },
53281
53282     // private
53283     addTypedPanels : function(lr, ps){
53284         if(typeof ps == 'string'){
53285             lr.add(new Roo.ContentPanel(ps));
53286         }
53287         else if(ps instanceof Array){
53288             for(var i =0, len = ps.length; i < len; i++){
53289                 this.addTypedPanels(lr, ps[i]);
53290             }
53291         }
53292         else if(!ps.events){ // raw config?
53293             var el = ps.el;
53294             delete ps.el; // prevent conflict
53295             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53296         }
53297         else {  // panel object assumed!
53298             lr.add(ps);
53299         }
53300     },
53301     /**
53302      * Adds a xtype elements to the layout.
53303      * <pre><code>
53304
53305 layout.addxtype({
53306        xtype : 'ContentPanel',
53307        region: 'west',
53308        items: [ .... ]
53309    }
53310 );
53311
53312 layout.addxtype({
53313         xtype : 'NestedLayoutPanel',
53314         region: 'west',
53315         layout: {
53316            center: { },
53317            west: { }   
53318         },
53319         items : [ ... list of content panels or nested layout panels.. ]
53320    }
53321 );
53322 </code></pre>
53323      * @param {Object} cfg Xtype definition of item to add.
53324      */
53325     addxtype : function(cfg)
53326     {
53327         // basically accepts a pannel...
53328         // can accept a layout region..!?!?
53329         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53330         
53331         if (!cfg.xtype.match(/Panel$/)) {
53332             return false;
53333         }
53334         var ret = false;
53335         
53336         if (typeof(cfg.region) == 'undefined') {
53337             Roo.log("Failed to add Panel, region was not set");
53338             Roo.log(cfg);
53339             return false;
53340         }
53341         var region = cfg.region;
53342         delete cfg.region;
53343         
53344           
53345         var xitems = [];
53346         if (cfg.items) {
53347             xitems = cfg.items;
53348             delete cfg.items;
53349         }
53350         var nb = false;
53351         
53352         switch(cfg.xtype) 
53353         {
53354             case 'ContentPanel':  // ContentPanel (el, cfg)
53355             case 'ScrollPanel':  // ContentPanel (el, cfg)
53356             case 'ViewPanel': 
53357                 if(cfg.autoCreate) {
53358                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53359                 } else {
53360                     var el = this.el.createChild();
53361                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53362                 }
53363                 
53364                 this.add(region, ret);
53365                 break;
53366             
53367             
53368             case 'TreePanel': // our new panel!
53369                 cfg.el = this.el.createChild();
53370                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53371                 this.add(region, ret);
53372                 break;
53373             
53374             case 'NestedLayoutPanel': 
53375                 // create a new Layout (which is  a Border Layout...
53376                 var el = this.el.createChild();
53377                 var clayout = cfg.layout;
53378                 delete cfg.layout;
53379                 clayout.items   = clayout.items  || [];
53380                 // replace this exitems with the clayout ones..
53381                 xitems = clayout.items;
53382                  
53383                 
53384                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53385                     cfg.background = false;
53386                 }
53387                 var layout = new Roo.BorderLayout(el, clayout);
53388                 
53389                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53390                 //console.log('adding nested layout panel '  + cfg.toSource());
53391                 this.add(region, ret);
53392                 nb = {}; /// find first...
53393                 break;
53394                 
53395             case 'GridPanel': 
53396             
53397                 // needs grid and region
53398                 
53399                 //var el = this.getRegion(region).el.createChild();
53400                 var el = this.el.createChild();
53401                 // create the grid first...
53402                 
53403                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53404                 delete cfg.grid;
53405                 if (region == 'center' && this.active ) {
53406                     cfg.background = false;
53407                 }
53408                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53409                 
53410                 this.add(region, ret);
53411                 if (cfg.background) {
53412                     ret.on('activate', function(gp) {
53413                         if (!gp.grid.rendered) {
53414                             gp.grid.render();
53415                         }
53416                     });
53417                 } else {
53418                     grid.render();
53419                 }
53420                 break;
53421            
53422            
53423            
53424                 
53425                 
53426                 
53427             default:
53428                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53429                     
53430                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53431                     this.add(region, ret);
53432                 } else {
53433                 
53434                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53435                     return null;
53436                 }
53437                 
53438              // GridPanel (grid, cfg)
53439             
53440         }
53441         this.beginUpdate();
53442         // add children..
53443         var region = '';
53444         var abn = {};
53445         Roo.each(xitems, function(i)  {
53446             region = nb && i.region ? i.region : false;
53447             
53448             var add = ret.addxtype(i);
53449            
53450             if (region) {
53451                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53452                 if (!i.background) {
53453                     abn[region] = nb[region] ;
53454                 }
53455             }
53456             
53457         });
53458         this.endUpdate();
53459
53460         // make the last non-background panel active..
53461         //if (nb) { Roo.log(abn); }
53462         if (nb) {
53463             
53464             for(var r in abn) {
53465                 region = this.getRegion(r);
53466                 if (region) {
53467                     // tried using nb[r], but it does not work..
53468                      
53469                     region.showPanel(abn[r]);
53470                    
53471                 }
53472             }
53473         }
53474         return ret;
53475         
53476     }
53477 });
53478
53479 /**
53480  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53481  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53482  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53483  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53484  * <pre><code>
53485 // shorthand
53486 var CP = Roo.ContentPanel;
53487
53488 var layout = Roo.BorderLayout.create({
53489     north: {
53490         initialSize: 25,
53491         titlebar: false,
53492         panels: [new CP("north", "North")]
53493     },
53494     west: {
53495         split:true,
53496         initialSize: 200,
53497         minSize: 175,
53498         maxSize: 400,
53499         titlebar: true,
53500         collapsible: true,
53501         panels: [new CP("west", {title: "West"})]
53502     },
53503     east: {
53504         split:true,
53505         initialSize: 202,
53506         minSize: 175,
53507         maxSize: 400,
53508         titlebar: true,
53509         collapsible: true,
53510         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53511     },
53512     south: {
53513         split:true,
53514         initialSize: 100,
53515         minSize: 100,
53516         maxSize: 200,
53517         titlebar: true,
53518         collapsible: true,
53519         panels: [new CP("south", {title: "South", closable: true})]
53520     },
53521     center: {
53522         titlebar: true,
53523         autoScroll:true,
53524         resizeTabs: true,
53525         minTabWidth: 50,
53526         preferredTabWidth: 150,
53527         panels: [
53528             new CP("center1", {title: "Close Me", closable: true}),
53529             new CP("center2", {title: "Center Panel", closable: false})
53530         ]
53531     }
53532 }, document.body);
53533
53534 layout.getRegion("center").showPanel("center1");
53535 </code></pre>
53536  * @param config
53537  * @param targetEl
53538  */
53539 Roo.BorderLayout.create = function(config, targetEl){
53540     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53541     layout.beginUpdate();
53542     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53543     for(var j = 0, jlen = regions.length; j < jlen; j++){
53544         var lr = regions[j];
53545         if(layout.regions[lr] && config[lr].panels){
53546             var r = layout.regions[lr];
53547             var ps = config[lr].panels;
53548             layout.addTypedPanels(r, ps);
53549         }
53550     }
53551     layout.endUpdate();
53552     return layout;
53553 };
53554
53555 // private
53556 Roo.BorderLayout.RegionFactory = {
53557     // private
53558     validRegions : ["north","south","east","west","center"],
53559
53560     // private
53561     create : function(target, mgr, config){
53562         target = target.toLowerCase();
53563         if(config.lightweight || config.basic){
53564             return new Roo.BasicLayoutRegion(mgr, config, target);
53565         }
53566         switch(target){
53567             case "north":
53568                 return new Roo.NorthLayoutRegion(mgr, config);
53569             case "south":
53570                 return new Roo.SouthLayoutRegion(mgr, config);
53571             case "east":
53572                 return new Roo.EastLayoutRegion(mgr, config);
53573             case "west":
53574                 return new Roo.WestLayoutRegion(mgr, config);
53575             case "center":
53576                 return new Roo.CenterLayoutRegion(mgr, config);
53577         }
53578         throw 'Layout region "'+target+'" not supported.';
53579     }
53580 };/*
53581  * Based on:
53582  * Ext JS Library 1.1.1
53583  * Copyright(c) 2006-2007, Ext JS, LLC.
53584  *
53585  * Originally Released Under LGPL - original licence link has changed is not relivant.
53586  *
53587  * Fork - LGPL
53588  * <script type="text/javascript">
53589  */
53590  
53591 /**
53592  * @class Roo.BasicLayoutRegion
53593  * @extends Roo.util.Observable
53594  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
53595  * and does not have a titlebar, tabs or any other features. All it does is size and position 
53596  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
53597  */
53598 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
53599     this.mgr = mgr;
53600     this.position  = pos;
53601     this.events = {
53602         /**
53603          * @scope Roo.BasicLayoutRegion
53604          */
53605         
53606         /**
53607          * @event beforeremove
53608          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
53609          * @param {Roo.LayoutRegion} this
53610          * @param {Roo.ContentPanel} panel The panel
53611          * @param {Object} e The cancel event object
53612          */
53613         "beforeremove" : true,
53614         /**
53615          * @event invalidated
53616          * Fires when the layout for this region is changed.
53617          * @param {Roo.LayoutRegion} this
53618          */
53619         "invalidated" : true,
53620         /**
53621          * @event visibilitychange
53622          * Fires when this region is shown or hidden 
53623          * @param {Roo.LayoutRegion} this
53624          * @param {Boolean} visibility true or false
53625          */
53626         "visibilitychange" : true,
53627         /**
53628          * @event paneladded
53629          * Fires when a panel is added. 
53630          * @param {Roo.LayoutRegion} this
53631          * @param {Roo.ContentPanel} panel The panel
53632          */
53633         "paneladded" : true,
53634         /**
53635          * @event panelremoved
53636          * Fires when a panel is removed. 
53637          * @param {Roo.LayoutRegion} this
53638          * @param {Roo.ContentPanel} panel The panel
53639          */
53640         "panelremoved" : true,
53641         /**
53642          * @event beforecollapse
53643          * Fires when this region before collapse.
53644          * @param {Roo.LayoutRegion} this
53645          */
53646         "beforecollapse" : true,
53647         /**
53648          * @event collapsed
53649          * Fires when this region is collapsed.
53650          * @param {Roo.LayoutRegion} this
53651          */
53652         "collapsed" : true,
53653         /**
53654          * @event expanded
53655          * Fires when this region is expanded.
53656          * @param {Roo.LayoutRegion} this
53657          */
53658         "expanded" : true,
53659         /**
53660          * @event slideshow
53661          * Fires when this region is slid into view.
53662          * @param {Roo.LayoutRegion} this
53663          */
53664         "slideshow" : true,
53665         /**
53666          * @event slidehide
53667          * Fires when this region slides out of view. 
53668          * @param {Roo.LayoutRegion} this
53669          */
53670         "slidehide" : true,
53671         /**
53672          * @event panelactivated
53673          * Fires when a panel is activated. 
53674          * @param {Roo.LayoutRegion} this
53675          * @param {Roo.ContentPanel} panel The activated panel
53676          */
53677         "panelactivated" : true,
53678         /**
53679          * @event resized
53680          * Fires when the user resizes this region. 
53681          * @param {Roo.LayoutRegion} this
53682          * @param {Number} newSize The new size (width for east/west, height for north/south)
53683          */
53684         "resized" : true
53685     };
53686     /** A collection of panels in this region. @type Roo.util.MixedCollection */
53687     this.panels = new Roo.util.MixedCollection();
53688     this.panels.getKey = this.getPanelId.createDelegate(this);
53689     this.box = null;
53690     this.activePanel = null;
53691     // ensure listeners are added...
53692     
53693     if (config.listeners || config.events) {
53694         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
53695             listeners : config.listeners || {},
53696             events : config.events || {}
53697         });
53698     }
53699     
53700     if(skipConfig !== true){
53701         this.applyConfig(config);
53702     }
53703 };
53704
53705 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
53706     getPanelId : function(p){
53707         return p.getId();
53708     },
53709     
53710     applyConfig : function(config){
53711         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53712         this.config = config;
53713         
53714     },
53715     
53716     /**
53717      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53718      * the width, for horizontal (north, south) the height.
53719      * @param {Number} newSize The new width or height
53720      */
53721     resizeTo : function(newSize){
53722         var el = this.el ? this.el :
53723                  (this.activePanel ? this.activePanel.getEl() : null);
53724         if(el){
53725             switch(this.position){
53726                 case "east":
53727                 case "west":
53728                     el.setWidth(newSize);
53729                     this.fireEvent("resized", this, newSize);
53730                 break;
53731                 case "north":
53732                 case "south":
53733                     el.setHeight(newSize);
53734                     this.fireEvent("resized", this, newSize);
53735                 break;                
53736             }
53737         }
53738     },
53739     
53740     getBox : function(){
53741         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53742     },
53743     
53744     getMargins : function(){
53745         return this.margins;
53746     },
53747     
53748     updateBox : function(box){
53749         this.box = box;
53750         var el = this.activePanel.getEl();
53751         el.dom.style.left = box.x + "px";
53752         el.dom.style.top = box.y + "px";
53753         this.activePanel.setSize(box.width, box.height);
53754     },
53755     
53756     /**
53757      * Returns the container element for this region.
53758      * @return {Roo.Element}
53759      */
53760     getEl : function(){
53761         return this.activePanel;
53762     },
53763     
53764     /**
53765      * Returns true if this region is currently visible.
53766      * @return {Boolean}
53767      */
53768     isVisible : function(){
53769         return this.activePanel ? true : false;
53770     },
53771     
53772     setActivePanel : function(panel){
53773         panel = this.getPanel(panel);
53774         if(this.activePanel && this.activePanel != panel){
53775             this.activePanel.setActiveState(false);
53776             this.activePanel.getEl().setLeftTop(-10000,-10000);
53777         }
53778         this.activePanel = panel;
53779         panel.setActiveState(true);
53780         if(this.box){
53781             panel.setSize(this.box.width, this.box.height);
53782         }
53783         this.fireEvent("panelactivated", this, panel);
53784         this.fireEvent("invalidated");
53785     },
53786     
53787     /**
53788      * Show the specified panel.
53789      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53790      * @return {Roo.ContentPanel} The shown panel or null
53791      */
53792     showPanel : function(panel){
53793         if(panel = this.getPanel(panel)){
53794             this.setActivePanel(panel);
53795         }
53796         return panel;
53797     },
53798     
53799     /**
53800      * Get the active panel for this region.
53801      * @return {Roo.ContentPanel} The active panel or null
53802      */
53803     getActivePanel : function(){
53804         return this.activePanel;
53805     },
53806     
53807     /**
53808      * Add the passed ContentPanel(s)
53809      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53810      * @return {Roo.ContentPanel} The panel added (if only one was added)
53811      */
53812     add : function(panel){
53813         if(arguments.length > 1){
53814             for(var i = 0, len = arguments.length; i < len; i++) {
53815                 this.add(arguments[i]);
53816             }
53817             return null;
53818         }
53819         if(this.hasPanel(panel)){
53820             this.showPanel(panel);
53821             return panel;
53822         }
53823         var el = panel.getEl();
53824         if(el.dom.parentNode != this.mgr.el.dom){
53825             this.mgr.el.dom.appendChild(el.dom);
53826         }
53827         if(panel.setRegion){
53828             panel.setRegion(this);
53829         }
53830         this.panels.add(panel);
53831         el.setStyle("position", "absolute");
53832         if(!panel.background){
53833             this.setActivePanel(panel);
53834             if(this.config.initialSize && this.panels.getCount()==1){
53835                 this.resizeTo(this.config.initialSize);
53836             }
53837         }
53838         this.fireEvent("paneladded", this, panel);
53839         return panel;
53840     },
53841     
53842     /**
53843      * Returns true if the panel is in this region.
53844      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53845      * @return {Boolean}
53846      */
53847     hasPanel : function(panel){
53848         if(typeof panel == "object"){ // must be panel obj
53849             panel = panel.getId();
53850         }
53851         return this.getPanel(panel) ? true : false;
53852     },
53853     
53854     /**
53855      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53856      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53857      * @param {Boolean} preservePanel Overrides the config preservePanel option
53858      * @return {Roo.ContentPanel} The panel that was removed
53859      */
53860     remove : function(panel, preservePanel){
53861         panel = this.getPanel(panel);
53862         if(!panel){
53863             return null;
53864         }
53865         var e = {};
53866         this.fireEvent("beforeremove", this, panel, e);
53867         if(e.cancel === true){
53868             return null;
53869         }
53870         var panelId = panel.getId();
53871         this.panels.removeKey(panelId);
53872         return panel;
53873     },
53874     
53875     /**
53876      * Returns the panel specified or null if it's not in this region.
53877      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53878      * @return {Roo.ContentPanel}
53879      */
53880     getPanel : function(id){
53881         if(typeof id == "object"){ // must be panel obj
53882             return id;
53883         }
53884         return this.panels.get(id);
53885     },
53886     
53887     /**
53888      * Returns this regions position (north/south/east/west/center).
53889      * @return {String} 
53890      */
53891     getPosition: function(){
53892         return this.position;    
53893     }
53894 });/*
53895  * Based on:
53896  * Ext JS Library 1.1.1
53897  * Copyright(c) 2006-2007, Ext JS, LLC.
53898  *
53899  * Originally Released Under LGPL - original licence link has changed is not relivant.
53900  *
53901  * Fork - LGPL
53902  * <script type="text/javascript">
53903  */
53904  
53905 /**
53906  * @class Roo.LayoutRegion
53907  * @extends Roo.BasicLayoutRegion
53908  * This class represents a region in a layout manager.
53909  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53910  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53911  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53912  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53913  * @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})
53914  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53915  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53916  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53917  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53918  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53919  * @cfg {String}    title           The title for the region (overrides panel titles)
53920  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53921  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53922  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53923  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53924  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53925  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53926  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53927  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53928  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53929  * @cfg {Boolean}   showPin         True to show a pin button
53930  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53931  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53932  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53933  * @cfg {Number}    width           For East/West panels
53934  * @cfg {Number}    height          For North/South panels
53935  * @cfg {Boolean}   split           To show the splitter
53936  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53937  */
53938 Roo.LayoutRegion = function(mgr, config, pos){
53939     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53940     var dh = Roo.DomHelper;
53941     /** This region's container element 
53942     * @type Roo.Element */
53943     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53944     /** This region's title element 
53945     * @type Roo.Element */
53946
53947     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53948         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53949         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53950     ]}, true);
53951     this.titleEl.enableDisplayMode();
53952     /** This region's title text element 
53953     * @type HTMLElement */
53954     this.titleTextEl = this.titleEl.dom.firstChild;
53955     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53956     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53957     this.closeBtn.enableDisplayMode();
53958     this.closeBtn.on("click", this.closeClicked, this);
53959     this.closeBtn.hide();
53960
53961     this.createBody(config);
53962     this.visible = true;
53963     this.collapsed = false;
53964
53965     if(config.hideWhenEmpty){
53966         this.hide();
53967         this.on("paneladded", this.validateVisibility, this);
53968         this.on("panelremoved", this.validateVisibility, this);
53969     }
53970     this.applyConfig(config);
53971 };
53972
53973 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53974
53975     createBody : function(){
53976         /** This region's body element 
53977         * @type Roo.Element */
53978         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53979     },
53980
53981     applyConfig : function(c){
53982         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53983             var dh = Roo.DomHelper;
53984             if(c.titlebar !== false){
53985                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53986                 this.collapseBtn.on("click", this.collapse, this);
53987                 this.collapseBtn.enableDisplayMode();
53988
53989                 if(c.showPin === true || this.showPin){
53990                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53991                     this.stickBtn.enableDisplayMode();
53992                     this.stickBtn.on("click", this.expand, this);
53993                     this.stickBtn.hide();
53994                 }
53995             }
53996             /** This region's collapsed element
53997             * @type Roo.Element */
53998             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53999                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54000             ]}, true);
54001             if(c.floatable !== false){
54002                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54003                this.collapsedEl.on("click", this.collapseClick, this);
54004             }
54005
54006             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54007                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54008                    id: "message", unselectable: "on", style:{"float":"left"}});
54009                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54010              }
54011             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54012             this.expandBtn.on("click", this.expand, this);
54013         }
54014         if(this.collapseBtn){
54015             this.collapseBtn.setVisible(c.collapsible == true);
54016         }
54017         this.cmargins = c.cmargins || this.cmargins ||
54018                          (this.position == "west" || this.position == "east" ?
54019                              {top: 0, left: 2, right:2, bottom: 0} :
54020                              {top: 2, left: 0, right:0, bottom: 2});
54021         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54022         this.bottomTabs = c.tabPosition != "top";
54023         this.autoScroll = c.autoScroll || false;
54024         if(this.autoScroll){
54025             this.bodyEl.setStyle("overflow", "auto");
54026         }else{
54027             this.bodyEl.setStyle("overflow", "hidden");
54028         }
54029         //if(c.titlebar !== false){
54030             if((!c.titlebar && !c.title) || c.titlebar === false){
54031                 this.titleEl.hide();
54032             }else{
54033                 this.titleEl.show();
54034                 if(c.title){
54035                     this.titleTextEl.innerHTML = c.title;
54036                 }
54037             }
54038         //}
54039         this.duration = c.duration || .30;
54040         this.slideDuration = c.slideDuration || .45;
54041         this.config = c;
54042         if(c.collapsed){
54043             this.collapse(true);
54044         }
54045         if(c.hidden){
54046             this.hide();
54047         }
54048     },
54049     /**
54050      * Returns true if this region is currently visible.
54051      * @return {Boolean}
54052      */
54053     isVisible : function(){
54054         return this.visible;
54055     },
54056
54057     /**
54058      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54059      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54060      */
54061     setCollapsedTitle : function(title){
54062         title = title || "&#160;";
54063         if(this.collapsedTitleTextEl){
54064             this.collapsedTitleTextEl.innerHTML = title;
54065         }
54066     },
54067
54068     getBox : function(){
54069         var b;
54070         if(!this.collapsed){
54071             b = this.el.getBox(false, true);
54072         }else{
54073             b = this.collapsedEl.getBox(false, true);
54074         }
54075         return b;
54076     },
54077
54078     getMargins : function(){
54079         return this.collapsed ? this.cmargins : this.margins;
54080     },
54081
54082     highlight : function(){
54083         this.el.addClass("x-layout-panel-dragover");
54084     },
54085
54086     unhighlight : function(){
54087         this.el.removeClass("x-layout-panel-dragover");
54088     },
54089
54090     updateBox : function(box){
54091         this.box = box;
54092         if(!this.collapsed){
54093             this.el.dom.style.left = box.x + "px";
54094             this.el.dom.style.top = box.y + "px";
54095             this.updateBody(box.width, box.height);
54096         }else{
54097             this.collapsedEl.dom.style.left = box.x + "px";
54098             this.collapsedEl.dom.style.top = box.y + "px";
54099             this.collapsedEl.setSize(box.width, box.height);
54100         }
54101         if(this.tabs){
54102             this.tabs.autoSizeTabs();
54103         }
54104     },
54105
54106     updateBody : function(w, h){
54107         if(w !== null){
54108             this.el.setWidth(w);
54109             w -= this.el.getBorderWidth("rl");
54110             if(this.config.adjustments){
54111                 w += this.config.adjustments[0];
54112             }
54113         }
54114         if(h !== null){
54115             this.el.setHeight(h);
54116             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54117             h -= this.el.getBorderWidth("tb");
54118             if(this.config.adjustments){
54119                 h += this.config.adjustments[1];
54120             }
54121             this.bodyEl.setHeight(h);
54122             if(this.tabs){
54123                 h = this.tabs.syncHeight(h);
54124             }
54125         }
54126         if(this.panelSize){
54127             w = w !== null ? w : this.panelSize.width;
54128             h = h !== null ? h : this.panelSize.height;
54129         }
54130         if(this.activePanel){
54131             var el = this.activePanel.getEl();
54132             w = w !== null ? w : el.getWidth();
54133             h = h !== null ? h : el.getHeight();
54134             this.panelSize = {width: w, height: h};
54135             this.activePanel.setSize(w, h);
54136         }
54137         if(Roo.isIE && this.tabs){
54138             this.tabs.el.repaint();
54139         }
54140     },
54141
54142     /**
54143      * Returns the container element for this region.
54144      * @return {Roo.Element}
54145      */
54146     getEl : function(){
54147         return this.el;
54148     },
54149
54150     /**
54151      * Hides this region.
54152      */
54153     hide : function(){
54154         if(!this.collapsed){
54155             this.el.dom.style.left = "-2000px";
54156             this.el.hide();
54157         }else{
54158             this.collapsedEl.dom.style.left = "-2000px";
54159             this.collapsedEl.hide();
54160         }
54161         this.visible = false;
54162         this.fireEvent("visibilitychange", this, false);
54163     },
54164
54165     /**
54166      * Shows this region if it was previously hidden.
54167      */
54168     show : function(){
54169         if(!this.collapsed){
54170             this.el.show();
54171         }else{
54172             this.collapsedEl.show();
54173         }
54174         this.visible = true;
54175         this.fireEvent("visibilitychange", this, true);
54176     },
54177
54178     closeClicked : function(){
54179         if(this.activePanel){
54180             this.remove(this.activePanel);
54181         }
54182     },
54183
54184     collapseClick : function(e){
54185         if(this.isSlid){
54186            e.stopPropagation();
54187            this.slideIn();
54188         }else{
54189            e.stopPropagation();
54190            this.slideOut();
54191         }
54192     },
54193
54194     /**
54195      * Collapses this region.
54196      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54197      */
54198     collapse : function(skipAnim, skipCheck){
54199         if(this.collapsed) {
54200             return;
54201         }
54202         
54203         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54204             
54205             this.collapsed = true;
54206             if(this.split){
54207                 this.split.el.hide();
54208             }
54209             if(this.config.animate && skipAnim !== true){
54210                 this.fireEvent("invalidated", this);
54211                 this.animateCollapse();
54212             }else{
54213                 this.el.setLocation(-20000,-20000);
54214                 this.el.hide();
54215                 this.collapsedEl.show();
54216                 this.fireEvent("collapsed", this);
54217                 this.fireEvent("invalidated", this);
54218             }
54219         }
54220         
54221     },
54222
54223     animateCollapse : function(){
54224         // overridden
54225     },
54226
54227     /**
54228      * Expands this region if it was previously collapsed.
54229      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54230      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54231      */
54232     expand : function(e, skipAnim){
54233         if(e) {
54234             e.stopPropagation();
54235         }
54236         if(!this.collapsed || this.el.hasActiveFx()) {
54237             return;
54238         }
54239         if(this.isSlid){
54240             this.afterSlideIn();
54241             skipAnim = true;
54242         }
54243         this.collapsed = false;
54244         if(this.config.animate && skipAnim !== true){
54245             this.animateExpand();
54246         }else{
54247             this.el.show();
54248             if(this.split){
54249                 this.split.el.show();
54250             }
54251             this.collapsedEl.setLocation(-2000,-2000);
54252             this.collapsedEl.hide();
54253             this.fireEvent("invalidated", this);
54254             this.fireEvent("expanded", this);
54255         }
54256     },
54257
54258     animateExpand : function(){
54259         // overridden
54260     },
54261
54262     initTabs : function()
54263     {
54264         this.bodyEl.setStyle("overflow", "hidden");
54265         var ts = new Roo.TabPanel(
54266                 this.bodyEl.dom,
54267                 {
54268                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54269                     disableTooltips: this.config.disableTabTips,
54270                     toolbar : this.config.toolbar
54271                 }
54272         );
54273         if(this.config.hideTabs){
54274             ts.stripWrap.setDisplayed(false);
54275         }
54276         this.tabs = ts;
54277         ts.resizeTabs = this.config.resizeTabs === true;
54278         ts.minTabWidth = this.config.minTabWidth || 40;
54279         ts.maxTabWidth = this.config.maxTabWidth || 250;
54280         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54281         ts.monitorResize = false;
54282         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54283         ts.bodyEl.addClass('x-layout-tabs-body');
54284         this.panels.each(this.initPanelAsTab, this);
54285     },
54286
54287     initPanelAsTab : function(panel){
54288         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54289                     this.config.closeOnTab && panel.isClosable());
54290         if(panel.tabTip !== undefined){
54291             ti.setTooltip(panel.tabTip);
54292         }
54293         ti.on("activate", function(){
54294               this.setActivePanel(panel);
54295         }, this);
54296         if(this.config.closeOnTab){
54297             ti.on("beforeclose", function(t, e){
54298                 e.cancel = true;
54299                 this.remove(panel);
54300             }, this);
54301         }
54302         return ti;
54303     },
54304
54305     updatePanelTitle : function(panel, title){
54306         if(this.activePanel == panel){
54307             this.updateTitle(title);
54308         }
54309         if(this.tabs){
54310             var ti = this.tabs.getTab(panel.getEl().id);
54311             ti.setText(title);
54312             if(panel.tabTip !== undefined){
54313                 ti.setTooltip(panel.tabTip);
54314             }
54315         }
54316     },
54317
54318     updateTitle : function(title){
54319         if(this.titleTextEl && !this.config.title){
54320             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54321         }
54322     },
54323
54324     setActivePanel : function(panel){
54325         panel = this.getPanel(panel);
54326         if(this.activePanel && this.activePanel != panel){
54327             this.activePanel.setActiveState(false);
54328         }
54329         this.activePanel = panel;
54330         panel.setActiveState(true);
54331         if(this.panelSize){
54332             panel.setSize(this.panelSize.width, this.panelSize.height);
54333         }
54334         if(this.closeBtn){
54335             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54336         }
54337         this.updateTitle(panel.getTitle());
54338         if(this.tabs){
54339             this.fireEvent("invalidated", this);
54340         }
54341         this.fireEvent("panelactivated", this, panel);
54342     },
54343
54344     /**
54345      * Shows the specified panel.
54346      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54347      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54348      */
54349     showPanel : function(panel)
54350     {
54351         panel = this.getPanel(panel);
54352         if(panel){
54353             if(this.tabs){
54354                 var tab = this.tabs.getTab(panel.getEl().id);
54355                 if(tab.isHidden()){
54356                     this.tabs.unhideTab(tab.id);
54357                 }
54358                 tab.activate();
54359             }else{
54360                 this.setActivePanel(panel);
54361             }
54362         }
54363         return panel;
54364     },
54365
54366     /**
54367      * Get the active panel for this region.
54368      * @return {Roo.ContentPanel} The active panel or null
54369      */
54370     getActivePanel : function(){
54371         return this.activePanel;
54372     },
54373
54374     validateVisibility : function(){
54375         if(this.panels.getCount() < 1){
54376             this.updateTitle("&#160;");
54377             this.closeBtn.hide();
54378             this.hide();
54379         }else{
54380             if(!this.isVisible()){
54381                 this.show();
54382             }
54383         }
54384     },
54385
54386     /**
54387      * Adds the passed ContentPanel(s) to this region.
54388      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54389      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54390      */
54391     add : function(panel){
54392         if(arguments.length > 1){
54393             for(var i = 0, len = arguments.length; i < len; i++) {
54394                 this.add(arguments[i]);
54395             }
54396             return null;
54397         }
54398         if(this.hasPanel(panel)){
54399             this.showPanel(panel);
54400             return panel;
54401         }
54402         panel.setRegion(this);
54403         this.panels.add(panel);
54404         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54405             this.bodyEl.dom.appendChild(panel.getEl().dom);
54406             if(panel.background !== true){
54407                 this.setActivePanel(panel);
54408             }
54409             this.fireEvent("paneladded", this, panel);
54410             return panel;
54411         }
54412         if(!this.tabs){
54413             this.initTabs();
54414         }else{
54415             this.initPanelAsTab(panel);
54416         }
54417         if(panel.background !== true){
54418             this.tabs.activate(panel.getEl().id);
54419         }
54420         this.fireEvent("paneladded", this, panel);
54421         return panel;
54422     },
54423
54424     /**
54425      * Hides the tab for the specified panel.
54426      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54427      */
54428     hidePanel : function(panel){
54429         if(this.tabs && (panel = this.getPanel(panel))){
54430             this.tabs.hideTab(panel.getEl().id);
54431         }
54432     },
54433
54434     /**
54435      * Unhides the tab for a previously hidden panel.
54436      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54437      */
54438     unhidePanel : function(panel){
54439         if(this.tabs && (panel = this.getPanel(panel))){
54440             this.tabs.unhideTab(panel.getEl().id);
54441         }
54442     },
54443
54444     clearPanels : function(){
54445         while(this.panels.getCount() > 0){
54446              this.remove(this.panels.first());
54447         }
54448     },
54449
54450     /**
54451      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54452      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54453      * @param {Boolean} preservePanel Overrides the config preservePanel option
54454      * @return {Roo.ContentPanel} The panel that was removed
54455      */
54456     remove : function(panel, preservePanel){
54457         panel = this.getPanel(panel);
54458         if(!panel){
54459             return null;
54460         }
54461         var e = {};
54462         this.fireEvent("beforeremove", this, panel, e);
54463         if(e.cancel === true){
54464             return null;
54465         }
54466         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54467         var panelId = panel.getId();
54468         this.panels.removeKey(panelId);
54469         if(preservePanel){
54470             document.body.appendChild(panel.getEl().dom);
54471         }
54472         if(this.tabs){
54473             this.tabs.removeTab(panel.getEl().id);
54474         }else if (!preservePanel){
54475             this.bodyEl.dom.removeChild(panel.getEl().dom);
54476         }
54477         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54478             var p = this.panels.first();
54479             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54480             tempEl.appendChild(p.getEl().dom);
54481             this.bodyEl.update("");
54482             this.bodyEl.dom.appendChild(p.getEl().dom);
54483             tempEl = null;
54484             this.updateTitle(p.getTitle());
54485             this.tabs = null;
54486             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54487             this.setActivePanel(p);
54488         }
54489         panel.setRegion(null);
54490         if(this.activePanel == panel){
54491             this.activePanel = null;
54492         }
54493         if(this.config.autoDestroy !== false && preservePanel !== true){
54494             try{panel.destroy();}catch(e){}
54495         }
54496         this.fireEvent("panelremoved", this, panel);
54497         return panel;
54498     },
54499
54500     /**
54501      * Returns the TabPanel component used by this region
54502      * @return {Roo.TabPanel}
54503      */
54504     getTabs : function(){
54505         return this.tabs;
54506     },
54507
54508     createTool : function(parentEl, className){
54509         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54510             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54511         btn.addClassOnOver("x-layout-tools-button-over");
54512         return btn;
54513     }
54514 });/*
54515  * Based on:
54516  * Ext JS Library 1.1.1
54517  * Copyright(c) 2006-2007, Ext JS, LLC.
54518  *
54519  * Originally Released Under LGPL - original licence link has changed is not relivant.
54520  *
54521  * Fork - LGPL
54522  * <script type="text/javascript">
54523  */
54524  
54525
54526
54527 /**
54528  * @class Roo.SplitLayoutRegion
54529  * @extends Roo.LayoutRegion
54530  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54531  */
54532 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54533     this.cursor = cursor;
54534     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54535 };
54536
54537 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54538     splitTip : "Drag to resize.",
54539     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54540     useSplitTips : false,
54541
54542     applyConfig : function(config){
54543         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54544         if(config.split){
54545             if(!this.split){
54546                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54547                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54548                 /** The SplitBar for this region 
54549                 * @type Roo.SplitBar */
54550                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54551                 this.split.on("moved", this.onSplitMove, this);
54552                 this.split.useShim = config.useShim === true;
54553                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
54554                 if(this.useSplitTips){
54555                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
54556                 }
54557                 if(config.collapsible){
54558                     this.split.el.on("dblclick", this.collapse,  this);
54559                 }
54560             }
54561             if(typeof config.minSize != "undefined"){
54562                 this.split.minSize = config.minSize;
54563             }
54564             if(typeof config.maxSize != "undefined"){
54565                 this.split.maxSize = config.maxSize;
54566             }
54567             if(config.hideWhenEmpty || config.hidden || config.collapsed){
54568                 this.hideSplitter();
54569             }
54570         }
54571     },
54572
54573     getHMaxSize : function(){
54574          var cmax = this.config.maxSize || 10000;
54575          var center = this.mgr.getRegion("center");
54576          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
54577     },
54578
54579     getVMaxSize : function(){
54580          var cmax = this.config.maxSize || 10000;
54581          var center = this.mgr.getRegion("center");
54582          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
54583     },
54584
54585     onSplitMove : function(split, newSize){
54586         this.fireEvent("resized", this, newSize);
54587     },
54588     
54589     /** 
54590      * Returns the {@link Roo.SplitBar} for this region.
54591      * @return {Roo.SplitBar}
54592      */
54593     getSplitBar : function(){
54594         return this.split;
54595     },
54596     
54597     hide : function(){
54598         this.hideSplitter();
54599         Roo.SplitLayoutRegion.superclass.hide.call(this);
54600     },
54601
54602     hideSplitter : function(){
54603         if(this.split){
54604             this.split.el.setLocation(-2000,-2000);
54605             this.split.el.hide();
54606         }
54607     },
54608
54609     show : function(){
54610         if(this.split){
54611             this.split.el.show();
54612         }
54613         Roo.SplitLayoutRegion.superclass.show.call(this);
54614     },
54615     
54616     beforeSlide: function(){
54617         if(Roo.isGecko){// firefox overflow auto bug workaround
54618             this.bodyEl.clip();
54619             if(this.tabs) {
54620                 this.tabs.bodyEl.clip();
54621             }
54622             if(this.activePanel){
54623                 this.activePanel.getEl().clip();
54624                 
54625                 if(this.activePanel.beforeSlide){
54626                     this.activePanel.beforeSlide();
54627                 }
54628             }
54629         }
54630     },
54631     
54632     afterSlide : function(){
54633         if(Roo.isGecko){// firefox overflow auto bug workaround
54634             this.bodyEl.unclip();
54635             if(this.tabs) {
54636                 this.tabs.bodyEl.unclip();
54637             }
54638             if(this.activePanel){
54639                 this.activePanel.getEl().unclip();
54640                 if(this.activePanel.afterSlide){
54641                     this.activePanel.afterSlide();
54642                 }
54643             }
54644         }
54645     },
54646
54647     initAutoHide : function(){
54648         if(this.autoHide !== false){
54649             if(!this.autoHideHd){
54650                 var st = new Roo.util.DelayedTask(this.slideIn, this);
54651                 this.autoHideHd = {
54652                     "mouseout": function(e){
54653                         if(!e.within(this.el, true)){
54654                             st.delay(500);
54655                         }
54656                     },
54657                     "mouseover" : function(e){
54658                         st.cancel();
54659                     },
54660                     scope : this
54661                 };
54662             }
54663             this.el.on(this.autoHideHd);
54664         }
54665     },
54666
54667     clearAutoHide : function(){
54668         if(this.autoHide !== false){
54669             this.el.un("mouseout", this.autoHideHd.mouseout);
54670             this.el.un("mouseover", this.autoHideHd.mouseover);
54671         }
54672     },
54673
54674     clearMonitor : function(){
54675         Roo.get(document).un("click", this.slideInIf, this);
54676     },
54677
54678     // these names are backwards but not changed for compat
54679     slideOut : function(){
54680         if(this.isSlid || this.el.hasActiveFx()){
54681             return;
54682         }
54683         this.isSlid = true;
54684         if(this.collapseBtn){
54685             this.collapseBtn.hide();
54686         }
54687         this.closeBtnState = this.closeBtn.getStyle('display');
54688         this.closeBtn.hide();
54689         if(this.stickBtn){
54690             this.stickBtn.show();
54691         }
54692         this.el.show();
54693         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
54694         this.beforeSlide();
54695         this.el.setStyle("z-index", 10001);
54696         this.el.slideIn(this.getSlideAnchor(), {
54697             callback: function(){
54698                 this.afterSlide();
54699                 this.initAutoHide();
54700                 Roo.get(document).on("click", this.slideInIf, this);
54701                 this.fireEvent("slideshow", this);
54702             },
54703             scope: this,
54704             block: true
54705         });
54706     },
54707
54708     afterSlideIn : function(){
54709         this.clearAutoHide();
54710         this.isSlid = false;
54711         this.clearMonitor();
54712         this.el.setStyle("z-index", "");
54713         if(this.collapseBtn){
54714             this.collapseBtn.show();
54715         }
54716         this.closeBtn.setStyle('display', this.closeBtnState);
54717         if(this.stickBtn){
54718             this.stickBtn.hide();
54719         }
54720         this.fireEvent("slidehide", this);
54721     },
54722
54723     slideIn : function(cb){
54724         if(!this.isSlid || this.el.hasActiveFx()){
54725             Roo.callback(cb);
54726             return;
54727         }
54728         this.isSlid = false;
54729         this.beforeSlide();
54730         this.el.slideOut(this.getSlideAnchor(), {
54731             callback: function(){
54732                 this.el.setLeftTop(-10000, -10000);
54733                 this.afterSlide();
54734                 this.afterSlideIn();
54735                 Roo.callback(cb);
54736             },
54737             scope: this,
54738             block: true
54739         });
54740     },
54741     
54742     slideInIf : function(e){
54743         if(!e.within(this.el)){
54744             this.slideIn();
54745         }
54746     },
54747
54748     animateCollapse : function(){
54749         this.beforeSlide();
54750         this.el.setStyle("z-index", 20000);
54751         var anchor = this.getSlideAnchor();
54752         this.el.slideOut(anchor, {
54753             callback : function(){
54754                 this.el.setStyle("z-index", "");
54755                 this.collapsedEl.slideIn(anchor, {duration:.3});
54756                 this.afterSlide();
54757                 this.el.setLocation(-10000,-10000);
54758                 this.el.hide();
54759                 this.fireEvent("collapsed", this);
54760             },
54761             scope: this,
54762             block: true
54763         });
54764     },
54765
54766     animateExpand : function(){
54767         this.beforeSlide();
54768         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54769         this.el.setStyle("z-index", 20000);
54770         this.collapsedEl.hide({
54771             duration:.1
54772         });
54773         this.el.slideIn(this.getSlideAnchor(), {
54774             callback : function(){
54775                 this.el.setStyle("z-index", "");
54776                 this.afterSlide();
54777                 if(this.split){
54778                     this.split.el.show();
54779                 }
54780                 this.fireEvent("invalidated", this);
54781                 this.fireEvent("expanded", this);
54782             },
54783             scope: this,
54784             block: true
54785         });
54786     },
54787
54788     anchors : {
54789         "west" : "left",
54790         "east" : "right",
54791         "north" : "top",
54792         "south" : "bottom"
54793     },
54794
54795     sanchors : {
54796         "west" : "l",
54797         "east" : "r",
54798         "north" : "t",
54799         "south" : "b"
54800     },
54801
54802     canchors : {
54803         "west" : "tl-tr",
54804         "east" : "tr-tl",
54805         "north" : "tl-bl",
54806         "south" : "bl-tl"
54807     },
54808
54809     getAnchor : function(){
54810         return this.anchors[this.position];
54811     },
54812
54813     getCollapseAnchor : function(){
54814         return this.canchors[this.position];
54815     },
54816
54817     getSlideAnchor : function(){
54818         return this.sanchors[this.position];
54819     },
54820
54821     getAlignAdj : function(){
54822         var cm = this.cmargins;
54823         switch(this.position){
54824             case "west":
54825                 return [0, 0];
54826             break;
54827             case "east":
54828                 return [0, 0];
54829             break;
54830             case "north":
54831                 return [0, 0];
54832             break;
54833             case "south":
54834                 return [0, 0];
54835             break;
54836         }
54837     },
54838
54839     getExpandAdj : function(){
54840         var c = this.collapsedEl, cm = this.cmargins;
54841         switch(this.position){
54842             case "west":
54843                 return [-(cm.right+c.getWidth()+cm.left), 0];
54844             break;
54845             case "east":
54846                 return [cm.right+c.getWidth()+cm.left, 0];
54847             break;
54848             case "north":
54849                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54850             break;
54851             case "south":
54852                 return [0, cm.top+cm.bottom+c.getHeight()];
54853             break;
54854         }
54855     }
54856 });/*
54857  * Based on:
54858  * Ext JS Library 1.1.1
54859  * Copyright(c) 2006-2007, Ext JS, LLC.
54860  *
54861  * Originally Released Under LGPL - original licence link has changed is not relivant.
54862  *
54863  * Fork - LGPL
54864  * <script type="text/javascript">
54865  */
54866 /*
54867  * These classes are private internal classes
54868  */
54869 Roo.CenterLayoutRegion = function(mgr, config){
54870     Roo.LayoutRegion.call(this, mgr, config, "center");
54871     this.visible = true;
54872     this.minWidth = config.minWidth || 20;
54873     this.minHeight = config.minHeight || 20;
54874 };
54875
54876 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54877     hide : function(){
54878         // center panel can't be hidden
54879     },
54880     
54881     show : function(){
54882         // center panel can't be hidden
54883     },
54884     
54885     getMinWidth: function(){
54886         return this.minWidth;
54887     },
54888     
54889     getMinHeight: function(){
54890         return this.minHeight;
54891     }
54892 });
54893
54894
54895 Roo.NorthLayoutRegion = function(mgr, config){
54896     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54897     if(this.split){
54898         this.split.placement = Roo.SplitBar.TOP;
54899         this.split.orientation = Roo.SplitBar.VERTICAL;
54900         this.split.el.addClass("x-layout-split-v");
54901     }
54902     var size = config.initialSize || config.height;
54903     if(typeof size != "undefined"){
54904         this.el.setHeight(size);
54905     }
54906 };
54907 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54908     orientation: Roo.SplitBar.VERTICAL,
54909     getBox : function(){
54910         if(this.collapsed){
54911             return this.collapsedEl.getBox();
54912         }
54913         var box = this.el.getBox();
54914         if(this.split){
54915             box.height += this.split.el.getHeight();
54916         }
54917         return box;
54918     },
54919     
54920     updateBox : function(box){
54921         if(this.split && !this.collapsed){
54922             box.height -= this.split.el.getHeight();
54923             this.split.el.setLeft(box.x);
54924             this.split.el.setTop(box.y+box.height);
54925             this.split.el.setWidth(box.width);
54926         }
54927         if(this.collapsed){
54928             this.updateBody(box.width, null);
54929         }
54930         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54931     }
54932 });
54933
54934 Roo.SouthLayoutRegion = function(mgr, config){
54935     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54936     if(this.split){
54937         this.split.placement = Roo.SplitBar.BOTTOM;
54938         this.split.orientation = Roo.SplitBar.VERTICAL;
54939         this.split.el.addClass("x-layout-split-v");
54940     }
54941     var size = config.initialSize || config.height;
54942     if(typeof size != "undefined"){
54943         this.el.setHeight(size);
54944     }
54945 };
54946 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54947     orientation: Roo.SplitBar.VERTICAL,
54948     getBox : function(){
54949         if(this.collapsed){
54950             return this.collapsedEl.getBox();
54951         }
54952         var box = this.el.getBox();
54953         if(this.split){
54954             var sh = this.split.el.getHeight();
54955             box.height += sh;
54956             box.y -= sh;
54957         }
54958         return box;
54959     },
54960     
54961     updateBox : function(box){
54962         if(this.split && !this.collapsed){
54963             var sh = this.split.el.getHeight();
54964             box.height -= sh;
54965             box.y += sh;
54966             this.split.el.setLeft(box.x);
54967             this.split.el.setTop(box.y-sh);
54968             this.split.el.setWidth(box.width);
54969         }
54970         if(this.collapsed){
54971             this.updateBody(box.width, null);
54972         }
54973         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54974     }
54975 });
54976
54977 Roo.EastLayoutRegion = function(mgr, config){
54978     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54979     if(this.split){
54980         this.split.placement = Roo.SplitBar.RIGHT;
54981         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54982         this.split.el.addClass("x-layout-split-h");
54983     }
54984     var size = config.initialSize || config.width;
54985     if(typeof size != "undefined"){
54986         this.el.setWidth(size);
54987     }
54988 };
54989 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54990     orientation: Roo.SplitBar.HORIZONTAL,
54991     getBox : function(){
54992         if(this.collapsed){
54993             return this.collapsedEl.getBox();
54994         }
54995         var box = this.el.getBox();
54996         if(this.split){
54997             var sw = this.split.el.getWidth();
54998             box.width += sw;
54999             box.x -= sw;
55000         }
55001         return box;
55002     },
55003
55004     updateBox : function(box){
55005         if(this.split && !this.collapsed){
55006             var sw = this.split.el.getWidth();
55007             box.width -= sw;
55008             this.split.el.setLeft(box.x);
55009             this.split.el.setTop(box.y);
55010             this.split.el.setHeight(box.height);
55011             box.x += sw;
55012         }
55013         if(this.collapsed){
55014             this.updateBody(null, box.height);
55015         }
55016         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55017     }
55018 });
55019
55020 Roo.WestLayoutRegion = function(mgr, config){
55021     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55022     if(this.split){
55023         this.split.placement = Roo.SplitBar.LEFT;
55024         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55025         this.split.el.addClass("x-layout-split-h");
55026     }
55027     var size = config.initialSize || config.width;
55028     if(typeof size != "undefined"){
55029         this.el.setWidth(size);
55030     }
55031 };
55032 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55033     orientation: Roo.SplitBar.HORIZONTAL,
55034     getBox : function(){
55035         if(this.collapsed){
55036             return this.collapsedEl.getBox();
55037         }
55038         var box = this.el.getBox();
55039         if(this.split){
55040             box.width += this.split.el.getWidth();
55041         }
55042         return box;
55043     },
55044     
55045     updateBox : function(box){
55046         if(this.split && !this.collapsed){
55047             var sw = this.split.el.getWidth();
55048             box.width -= sw;
55049             this.split.el.setLeft(box.x+box.width);
55050             this.split.el.setTop(box.y);
55051             this.split.el.setHeight(box.height);
55052         }
55053         if(this.collapsed){
55054             this.updateBody(null, box.height);
55055         }
55056         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55057     }
55058 });
55059 /*
55060  * Based on:
55061  * Ext JS Library 1.1.1
55062  * Copyright(c) 2006-2007, Ext JS, LLC.
55063  *
55064  * Originally Released Under LGPL - original licence link has changed is not relivant.
55065  *
55066  * Fork - LGPL
55067  * <script type="text/javascript">
55068  */
55069  
55070  
55071 /*
55072  * Private internal class for reading and applying state
55073  */
55074 Roo.LayoutStateManager = function(layout){
55075      // default empty state
55076      this.state = {
55077         north: {},
55078         south: {},
55079         east: {},
55080         west: {}       
55081     };
55082 };
55083
55084 Roo.LayoutStateManager.prototype = {
55085     init : function(layout, provider){
55086         this.provider = provider;
55087         var state = provider.get(layout.id+"-layout-state");
55088         if(state){
55089             var wasUpdating = layout.isUpdating();
55090             if(!wasUpdating){
55091                 layout.beginUpdate();
55092             }
55093             for(var key in state){
55094                 if(typeof state[key] != "function"){
55095                     var rstate = state[key];
55096                     var r = layout.getRegion(key);
55097                     if(r && rstate){
55098                         if(rstate.size){
55099                             r.resizeTo(rstate.size);
55100                         }
55101                         if(rstate.collapsed == true){
55102                             r.collapse(true);
55103                         }else{
55104                             r.expand(null, true);
55105                         }
55106                     }
55107                 }
55108             }
55109             if(!wasUpdating){
55110                 layout.endUpdate();
55111             }
55112             this.state = state; 
55113         }
55114         this.layout = layout;
55115         layout.on("regionresized", this.onRegionResized, this);
55116         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55117         layout.on("regionexpanded", this.onRegionExpanded, this);
55118     },
55119     
55120     storeState : function(){
55121         this.provider.set(this.layout.id+"-layout-state", this.state);
55122     },
55123     
55124     onRegionResized : function(region, newSize){
55125         this.state[region.getPosition()].size = newSize;
55126         this.storeState();
55127     },
55128     
55129     onRegionCollapsed : function(region){
55130         this.state[region.getPosition()].collapsed = true;
55131         this.storeState();
55132     },
55133     
55134     onRegionExpanded : function(region){
55135         this.state[region.getPosition()].collapsed = false;
55136         this.storeState();
55137     }
55138 };/*
55139  * Based on:
55140  * Ext JS Library 1.1.1
55141  * Copyright(c) 2006-2007, Ext JS, LLC.
55142  *
55143  * Originally Released Under LGPL - original licence link has changed is not relivant.
55144  *
55145  * Fork - LGPL
55146  * <script type="text/javascript">
55147  */
55148 /**
55149  * @class Roo.ContentPanel
55150  * @extends Roo.util.Observable
55151  * @children Roo.form.Form Roo.JsonView Roo.View
55152  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55153  * A basic ContentPanel element.
55154  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55155  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55156  * @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
55157  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55158  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55159  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55160  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55161  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55162  * @cfg {String} title          The title for this panel
55163  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55164  * @cfg {String} url            Calls {@link #setUrl} with this value
55165  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55166  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55167  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55168  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55169  * @cfg {String}    style  Extra style to add to the content panel
55170  * @cfg {Roo.menu.Menu} menu  popup menu
55171
55172  * @constructor
55173  * Create a new ContentPanel.
55174  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55175  * @param {String/Object} config A string to set only the title or a config object
55176  * @param {String} content (optional) Set the HTML content for this panel
55177  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55178  */
55179 Roo.ContentPanel = function(el, config, content){
55180     
55181      
55182     /*
55183     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55184         config = el;
55185         el = Roo.id();
55186     }
55187     if (config && config.parentLayout) { 
55188         el = config.parentLayout.el.createChild(); 
55189     }
55190     */
55191     if(el.autoCreate){ // xtype is available if this is called from factory
55192         config = el;
55193         el = Roo.id();
55194     }
55195     this.el = Roo.get(el);
55196     if(!this.el && config && config.autoCreate){
55197         if(typeof config.autoCreate == "object"){
55198             if(!config.autoCreate.id){
55199                 config.autoCreate.id = config.id||el;
55200             }
55201             this.el = Roo.DomHelper.append(document.body,
55202                         config.autoCreate, true);
55203         }else{
55204             this.el = Roo.DomHelper.append(document.body,
55205                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55206         }
55207     }
55208     
55209     
55210     this.closable = false;
55211     this.loaded = false;
55212     this.active = false;
55213     if(typeof config == "string"){
55214         this.title = config;
55215     }else{
55216         Roo.apply(this, config);
55217     }
55218     
55219     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55220         this.wrapEl = this.el.wrap();
55221         this.toolbar.container = this.el.insertSibling(false, 'before');
55222         this.toolbar = new Roo.Toolbar(this.toolbar);
55223     }
55224     
55225     // xtype created footer. - not sure if will work as we normally have to render first..
55226     if (this.footer && !this.footer.el && this.footer.xtype) {
55227         if (!this.wrapEl) {
55228             this.wrapEl = this.el.wrap();
55229         }
55230     
55231         this.footer.container = this.wrapEl.createChild();
55232          
55233         this.footer = Roo.factory(this.footer, Roo);
55234         
55235     }
55236     
55237     if(this.resizeEl){
55238         this.resizeEl = Roo.get(this.resizeEl, true);
55239     }else{
55240         this.resizeEl = this.el;
55241     }
55242     // handle view.xtype
55243     
55244  
55245     
55246     
55247     this.addEvents({
55248         /**
55249          * @event activate
55250          * Fires when this panel is activated. 
55251          * @param {Roo.ContentPanel} this
55252          */
55253         "activate" : true,
55254         /**
55255          * @event deactivate
55256          * Fires when this panel is activated. 
55257          * @param {Roo.ContentPanel} this
55258          */
55259         "deactivate" : true,
55260
55261         /**
55262          * @event resize
55263          * Fires when this panel is resized if fitToFrame is true.
55264          * @param {Roo.ContentPanel} this
55265          * @param {Number} width The width after any component adjustments
55266          * @param {Number} height The height after any component adjustments
55267          */
55268         "resize" : true,
55269         
55270          /**
55271          * @event render
55272          * Fires when this tab is created
55273          * @param {Roo.ContentPanel} this
55274          */
55275         "render" : true
55276          
55277         
55278     });
55279     
55280
55281     
55282     
55283     if(this.autoScroll){
55284         this.resizeEl.setStyle("overflow", "auto");
55285     } else {
55286         // fix randome scrolling
55287         this.el.on('scroll', function() {
55288             Roo.log('fix random scolling');
55289             this.scrollTo('top',0); 
55290         });
55291     }
55292     content = content || this.content;
55293     if(content){
55294         this.setContent(content);
55295     }
55296     if(config && config.url){
55297         this.setUrl(this.url, this.params, this.loadOnce);
55298     }
55299     
55300     
55301     
55302     Roo.ContentPanel.superclass.constructor.call(this);
55303     
55304     if (this.view && typeof(this.view.xtype) != 'undefined') {
55305         this.view.el = this.el.appendChild(document.createElement("div"));
55306         this.view = Roo.factory(this.view); 
55307         this.view.render  &&  this.view.render(false, '');  
55308     }
55309     
55310     
55311     this.fireEvent('render', this);
55312 };
55313
55314 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55315     tabTip:'',
55316     setRegion : function(region){
55317         this.region = region;
55318         if(region){
55319            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55320         }else{
55321            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55322         } 
55323     },
55324     
55325     /**
55326      * Returns the toolbar for this Panel if one was configured. 
55327      * @return {Roo.Toolbar} 
55328      */
55329     getToolbar : function(){
55330         return this.toolbar;
55331     },
55332     
55333     setActiveState : function(active){
55334         this.active = active;
55335         if(!active){
55336             this.fireEvent("deactivate", this);
55337         }else{
55338             this.fireEvent("activate", this);
55339         }
55340     },
55341     /**
55342      * Updates this panel's element
55343      * @param {String} content The new content
55344      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55345     */
55346     setContent : function(content, loadScripts){
55347         this.el.update(content, loadScripts);
55348     },
55349
55350     ignoreResize : function(w, h){
55351         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55352             return true;
55353         }else{
55354             this.lastSize = {width: w, height: h};
55355             return false;
55356         }
55357     },
55358     /**
55359      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55360      * @return {Roo.UpdateManager} The UpdateManager
55361      */
55362     getUpdateManager : function(){
55363         return this.el.getUpdateManager();
55364     },
55365      /**
55366      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55367      * @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:
55368 <pre><code>
55369 panel.load({
55370     url: "your-url.php",
55371     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55372     callback: yourFunction,
55373     scope: yourObject, //(optional scope)
55374     discardUrl: false,
55375     nocache: false,
55376     text: "Loading...",
55377     timeout: 30,
55378     scripts: false
55379 });
55380 </code></pre>
55381      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55382      * 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.
55383      * @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}
55384      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55385      * @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.
55386      * @return {Roo.ContentPanel} this
55387      */
55388     load : function(){
55389         var um = this.el.getUpdateManager();
55390         um.update.apply(um, arguments);
55391         return this;
55392     },
55393
55394
55395     /**
55396      * 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.
55397      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55398      * @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)
55399      * @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)
55400      * @return {Roo.UpdateManager} The UpdateManager
55401      */
55402     setUrl : function(url, params, loadOnce){
55403         if(this.refreshDelegate){
55404             this.removeListener("activate", this.refreshDelegate);
55405         }
55406         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55407         this.on("activate", this.refreshDelegate);
55408         return this.el.getUpdateManager();
55409     },
55410     
55411     _handleRefresh : function(url, params, loadOnce){
55412         if(!loadOnce || !this.loaded){
55413             var updater = this.el.getUpdateManager();
55414             updater.update(url, params, this._setLoaded.createDelegate(this));
55415         }
55416     },
55417     
55418     _setLoaded : function(){
55419         this.loaded = true;
55420     }, 
55421     
55422     /**
55423      * Returns this panel's id
55424      * @return {String} 
55425      */
55426     getId : function(){
55427         return this.el.id;
55428     },
55429     
55430     /** 
55431      * Returns this panel's element - used by regiosn to add.
55432      * @return {Roo.Element} 
55433      */
55434     getEl : function(){
55435         return this.wrapEl || this.el;
55436     },
55437     
55438     adjustForComponents : function(width, height)
55439     {
55440         //Roo.log('adjustForComponents ');
55441         if(this.resizeEl != this.el){
55442             width -= this.el.getFrameWidth('lr');
55443             height -= this.el.getFrameWidth('tb');
55444         }
55445         if(this.toolbar){
55446             var te = this.toolbar.getEl();
55447             height -= te.getHeight();
55448             te.setWidth(width);
55449         }
55450         if(this.footer){
55451             var te = this.footer.getEl();
55452             //Roo.log("footer:" + te.getHeight());
55453             
55454             height -= te.getHeight();
55455             te.setWidth(width);
55456         }
55457         
55458         
55459         if(this.adjustments){
55460             width += this.adjustments[0];
55461             height += this.adjustments[1];
55462         }
55463         return {"width": width, "height": height};
55464     },
55465     
55466     setSize : function(width, height){
55467         if(this.fitToFrame && !this.ignoreResize(width, height)){
55468             if(this.fitContainer && this.resizeEl != this.el){
55469                 this.el.setSize(width, height);
55470             }
55471             var size = this.adjustForComponents(width, height);
55472             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55473             this.fireEvent('resize', this, size.width, size.height);
55474         }
55475     },
55476     
55477     /**
55478      * Returns this panel's title
55479      * @return {String} 
55480      */
55481     getTitle : function(){
55482         return this.title;
55483     },
55484     
55485     /**
55486      * Set this panel's title
55487      * @param {String} title
55488      */
55489     setTitle : function(title){
55490         this.title = title;
55491         if(this.region){
55492             this.region.updatePanelTitle(this, title);
55493         }
55494     },
55495     
55496     /**
55497      * Returns true is this panel was configured to be closable
55498      * @return {Boolean} 
55499      */
55500     isClosable : function(){
55501         return this.closable;
55502     },
55503     
55504     beforeSlide : function(){
55505         this.el.clip();
55506         this.resizeEl.clip();
55507     },
55508     
55509     afterSlide : function(){
55510         this.el.unclip();
55511         this.resizeEl.unclip();
55512     },
55513     
55514     /**
55515      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55516      *   Will fail silently if the {@link #setUrl} method has not been called.
55517      *   This does not activate the panel, just updates its content.
55518      */
55519     refresh : function(){
55520         if(this.refreshDelegate){
55521            this.loaded = false;
55522            this.refreshDelegate();
55523         }
55524     },
55525     
55526     /**
55527      * Destroys this panel
55528      */
55529     destroy : function(){
55530         this.el.removeAllListeners();
55531         var tempEl = document.createElement("span");
55532         tempEl.appendChild(this.el.dom);
55533         tempEl.innerHTML = "";
55534         this.el.remove();
55535         this.el = null;
55536     },
55537     
55538     /**
55539      * form - if the content panel contains a form - this is a reference to it.
55540      * @type {Roo.form.Form}
55541      */
55542     form : false,
55543     /**
55544      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55545      *    This contains a reference to it.
55546      * @type {Roo.View}
55547      */
55548     view : false,
55549     
55550       /**
55551      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
55552      * <pre><code>
55553
55554 layout.addxtype({
55555        xtype : 'Form',
55556        items: [ .... ]
55557    }
55558 );
55559
55560 </code></pre>
55561      * @param {Object} cfg Xtype definition of item to add.
55562      */
55563     
55564     addxtype : function(cfg) {
55565         // add form..
55566         if (cfg.xtype.match(/^Form$/)) {
55567             
55568             var el;
55569             //if (this.footer) {
55570             //    el = this.footer.container.insertSibling(false, 'before');
55571             //} else {
55572                 el = this.el.createChild();
55573             //}
55574
55575             this.form = new  Roo.form.Form(cfg);
55576             
55577             
55578             if ( this.form.allItems.length) {
55579                 this.form.render(el.dom);
55580             }
55581             return this.form;
55582         }
55583         // should only have one of theses..
55584         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
55585             // views.. should not be just added - used named prop 'view''
55586             
55587             cfg.el = this.el.appendChild(document.createElement("div"));
55588             // factory?
55589             
55590             var ret = new Roo.factory(cfg);
55591              
55592              ret.render && ret.render(false, ''); // render blank..
55593             this.view = ret;
55594             return ret;
55595         }
55596         return false;
55597     }
55598 });
55599
55600 /**
55601  * @class Roo.GridPanel
55602  * @extends Roo.ContentPanel
55603  * @constructor
55604  * Create a new GridPanel.
55605  * @param {Roo.grid.Grid} grid The grid for this panel
55606  * @param {String/Object} config A string to set only the panel's title, or a config object
55607  */
55608 Roo.GridPanel = function(grid, config){
55609     
55610   
55611     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
55612         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
55613         
55614     this.wrapper.dom.appendChild(grid.getGridEl().dom);
55615     
55616     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
55617     
55618     if(this.toolbar){
55619         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
55620     }
55621     // xtype created footer. - not sure if will work as we normally have to render first..
55622     if (this.footer && !this.footer.el && this.footer.xtype) {
55623         
55624         this.footer.container = this.grid.getView().getFooterPanel(true);
55625         this.footer.dataSource = this.grid.dataSource;
55626         this.footer = Roo.factory(this.footer, Roo);
55627         
55628     }
55629     
55630     grid.monitorWindowResize = false; // turn off autosizing
55631     grid.autoHeight = false;
55632     grid.autoWidth = false;
55633     this.grid = grid;
55634     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
55635 };
55636
55637 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
55638     getId : function(){
55639         return this.grid.id;
55640     },
55641     
55642     /**
55643      * Returns the grid for this panel
55644      * @return {Roo.grid.Grid} 
55645      */
55646     getGrid : function(){
55647         return this.grid;    
55648     },
55649     
55650     setSize : function(width, height){
55651         if(!this.ignoreResize(width, height)){
55652             var grid = this.grid;
55653             var size = this.adjustForComponents(width, height);
55654             grid.getGridEl().setSize(size.width, size.height);
55655             grid.autoSize();
55656         }
55657     },
55658     
55659     beforeSlide : function(){
55660         this.grid.getView().scroller.clip();
55661     },
55662     
55663     afterSlide : function(){
55664         this.grid.getView().scroller.unclip();
55665     },
55666     
55667     destroy : function(){
55668         this.grid.destroy();
55669         delete this.grid;
55670         Roo.GridPanel.superclass.destroy.call(this); 
55671     }
55672 });
55673
55674
55675 /**
55676  * @class Roo.NestedLayoutPanel
55677  * @extends Roo.ContentPanel
55678  * @constructor
55679  * Create a new NestedLayoutPanel.
55680  * 
55681  * 
55682  * @param {Roo.BorderLayout} layout [required] The layout for this panel
55683  * @param {String/Object} config A string to set only the title or a config object
55684  */
55685 Roo.NestedLayoutPanel = function(layout, config)
55686 {
55687     // construct with only one argument..
55688     /* FIXME - implement nicer consturctors
55689     if (layout.layout) {
55690         config = layout;
55691         layout = config.layout;
55692         delete config.layout;
55693     }
55694     if (layout.xtype && !layout.getEl) {
55695         // then layout needs constructing..
55696         layout = Roo.factory(layout, Roo);
55697     }
55698     */
55699     
55700     
55701     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
55702     
55703     layout.monitorWindowResize = false; // turn off autosizing
55704     this.layout = layout;
55705     this.layout.getEl().addClass("x-layout-nested-layout");
55706     
55707     
55708     
55709     
55710 };
55711
55712 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55713
55714     setSize : function(width, height){
55715         if(!this.ignoreResize(width, height)){
55716             var size = this.adjustForComponents(width, height);
55717             var el = this.layout.getEl();
55718             el.setSize(size.width, size.height);
55719             var touch = el.dom.offsetWidth;
55720             this.layout.layout();
55721             // ie requires a double layout on the first pass
55722             if(Roo.isIE && !this.initialized){
55723                 this.initialized = true;
55724                 this.layout.layout();
55725             }
55726         }
55727     },
55728     
55729     // activate all subpanels if not currently active..
55730     
55731     setActiveState : function(active){
55732         this.active = active;
55733         if(!active){
55734             this.fireEvent("deactivate", this);
55735             return;
55736         }
55737         
55738         this.fireEvent("activate", this);
55739         // not sure if this should happen before or after..
55740         if (!this.layout) {
55741             return; // should not happen..
55742         }
55743         var reg = false;
55744         for (var r in this.layout.regions) {
55745             reg = this.layout.getRegion(r);
55746             if (reg.getActivePanel()) {
55747                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55748                 reg.setActivePanel(reg.getActivePanel());
55749                 continue;
55750             }
55751             if (!reg.panels.length) {
55752                 continue;
55753             }
55754             reg.showPanel(reg.getPanel(0));
55755         }
55756         
55757         
55758         
55759         
55760     },
55761     
55762     /**
55763      * Returns the nested BorderLayout for this panel
55764      * @return {Roo.BorderLayout} 
55765      */
55766     getLayout : function(){
55767         return this.layout;
55768     },
55769     
55770      /**
55771      * Adds a xtype elements to the layout of the nested panel
55772      * <pre><code>
55773
55774 panel.addxtype({
55775        xtype : 'ContentPanel',
55776        region: 'west',
55777        items: [ .... ]
55778    }
55779 );
55780
55781 panel.addxtype({
55782         xtype : 'NestedLayoutPanel',
55783         region: 'west',
55784         layout: {
55785            center: { },
55786            west: { }   
55787         },
55788         items : [ ... list of content panels or nested layout panels.. ]
55789    }
55790 );
55791 </code></pre>
55792      * @param {Object} cfg Xtype definition of item to add.
55793      */
55794     addxtype : function(cfg) {
55795         return this.layout.addxtype(cfg);
55796     
55797     }
55798 });
55799
55800 Roo.ScrollPanel = function(el, config, content){
55801     config = config || {};
55802     config.fitToFrame = true;
55803     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55804     
55805     this.el.dom.style.overflow = "hidden";
55806     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55807     this.el.removeClass("x-layout-inactive-content");
55808     this.el.on("mousewheel", this.onWheel, this);
55809
55810     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55811     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55812     up.unselectable(); down.unselectable();
55813     up.on("click", this.scrollUp, this);
55814     down.on("click", this.scrollDown, this);
55815     up.addClassOnOver("x-scroller-btn-over");
55816     down.addClassOnOver("x-scroller-btn-over");
55817     up.addClassOnClick("x-scroller-btn-click");
55818     down.addClassOnClick("x-scroller-btn-click");
55819     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55820
55821     this.resizeEl = this.el;
55822     this.el = wrap; this.up = up; this.down = down;
55823 };
55824
55825 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55826     increment : 100,
55827     wheelIncrement : 5,
55828     scrollUp : function(){
55829         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55830     },
55831
55832     scrollDown : function(){
55833         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55834     },
55835
55836     afterScroll : function(){
55837         var el = this.resizeEl;
55838         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55839         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55840         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55841     },
55842
55843     setSize : function(){
55844         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55845         this.afterScroll();
55846     },
55847
55848     onWheel : function(e){
55849         var d = e.getWheelDelta();
55850         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55851         this.afterScroll();
55852         e.stopEvent();
55853     },
55854
55855     setContent : function(content, loadScripts){
55856         this.resizeEl.update(content, loadScripts);
55857     }
55858
55859 });
55860
55861
55862
55863 /**
55864  * @class Roo.TreePanel
55865  * @extends Roo.ContentPanel
55866  * Treepanel component
55867  * 
55868  * @constructor
55869  * Create a new TreePanel. - defaults to fit/scoll contents.
55870  * @param {String/Object} config A string to set only the panel's title, or a config object
55871  */
55872 Roo.TreePanel = function(config){
55873     var el = config.el;
55874     var tree = config.tree;
55875     delete config.tree; 
55876     delete config.el; // hopefull!
55877     
55878     // wrapper for IE7 strict & safari scroll issue
55879     
55880     var treeEl = el.createChild();
55881     config.resizeEl = treeEl;
55882     
55883     
55884     
55885     Roo.TreePanel.superclass.constructor.call(this, el, config);
55886  
55887  
55888     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55889     //console.log(tree);
55890     this.on('activate', function()
55891     {
55892         if (this.tree.rendered) {
55893             return;
55894         }
55895         //console.log('render tree');
55896         this.tree.render();
55897     });
55898     // this should not be needed.. - it's actually the 'el' that resizes?
55899     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55900     
55901     //this.on('resize',  function (cp, w, h) {
55902     //        this.tree.innerCt.setWidth(w);
55903     //        this.tree.innerCt.setHeight(h);
55904     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55905     //});
55906
55907         
55908     
55909 };
55910
55911 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55912     fitToFrame : true,
55913     autoScroll : true,
55914     /*
55915      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55916      */
55917     tree : false
55918
55919 });
55920
55921
55922
55923
55924
55925
55926
55927
55928
55929
55930
55931 /*
55932  * Based on:
55933  * Ext JS Library 1.1.1
55934  * Copyright(c) 2006-2007, Ext JS, LLC.
55935  *
55936  * Originally Released Under LGPL - original licence link has changed is not relivant.
55937  *
55938  * Fork - LGPL
55939  * <script type="text/javascript">
55940  */
55941  
55942
55943 /**
55944  * @class Roo.ReaderLayout
55945  * @extends Roo.BorderLayout
55946  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55947  * center region containing two nested regions (a top one for a list view and one for item preview below),
55948  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55949  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55950  * expedites the setup of the overall layout and regions for this common application style.
55951  * Example:
55952  <pre><code>
55953 var reader = new Roo.ReaderLayout();
55954 var CP = Roo.ContentPanel;  // shortcut for adding
55955
55956 reader.beginUpdate();
55957 reader.add("north", new CP("north", "North"));
55958 reader.add("west", new CP("west", {title: "West"}));
55959 reader.add("east", new CP("east", {title: "East"}));
55960
55961 reader.regions.listView.add(new CP("listView", "List"));
55962 reader.regions.preview.add(new CP("preview", "Preview"));
55963 reader.endUpdate();
55964 </code></pre>
55965 * @constructor
55966 * Create a new ReaderLayout
55967 * @param {Object} config Configuration options
55968 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55969 * document.body if omitted)
55970 */
55971 Roo.ReaderLayout = function(config, renderTo){
55972     var c = config || {size:{}};
55973     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55974         north: c.north !== false ? Roo.apply({
55975             split:false,
55976             initialSize: 32,
55977             titlebar: false
55978         }, c.north) : false,
55979         west: c.west !== false ? Roo.apply({
55980             split:true,
55981             initialSize: 200,
55982             minSize: 175,
55983             maxSize: 400,
55984             titlebar: true,
55985             collapsible: true,
55986             animate: true,
55987             margins:{left:5,right:0,bottom:5,top:5},
55988             cmargins:{left:5,right:5,bottom:5,top:5}
55989         }, c.west) : false,
55990         east: c.east !== false ? Roo.apply({
55991             split:true,
55992             initialSize: 200,
55993             minSize: 175,
55994             maxSize: 400,
55995             titlebar: true,
55996             collapsible: true,
55997             animate: true,
55998             margins:{left:0,right:5,bottom:5,top:5},
55999             cmargins:{left:5,right:5,bottom:5,top:5}
56000         }, c.east) : false,
56001         center: Roo.apply({
56002             tabPosition: 'top',
56003             autoScroll:false,
56004             closeOnTab: true,
56005             titlebar:false,
56006             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56007         }, c.center)
56008     });
56009
56010     this.el.addClass('x-reader');
56011
56012     this.beginUpdate();
56013
56014     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56015         south: c.preview !== false ? Roo.apply({
56016             split:true,
56017             initialSize: 200,
56018             minSize: 100,
56019             autoScroll:true,
56020             collapsible:true,
56021             titlebar: true,
56022             cmargins:{top:5,left:0, right:0, bottom:0}
56023         }, c.preview) : false,
56024         center: Roo.apply({
56025             autoScroll:false,
56026             titlebar:false,
56027             minHeight:200
56028         }, c.listView)
56029     });
56030     this.add('center', new Roo.NestedLayoutPanel(inner,
56031             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56032
56033     this.endUpdate();
56034
56035     this.regions.preview = inner.getRegion('south');
56036     this.regions.listView = inner.getRegion('center');
56037 };
56038
56039 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56040  * Based on:
56041  * Ext JS Library 1.1.1
56042  * Copyright(c) 2006-2007, Ext JS, LLC.
56043  *
56044  * Originally Released Under LGPL - original licence link has changed is not relivant.
56045  *
56046  * Fork - LGPL
56047  * <script type="text/javascript">
56048  */
56049  
56050 /**
56051  * @class Roo.grid.Grid
56052  * @extends Roo.util.Observable
56053  * This class represents the primary interface of a component based grid control.
56054  * <br><br>Usage:<pre><code>
56055  var grid = new Roo.grid.Grid("my-container-id", {
56056      ds: myDataStore,
56057      cm: myColModel,
56058      selModel: mySelectionModel,
56059      autoSizeColumns: true,
56060      monitorWindowResize: false,
56061      trackMouseOver: true
56062  });
56063  // set any options
56064  grid.render();
56065  * </code></pre>
56066  * <b>Common Problems:</b><br/>
56067  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56068  * element will correct this<br/>
56069  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56070  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56071  * are unpredictable.<br/>
56072  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56073  * grid to calculate dimensions/offsets.<br/>
56074   * @constructor
56075  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56076  * The container MUST have some type of size defined for the grid to fill. The container will be
56077  * automatically set to position relative if it isn't already.
56078  * @param {Object} config A config object that sets properties on this grid.
56079  */
56080 Roo.grid.Grid = function(container, config){
56081         // initialize the container
56082         this.container = Roo.get(container);
56083         this.container.update("");
56084         this.container.setStyle("overflow", "hidden");
56085     this.container.addClass('x-grid-container');
56086
56087     this.id = this.container.id;
56088
56089     Roo.apply(this, config);
56090     // check and correct shorthanded configs
56091     if(this.ds){
56092         this.dataSource = this.ds;
56093         delete this.ds;
56094     }
56095     if(this.cm){
56096         this.colModel = this.cm;
56097         delete this.cm;
56098     }
56099     if(this.sm){
56100         this.selModel = this.sm;
56101         delete this.sm;
56102     }
56103
56104     if (this.selModel) {
56105         this.selModel = Roo.factory(this.selModel, Roo.grid);
56106         this.sm = this.selModel;
56107         this.sm.xmodule = this.xmodule || false;
56108     }
56109     if (typeof(this.colModel.config) == 'undefined') {
56110         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56111         this.cm = this.colModel;
56112         this.cm.xmodule = this.xmodule || false;
56113     }
56114     if (this.dataSource) {
56115         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56116         this.ds = this.dataSource;
56117         this.ds.xmodule = this.xmodule || false;
56118          
56119     }
56120     
56121     
56122     
56123     if(this.width){
56124         this.container.setWidth(this.width);
56125     }
56126
56127     if(this.height){
56128         this.container.setHeight(this.height);
56129     }
56130     /** @private */
56131         this.addEvents({
56132         // raw events
56133         /**
56134          * @event click
56135          * The raw click event for the entire grid.
56136          * @param {Roo.EventObject} e
56137          */
56138         "click" : true,
56139         /**
56140          * @event dblclick
56141          * The raw dblclick event for the entire grid.
56142          * @param {Roo.EventObject} e
56143          */
56144         "dblclick" : true,
56145         /**
56146          * @event contextmenu
56147          * The raw contextmenu event for the entire grid.
56148          * @param {Roo.EventObject} e
56149          */
56150         "contextmenu" : true,
56151         /**
56152          * @event mousedown
56153          * The raw mousedown event for the entire grid.
56154          * @param {Roo.EventObject} e
56155          */
56156         "mousedown" : true,
56157         /**
56158          * @event mouseup
56159          * The raw mouseup event for the entire grid.
56160          * @param {Roo.EventObject} e
56161          */
56162         "mouseup" : true,
56163         /**
56164          * @event mouseover
56165          * The raw mouseover event for the entire grid.
56166          * @param {Roo.EventObject} e
56167          */
56168         "mouseover" : true,
56169         /**
56170          * @event mouseout
56171          * The raw mouseout event for the entire grid.
56172          * @param {Roo.EventObject} e
56173          */
56174         "mouseout" : true,
56175         /**
56176          * @event keypress
56177          * The raw keypress event for the entire grid.
56178          * @param {Roo.EventObject} e
56179          */
56180         "keypress" : true,
56181         /**
56182          * @event keydown
56183          * The raw keydown event for the entire grid.
56184          * @param {Roo.EventObject} e
56185          */
56186         "keydown" : true,
56187
56188         // custom events
56189
56190         /**
56191          * @event cellclick
56192          * Fires when a cell is clicked
56193          * @param {Grid} this
56194          * @param {Number} rowIndex
56195          * @param {Number} columnIndex
56196          * @param {Roo.EventObject} e
56197          */
56198         "cellclick" : true,
56199         /**
56200          * @event celldblclick
56201          * Fires when a cell is double clicked
56202          * @param {Grid} this
56203          * @param {Number} rowIndex
56204          * @param {Number} columnIndex
56205          * @param {Roo.EventObject} e
56206          */
56207         "celldblclick" : true,
56208         /**
56209          * @event rowclick
56210          * Fires when a row is clicked
56211          * @param {Grid} this
56212          * @param {Number} rowIndex
56213          * @param {Roo.EventObject} e
56214          */
56215         "rowclick" : true,
56216         /**
56217          * @event rowdblclick
56218          * Fires when a row is double clicked
56219          * @param {Grid} this
56220          * @param {Number} rowIndex
56221          * @param {Roo.EventObject} e
56222          */
56223         "rowdblclick" : true,
56224         /**
56225          * @event headerclick
56226          * Fires when a header is clicked
56227          * @param {Grid} this
56228          * @param {Number} columnIndex
56229          * @param {Roo.EventObject} e
56230          */
56231         "headerclick" : true,
56232         /**
56233          * @event headerdblclick
56234          * Fires when a header cell is double clicked
56235          * @param {Grid} this
56236          * @param {Number} columnIndex
56237          * @param {Roo.EventObject} e
56238          */
56239         "headerdblclick" : true,
56240         /**
56241          * @event rowcontextmenu
56242          * Fires when a row is right clicked
56243          * @param {Grid} this
56244          * @param {Number} rowIndex
56245          * @param {Roo.EventObject} e
56246          */
56247         "rowcontextmenu" : true,
56248         /**
56249          * @event cellcontextmenu
56250          * Fires when a cell is right clicked
56251          * @param {Grid} this
56252          * @param {Number} rowIndex
56253          * @param {Number} cellIndex
56254          * @param {Roo.EventObject} e
56255          */
56256          "cellcontextmenu" : true,
56257         /**
56258          * @event headercontextmenu
56259          * Fires when a header is right clicked
56260          * @param {Grid} this
56261          * @param {Number} columnIndex
56262          * @param {Roo.EventObject} e
56263          */
56264         "headercontextmenu" : true,
56265         /**
56266          * @event bodyscroll
56267          * Fires when the body element is scrolled
56268          * @param {Number} scrollLeft
56269          * @param {Number} scrollTop
56270          */
56271         "bodyscroll" : true,
56272         /**
56273          * @event columnresize
56274          * Fires when the user resizes a column
56275          * @param {Number} columnIndex
56276          * @param {Number} newSize
56277          */
56278         "columnresize" : true,
56279         /**
56280          * @event columnmove
56281          * Fires when the user moves a column
56282          * @param {Number} oldIndex
56283          * @param {Number} newIndex
56284          */
56285         "columnmove" : true,
56286         /**
56287          * @event startdrag
56288          * Fires when row(s) start being dragged
56289          * @param {Grid} this
56290          * @param {Roo.GridDD} dd The drag drop object
56291          * @param {event} e The raw browser event
56292          */
56293         "startdrag" : true,
56294         /**
56295          * @event enddrag
56296          * Fires when a drag operation is complete
56297          * @param {Grid} this
56298          * @param {Roo.GridDD} dd The drag drop object
56299          * @param {event} e The raw browser event
56300          */
56301         "enddrag" : true,
56302         /**
56303          * @event dragdrop
56304          * Fires when dragged row(s) are dropped on a valid DD target
56305          * @param {Grid} this
56306          * @param {Roo.GridDD} dd The drag drop object
56307          * @param {String} targetId The target drag drop object
56308          * @param {event} e The raw browser event
56309          */
56310         "dragdrop" : true,
56311         /**
56312          * @event dragover
56313          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56314          * @param {Grid} this
56315          * @param {Roo.GridDD} dd The drag drop object
56316          * @param {String} targetId The target drag drop object
56317          * @param {event} e The raw browser event
56318          */
56319         "dragover" : true,
56320         /**
56321          * @event dragenter
56322          *  Fires when the dragged row(s) first cross another DD target while being dragged
56323          * @param {Grid} this
56324          * @param {Roo.GridDD} dd The drag drop object
56325          * @param {String} targetId The target drag drop object
56326          * @param {event} e The raw browser event
56327          */
56328         "dragenter" : true,
56329         /**
56330          * @event dragout
56331          * Fires when the dragged row(s) leave another DD target while being dragged
56332          * @param {Grid} this
56333          * @param {Roo.GridDD} dd The drag drop object
56334          * @param {String} targetId The target drag drop object
56335          * @param {event} e The raw browser event
56336          */
56337         "dragout" : true,
56338         /**
56339          * @event rowclass
56340          * Fires when a row is rendered, so you can change add a style to it.
56341          * @param {GridView} gridview   The grid view
56342          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56343          */
56344         'rowclass' : true,
56345
56346         /**
56347          * @event render
56348          * Fires when the grid is rendered
56349          * @param {Grid} grid
56350          */
56351         'render' : true
56352     });
56353
56354     Roo.grid.Grid.superclass.constructor.call(this);
56355 };
56356 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56357     
56358     /**
56359          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56360          */
56361         /**
56362          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56363          */
56364         /**
56365          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56366          */
56367         /**
56368          * @cfg {Roo.grid.Store} ds The data store for the grid
56369          */
56370         /**
56371          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56372          */
56373         /**
56374      * @cfg {String} ddGroup - drag drop group.
56375      */
56376       /**
56377      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56378      */
56379
56380     /**
56381      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56382      */
56383     minColumnWidth : 25,
56384
56385     /**
56386      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56387      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56388      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56389      */
56390     autoSizeColumns : false,
56391
56392     /**
56393      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56394      */
56395     autoSizeHeaders : true,
56396
56397     /**
56398      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56399      */
56400     monitorWindowResize : true,
56401
56402     /**
56403      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56404      * rows measured to get a columns size. Default is 0 (all rows).
56405      */
56406     maxRowsToMeasure : 0,
56407
56408     /**
56409      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56410      */
56411     trackMouseOver : true,
56412
56413     /**
56414     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56415     */
56416       /**
56417     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56418     */
56419     
56420     /**
56421     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56422     */
56423     enableDragDrop : false,
56424     
56425     /**
56426     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56427     */
56428     enableColumnMove : true,
56429     
56430     /**
56431     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56432     */
56433     enableColumnHide : true,
56434     
56435     /**
56436     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56437     */
56438     enableRowHeightSync : false,
56439     
56440     /**
56441     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56442     */
56443     stripeRows : true,
56444     
56445     /**
56446     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56447     */
56448     autoHeight : false,
56449
56450     /**
56451      * @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.
56452      */
56453     autoExpandColumn : false,
56454
56455     /**
56456     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56457     * Default is 50.
56458     */
56459     autoExpandMin : 50,
56460
56461     /**
56462     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56463     */
56464     autoExpandMax : 1000,
56465
56466     /**
56467     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56468     */
56469     view : null,
56470
56471     /**
56472     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56473     */
56474     loadMask : false,
56475     /**
56476     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56477     */
56478     dropTarget: false,
56479     
56480    
56481     
56482     // private
56483     rendered : false,
56484
56485     /**
56486     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56487     * of a fixed width. Default is false.
56488     */
56489     /**
56490     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56491     */
56492     
56493     
56494     /**
56495     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56496     * %0 is replaced with the number of selected rows.
56497     */
56498     ddText : "{0} selected row{1}",
56499     
56500     
56501     /**
56502      * Called once after all setup has been completed and the grid is ready to be rendered.
56503      * @return {Roo.grid.Grid} this
56504      */
56505     render : function()
56506     {
56507         var c = this.container;
56508         // try to detect autoHeight/width mode
56509         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56510             this.autoHeight = true;
56511         }
56512         var view = this.getView();
56513         view.init(this);
56514
56515         c.on("click", this.onClick, this);
56516         c.on("dblclick", this.onDblClick, this);
56517         c.on("contextmenu", this.onContextMenu, this);
56518         c.on("keydown", this.onKeyDown, this);
56519         if (Roo.isTouch) {
56520             c.on("touchstart", this.onTouchStart, this);
56521         }
56522
56523         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56524
56525         this.getSelectionModel().init(this);
56526
56527         view.render();
56528
56529         if(this.loadMask){
56530             this.loadMask = new Roo.LoadMask(this.container,
56531                     Roo.apply({store:this.dataSource}, this.loadMask));
56532         }
56533         
56534         
56535         if (this.toolbar && this.toolbar.xtype) {
56536             this.toolbar.container = this.getView().getHeaderPanel(true);
56537             this.toolbar = new Roo.Toolbar(this.toolbar);
56538         }
56539         if (this.footer && this.footer.xtype) {
56540             this.footer.dataSource = this.getDataSource();
56541             this.footer.container = this.getView().getFooterPanel(true);
56542             this.footer = Roo.factory(this.footer, Roo);
56543         }
56544         if (this.dropTarget && this.dropTarget.xtype) {
56545             delete this.dropTarget.xtype;
56546             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56547         }
56548         
56549         
56550         this.rendered = true;
56551         this.fireEvent('render', this);
56552         return this;
56553     },
56554
56555     /**
56556      * Reconfigures the grid to use a different Store and Column Model.
56557      * The View will be bound to the new objects and refreshed.
56558      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
56559      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
56560      */
56561     reconfigure : function(dataSource, colModel){
56562         if(this.loadMask){
56563             this.loadMask.destroy();
56564             this.loadMask = new Roo.LoadMask(this.container,
56565                     Roo.apply({store:dataSource}, this.loadMask));
56566         }
56567         this.view.bind(dataSource, colModel);
56568         this.dataSource = dataSource;
56569         this.colModel = colModel;
56570         this.view.refresh(true);
56571     },
56572     /**
56573      * addColumns
56574      * Add's a column, default at the end..
56575      
56576      * @param {int} position to add (default end)
56577      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
56578      */
56579     addColumns : function(pos, ar)
56580     {
56581         
56582         for (var i =0;i< ar.length;i++) {
56583             var cfg = ar[i];
56584             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
56585             this.cm.lookup[cfg.id] = cfg;
56586         }
56587         
56588         
56589         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
56590             pos = this.cm.config.length; //this.cm.config.push(cfg);
56591         } 
56592         pos = Math.max(0,pos);
56593         ar.unshift(0);
56594         ar.unshift(pos);
56595         this.cm.config.splice.apply(this.cm.config, ar);
56596         
56597         
56598         
56599         this.view.generateRules(this.cm);
56600         this.view.refresh(true);
56601         
56602     },
56603     
56604     
56605     
56606     
56607     // private
56608     onKeyDown : function(e){
56609         this.fireEvent("keydown", e);
56610     },
56611
56612     /**
56613      * Destroy this grid.
56614      * @param {Boolean} removeEl True to remove the element
56615      */
56616     destroy : function(removeEl, keepListeners){
56617         if(this.loadMask){
56618             this.loadMask.destroy();
56619         }
56620         var c = this.container;
56621         c.removeAllListeners();
56622         this.view.destroy();
56623         this.colModel.purgeListeners();
56624         if(!keepListeners){
56625             this.purgeListeners();
56626         }
56627         c.update("");
56628         if(removeEl === true){
56629             c.remove();
56630         }
56631     },
56632
56633     // private
56634     processEvent : function(name, e){
56635         // does this fire select???
56636         //Roo.log('grid:processEvent '  + name);
56637         
56638         if (name != 'touchstart' ) {
56639             this.fireEvent(name, e);    
56640         }
56641         
56642         var t = e.getTarget();
56643         var v = this.view;
56644         var header = v.findHeaderIndex(t);
56645         if(header !== false){
56646             var ename = name == 'touchstart' ? 'click' : name;
56647              
56648             this.fireEvent("header" + ename, this, header, e);
56649         }else{
56650             var row = v.findRowIndex(t);
56651             var cell = v.findCellIndex(t);
56652             if (name == 'touchstart') {
56653                 // first touch is always a click.
56654                 // hopefull this happens after selection is updated.?
56655                 name = false;
56656                 
56657                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
56658                     var cs = this.selModel.getSelectedCell();
56659                     if (row == cs[0] && cell == cs[1]){
56660                         name = 'dblclick';
56661                     }
56662                 }
56663                 if (typeof(this.selModel.getSelections) != 'undefined') {
56664                     var cs = this.selModel.getSelections();
56665                     var ds = this.dataSource;
56666                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
56667                         name = 'dblclick';
56668                     }
56669                 }
56670                 if (!name) {
56671                     return;
56672                 }
56673             }
56674             
56675             
56676             if(row !== false){
56677                 this.fireEvent("row" + name, this, row, e);
56678                 if(cell !== false){
56679                     this.fireEvent("cell" + name, this, row, cell, e);
56680                 }
56681             }
56682         }
56683     },
56684
56685     // private
56686     onClick : function(e){
56687         this.processEvent("click", e);
56688     },
56689    // private
56690     onTouchStart : function(e){
56691         this.processEvent("touchstart", e);
56692     },
56693
56694     // private
56695     onContextMenu : function(e, t){
56696         this.processEvent("contextmenu", e);
56697     },
56698
56699     // private
56700     onDblClick : function(e){
56701         this.processEvent("dblclick", e);
56702     },
56703
56704     // private
56705     walkCells : function(row, col, step, fn, scope){
56706         var cm = this.colModel, clen = cm.getColumnCount();
56707         var ds = this.dataSource, rlen = ds.getCount(), first = true;
56708         if(step < 0){
56709             if(col < 0){
56710                 row--;
56711                 first = false;
56712             }
56713             while(row >= 0){
56714                 if(!first){
56715                     col = clen-1;
56716                 }
56717                 first = false;
56718                 while(col >= 0){
56719                     if(fn.call(scope || this, row, col, cm) === true){
56720                         return [row, col];
56721                     }
56722                     col--;
56723                 }
56724                 row--;
56725             }
56726         } else {
56727             if(col >= clen){
56728                 row++;
56729                 first = false;
56730             }
56731             while(row < rlen){
56732                 if(!first){
56733                     col = 0;
56734                 }
56735                 first = false;
56736                 while(col < clen){
56737                     if(fn.call(scope || this, row, col, cm) === true){
56738                         return [row, col];
56739                     }
56740                     col++;
56741                 }
56742                 row++;
56743             }
56744         }
56745         return null;
56746     },
56747
56748     // private
56749     getSelections : function(){
56750         return this.selModel.getSelections();
56751     },
56752
56753     /**
56754      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56755      * but if manual update is required this method will initiate it.
56756      */
56757     autoSize : function(){
56758         if(this.rendered){
56759             this.view.layout();
56760             if(this.view.adjustForScroll){
56761                 this.view.adjustForScroll();
56762             }
56763         }
56764     },
56765
56766     /**
56767      * Returns the grid's underlying element.
56768      * @return {Element} The element
56769      */
56770     getGridEl : function(){
56771         return this.container;
56772     },
56773
56774     // private for compatibility, overridden by editor grid
56775     stopEditing : function(){},
56776
56777     /**
56778      * Returns the grid's SelectionModel.
56779      * @return {SelectionModel}
56780      */
56781     getSelectionModel : function(){
56782         if(!this.selModel){
56783             this.selModel = new Roo.grid.RowSelectionModel();
56784         }
56785         return this.selModel;
56786     },
56787
56788     /**
56789      * Returns the grid's DataSource.
56790      * @return {DataSource}
56791      */
56792     getDataSource : function(){
56793         return this.dataSource;
56794     },
56795
56796     /**
56797      * Returns the grid's ColumnModel.
56798      * @return {ColumnModel}
56799      */
56800     getColumnModel : function(){
56801         return this.colModel;
56802     },
56803
56804     /**
56805      * Returns the grid's GridView object.
56806      * @return {GridView}
56807      */
56808     getView : function(){
56809         if(!this.view){
56810             this.view = new Roo.grid.GridView(this.viewConfig);
56811             this.relayEvents(this.view, [
56812                 "beforerowremoved", "beforerowsinserted",
56813                 "beforerefresh", "rowremoved",
56814                 "rowsinserted", "rowupdated" ,"refresh"
56815             ]);
56816         }
56817         return this.view;
56818     },
56819     /**
56820      * Called to get grid's drag proxy text, by default returns this.ddText.
56821      * Override this to put something different in the dragged text.
56822      * @return {String}
56823      */
56824     getDragDropText : function(){
56825         var count = this.selModel.getCount();
56826         return String.format(this.ddText, count, count == 1 ? '' : 's');
56827     }
56828 });
56829 /*
56830  * Based on:
56831  * Ext JS Library 1.1.1
56832  * Copyright(c) 2006-2007, Ext JS, LLC.
56833  *
56834  * Originally Released Under LGPL - original licence link has changed is not relivant.
56835  *
56836  * Fork - LGPL
56837  * <script type="text/javascript">
56838  */
56839  /**
56840  * @class Roo.grid.AbstractGridView
56841  * @extends Roo.util.Observable
56842  * @abstract
56843  * Abstract base class for grid Views
56844  * @constructor
56845  */
56846 Roo.grid.AbstractGridView = function(){
56847         this.grid = null;
56848         
56849         this.events = {
56850             "beforerowremoved" : true,
56851             "beforerowsinserted" : true,
56852             "beforerefresh" : true,
56853             "rowremoved" : true,
56854             "rowsinserted" : true,
56855             "rowupdated" : true,
56856             "refresh" : true
56857         };
56858     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56859 };
56860
56861 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56862     rowClass : "x-grid-row",
56863     cellClass : "x-grid-cell",
56864     tdClass : "x-grid-td",
56865     hdClass : "x-grid-hd",
56866     splitClass : "x-grid-hd-split",
56867     
56868     init: function(grid){
56869         this.grid = grid;
56870                 var cid = this.grid.getGridEl().id;
56871         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56872         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56873         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56874         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56875         },
56876         
56877     getColumnRenderers : function(){
56878         var renderers = [];
56879         var cm = this.grid.colModel;
56880         var colCount = cm.getColumnCount();
56881         for(var i = 0; i < colCount; i++){
56882             renderers[i] = cm.getRenderer(i);
56883         }
56884         return renderers;
56885     },
56886     
56887     getColumnIds : function(){
56888         var ids = [];
56889         var cm = this.grid.colModel;
56890         var colCount = cm.getColumnCount();
56891         for(var i = 0; i < colCount; i++){
56892             ids[i] = cm.getColumnId(i);
56893         }
56894         return ids;
56895     },
56896     
56897     getDataIndexes : function(){
56898         if(!this.indexMap){
56899             this.indexMap = this.buildIndexMap();
56900         }
56901         return this.indexMap.colToData;
56902     },
56903     
56904     getColumnIndexByDataIndex : function(dataIndex){
56905         if(!this.indexMap){
56906             this.indexMap = this.buildIndexMap();
56907         }
56908         return this.indexMap.dataToCol[dataIndex];
56909     },
56910     
56911     /**
56912      * Set a css style for a column dynamically. 
56913      * @param {Number} colIndex The index of the column
56914      * @param {String} name The css property name
56915      * @param {String} value The css value
56916      */
56917     setCSSStyle : function(colIndex, name, value){
56918         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56919         Roo.util.CSS.updateRule(selector, name, value);
56920     },
56921     
56922     generateRules : function(cm){
56923         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56924         Roo.util.CSS.removeStyleSheet(rulesId);
56925         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56926             var cid = cm.getColumnId(i);
56927             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56928                          this.tdSelector, cid, " {\n}\n",
56929                          this.hdSelector, cid, " {\n}\n",
56930                          this.splitSelector, cid, " {\n}\n");
56931         }
56932         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56933     }
56934 });/*
56935  * Based on:
56936  * Ext JS Library 1.1.1
56937  * Copyright(c) 2006-2007, Ext JS, LLC.
56938  *
56939  * Originally Released Under LGPL - original licence link has changed is not relivant.
56940  *
56941  * Fork - LGPL
56942  * <script type="text/javascript">
56943  */
56944
56945 // private
56946 // This is a support class used internally by the Grid components
56947 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56948     this.grid = grid;
56949     this.view = grid.getView();
56950     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56951     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56952     if(hd2){
56953         this.setHandleElId(Roo.id(hd));
56954         this.setOuterHandleElId(Roo.id(hd2));
56955     }
56956     this.scroll = false;
56957 };
56958 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56959     maxDragWidth: 120,
56960     getDragData : function(e){
56961         var t = Roo.lib.Event.getTarget(e);
56962         var h = this.view.findHeaderCell(t);
56963         if(h){
56964             return {ddel: h.firstChild, header:h};
56965         }
56966         return false;
56967     },
56968
56969     onInitDrag : function(e){
56970         this.view.headersDisabled = true;
56971         var clone = this.dragData.ddel.cloneNode(true);
56972         clone.id = Roo.id();
56973         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56974         this.proxy.update(clone);
56975         return true;
56976     },
56977
56978     afterValidDrop : function(){
56979         var v = this.view;
56980         setTimeout(function(){
56981             v.headersDisabled = false;
56982         }, 50);
56983     },
56984
56985     afterInvalidDrop : function(){
56986         var v = this.view;
56987         setTimeout(function(){
56988             v.headersDisabled = false;
56989         }, 50);
56990     }
56991 });
56992 /*
56993  * Based on:
56994  * Ext JS Library 1.1.1
56995  * Copyright(c) 2006-2007, Ext JS, LLC.
56996  *
56997  * Originally Released Under LGPL - original licence link has changed is not relivant.
56998  *
56999  * Fork - LGPL
57000  * <script type="text/javascript">
57001  */
57002 // private
57003 // This is a support class used internally by the Grid components
57004 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57005     this.grid = grid;
57006     this.view = grid.getView();
57007     // split the proxies so they don't interfere with mouse events
57008     this.proxyTop = Roo.DomHelper.append(document.body, {
57009         cls:"col-move-top", html:"&#160;"
57010     }, true);
57011     this.proxyBottom = Roo.DomHelper.append(document.body, {
57012         cls:"col-move-bottom", html:"&#160;"
57013     }, true);
57014     this.proxyTop.hide = this.proxyBottom.hide = function(){
57015         this.setLeftTop(-100,-100);
57016         this.setStyle("visibility", "hidden");
57017     };
57018     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57019     // temporarily disabled
57020     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57021     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57022 };
57023 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57024     proxyOffsets : [-4, -9],
57025     fly: Roo.Element.fly,
57026
57027     getTargetFromEvent : function(e){
57028         var t = Roo.lib.Event.getTarget(e);
57029         var cindex = this.view.findCellIndex(t);
57030         if(cindex !== false){
57031             return this.view.getHeaderCell(cindex);
57032         }
57033         return null;
57034     },
57035
57036     nextVisible : function(h){
57037         var v = this.view, cm = this.grid.colModel;
57038         h = h.nextSibling;
57039         while(h){
57040             if(!cm.isHidden(v.getCellIndex(h))){
57041                 return h;
57042             }
57043             h = h.nextSibling;
57044         }
57045         return null;
57046     },
57047
57048     prevVisible : function(h){
57049         var v = this.view, cm = this.grid.colModel;
57050         h = h.prevSibling;
57051         while(h){
57052             if(!cm.isHidden(v.getCellIndex(h))){
57053                 return h;
57054             }
57055             h = h.prevSibling;
57056         }
57057         return null;
57058     },
57059
57060     positionIndicator : function(h, n, e){
57061         var x = Roo.lib.Event.getPageX(e);
57062         var r = Roo.lib.Dom.getRegion(n.firstChild);
57063         var px, pt, py = r.top + this.proxyOffsets[1];
57064         if((r.right - x) <= (r.right-r.left)/2){
57065             px = r.right+this.view.borderWidth;
57066             pt = "after";
57067         }else{
57068             px = r.left;
57069             pt = "before";
57070         }
57071         var oldIndex = this.view.getCellIndex(h);
57072         var newIndex = this.view.getCellIndex(n);
57073
57074         if(this.grid.colModel.isFixed(newIndex)){
57075             return false;
57076         }
57077
57078         var locked = this.grid.colModel.isLocked(newIndex);
57079
57080         if(pt == "after"){
57081             newIndex++;
57082         }
57083         if(oldIndex < newIndex){
57084             newIndex--;
57085         }
57086         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57087             return false;
57088         }
57089         px +=  this.proxyOffsets[0];
57090         this.proxyTop.setLeftTop(px, py);
57091         this.proxyTop.show();
57092         if(!this.bottomOffset){
57093             this.bottomOffset = this.view.mainHd.getHeight();
57094         }
57095         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57096         this.proxyBottom.show();
57097         return pt;
57098     },
57099
57100     onNodeEnter : function(n, dd, e, data){
57101         if(data.header != n){
57102             this.positionIndicator(data.header, n, e);
57103         }
57104     },
57105
57106     onNodeOver : function(n, dd, e, data){
57107         var result = false;
57108         if(data.header != n){
57109             result = this.positionIndicator(data.header, n, e);
57110         }
57111         if(!result){
57112             this.proxyTop.hide();
57113             this.proxyBottom.hide();
57114         }
57115         return result ? this.dropAllowed : this.dropNotAllowed;
57116     },
57117
57118     onNodeOut : function(n, dd, e, data){
57119         this.proxyTop.hide();
57120         this.proxyBottom.hide();
57121     },
57122
57123     onNodeDrop : function(n, dd, e, data){
57124         var h = data.header;
57125         if(h != n){
57126             var cm = this.grid.colModel;
57127             var x = Roo.lib.Event.getPageX(e);
57128             var r = Roo.lib.Dom.getRegion(n.firstChild);
57129             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57130             var oldIndex = this.view.getCellIndex(h);
57131             var newIndex = this.view.getCellIndex(n);
57132             var locked = cm.isLocked(newIndex);
57133             if(pt == "after"){
57134                 newIndex++;
57135             }
57136             if(oldIndex < newIndex){
57137                 newIndex--;
57138             }
57139             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57140                 return false;
57141             }
57142             cm.setLocked(oldIndex, locked, true);
57143             cm.moveColumn(oldIndex, newIndex);
57144             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57145             return true;
57146         }
57147         return false;
57148     }
57149 });
57150 /*
57151  * Based on:
57152  * Ext JS Library 1.1.1
57153  * Copyright(c) 2006-2007, Ext JS, LLC.
57154  *
57155  * Originally Released Under LGPL - original licence link has changed is not relivant.
57156  *
57157  * Fork - LGPL
57158  * <script type="text/javascript">
57159  */
57160   
57161 /**
57162  * @class Roo.grid.GridView
57163  * @extends Roo.util.Observable
57164  *
57165  * @constructor
57166  * @param {Object} config
57167  */
57168 Roo.grid.GridView = function(config){
57169     Roo.grid.GridView.superclass.constructor.call(this);
57170     this.el = null;
57171
57172     Roo.apply(this, config);
57173 };
57174
57175 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57176
57177     unselectable :  'unselectable="on"',
57178     unselectableCls :  'x-unselectable',
57179     
57180     
57181     rowClass : "x-grid-row",
57182
57183     cellClass : "x-grid-col",
57184
57185     tdClass : "x-grid-td",
57186
57187     hdClass : "x-grid-hd",
57188
57189     splitClass : "x-grid-split",
57190
57191     sortClasses : ["sort-asc", "sort-desc"],
57192
57193     enableMoveAnim : false,
57194
57195     hlColor: "C3DAF9",
57196
57197     dh : Roo.DomHelper,
57198
57199     fly : Roo.Element.fly,
57200
57201     css : Roo.util.CSS,
57202
57203     borderWidth: 1,
57204
57205     splitOffset: 3,
57206
57207     scrollIncrement : 22,
57208
57209     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57210
57211     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57212
57213     bind : function(ds, cm){
57214         if(this.ds){
57215             this.ds.un("load", this.onLoad, this);
57216             this.ds.un("datachanged", this.onDataChange, this);
57217             this.ds.un("add", this.onAdd, this);
57218             this.ds.un("remove", this.onRemove, this);
57219             this.ds.un("update", this.onUpdate, this);
57220             this.ds.un("clear", this.onClear, this);
57221         }
57222         if(ds){
57223             ds.on("load", this.onLoad, this);
57224             ds.on("datachanged", this.onDataChange, this);
57225             ds.on("add", this.onAdd, this);
57226             ds.on("remove", this.onRemove, this);
57227             ds.on("update", this.onUpdate, this);
57228             ds.on("clear", this.onClear, this);
57229         }
57230         this.ds = ds;
57231
57232         if(this.cm){
57233             this.cm.un("widthchange", this.onColWidthChange, this);
57234             this.cm.un("headerchange", this.onHeaderChange, this);
57235             this.cm.un("hiddenchange", this.onHiddenChange, this);
57236             this.cm.un("columnmoved", this.onColumnMove, this);
57237             this.cm.un("columnlockchange", this.onColumnLock, this);
57238         }
57239         if(cm){
57240             this.generateRules(cm);
57241             cm.on("widthchange", this.onColWidthChange, this);
57242             cm.on("headerchange", this.onHeaderChange, this);
57243             cm.on("hiddenchange", this.onHiddenChange, this);
57244             cm.on("columnmoved", this.onColumnMove, this);
57245             cm.on("columnlockchange", this.onColumnLock, this);
57246         }
57247         this.cm = cm;
57248     },
57249
57250     init: function(grid){
57251         Roo.grid.GridView.superclass.init.call(this, grid);
57252
57253         this.bind(grid.dataSource, grid.colModel);
57254
57255         grid.on("headerclick", this.handleHeaderClick, this);
57256
57257         if(grid.trackMouseOver){
57258             grid.on("mouseover", this.onRowOver, this);
57259             grid.on("mouseout", this.onRowOut, this);
57260         }
57261         grid.cancelTextSelection = function(){};
57262         this.gridId = grid.id;
57263
57264         var tpls = this.templates || {};
57265
57266         if(!tpls.master){
57267             tpls.master = new Roo.Template(
57268                '<div class="x-grid" hidefocus="true">',
57269                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57270                   '<div class="x-grid-topbar"></div>',
57271                   '<div class="x-grid-scroller"><div></div></div>',
57272                   '<div class="x-grid-locked">',
57273                       '<div class="x-grid-header">{lockedHeader}</div>',
57274                       '<div class="x-grid-body">{lockedBody}</div>',
57275                   "</div>",
57276                   '<div class="x-grid-viewport">',
57277                       '<div class="x-grid-header">{header}</div>',
57278                       '<div class="x-grid-body">{body}</div>',
57279                   "</div>",
57280                   '<div class="x-grid-bottombar"></div>',
57281                  
57282                   '<div class="x-grid-resize-proxy">&#160;</div>',
57283                "</div>"
57284             );
57285             tpls.master.disableformats = true;
57286         }
57287
57288         if(!tpls.header){
57289             tpls.header = new Roo.Template(
57290                '<table border="0" cellspacing="0" cellpadding="0">',
57291                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57292                "</table>{splits}"
57293             );
57294             tpls.header.disableformats = true;
57295         }
57296         tpls.header.compile();
57297
57298         if(!tpls.hcell){
57299             tpls.hcell = new Roo.Template(
57300                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57301                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57302                 "</div></td>"
57303              );
57304              tpls.hcell.disableFormats = true;
57305         }
57306         tpls.hcell.compile();
57307
57308         if(!tpls.hsplit){
57309             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57310                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57311             tpls.hsplit.disableFormats = true;
57312         }
57313         tpls.hsplit.compile();
57314
57315         if(!tpls.body){
57316             tpls.body = new Roo.Template(
57317                '<table border="0" cellspacing="0" cellpadding="0">',
57318                "<tbody>{rows}</tbody>",
57319                "</table>"
57320             );
57321             tpls.body.disableFormats = true;
57322         }
57323         tpls.body.compile();
57324
57325         if(!tpls.row){
57326             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57327             tpls.row.disableFormats = true;
57328         }
57329         tpls.row.compile();
57330
57331         if(!tpls.cell){
57332             tpls.cell = new Roo.Template(
57333                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57334                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57335                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57336                 "</td>"
57337             );
57338             tpls.cell.disableFormats = true;
57339         }
57340         tpls.cell.compile();
57341
57342         this.templates = tpls;
57343     },
57344
57345     // remap these for backwards compat
57346     onColWidthChange : function(){
57347         this.updateColumns.apply(this, arguments);
57348     },
57349     onHeaderChange : function(){
57350         this.updateHeaders.apply(this, arguments);
57351     }, 
57352     onHiddenChange : function(){
57353         this.handleHiddenChange.apply(this, arguments);
57354     },
57355     onColumnMove : function(){
57356         this.handleColumnMove.apply(this, arguments);
57357     },
57358     onColumnLock : function(){
57359         this.handleLockChange.apply(this, arguments);
57360     },
57361
57362     onDataChange : function(){
57363         this.refresh();
57364         this.updateHeaderSortState();
57365     },
57366
57367     onClear : function(){
57368         this.refresh();
57369     },
57370
57371     onUpdate : function(ds, record){
57372         this.refreshRow(record);
57373     },
57374
57375     refreshRow : function(record){
57376         var ds = this.ds, index;
57377         if(typeof record == 'number'){
57378             index = record;
57379             record = ds.getAt(index);
57380         }else{
57381             index = ds.indexOf(record);
57382         }
57383         this.insertRows(ds, index, index, true);
57384         this.onRemove(ds, record, index+1, true);
57385         this.syncRowHeights(index, index);
57386         this.layout();
57387         this.fireEvent("rowupdated", this, index, record);
57388     },
57389
57390     onAdd : function(ds, records, index){
57391         this.insertRows(ds, index, index + (records.length-1));
57392     },
57393
57394     onRemove : function(ds, record, index, isUpdate){
57395         if(isUpdate !== true){
57396             this.fireEvent("beforerowremoved", this, index, record);
57397         }
57398         var bt = this.getBodyTable(), lt = this.getLockedTable();
57399         if(bt.rows[index]){
57400             bt.firstChild.removeChild(bt.rows[index]);
57401         }
57402         if(lt.rows[index]){
57403             lt.firstChild.removeChild(lt.rows[index]);
57404         }
57405         if(isUpdate !== true){
57406             this.stripeRows(index);
57407             this.syncRowHeights(index, index);
57408             this.layout();
57409             this.fireEvent("rowremoved", this, index, record);
57410         }
57411     },
57412
57413     onLoad : function(){
57414         this.scrollToTop();
57415     },
57416
57417     /**
57418      * Scrolls the grid to the top
57419      */
57420     scrollToTop : function(){
57421         if(this.scroller){
57422             this.scroller.dom.scrollTop = 0;
57423             this.syncScroll();
57424         }
57425     },
57426
57427     /**
57428      * Gets a panel in the header of the grid that can be used for toolbars etc.
57429      * After modifying the contents of this panel a call to grid.autoSize() may be
57430      * required to register any changes in size.
57431      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57432      * @return Roo.Element
57433      */
57434     getHeaderPanel : function(doShow){
57435         if(doShow){
57436             this.headerPanel.show();
57437         }
57438         return this.headerPanel;
57439     },
57440
57441     /**
57442      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57443      * After modifying the contents of this panel a call to grid.autoSize() may be
57444      * required to register any changes in size.
57445      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57446      * @return Roo.Element
57447      */
57448     getFooterPanel : function(doShow){
57449         if(doShow){
57450             this.footerPanel.show();
57451         }
57452         return this.footerPanel;
57453     },
57454
57455     initElements : function(){
57456         var E = Roo.Element;
57457         var el = this.grid.getGridEl().dom.firstChild;
57458         var cs = el.childNodes;
57459
57460         this.el = new E(el);
57461         
57462          this.focusEl = new E(el.firstChild);
57463         this.focusEl.swallowEvent("click", true);
57464         
57465         this.headerPanel = new E(cs[1]);
57466         this.headerPanel.enableDisplayMode("block");
57467
57468         this.scroller = new E(cs[2]);
57469         this.scrollSizer = new E(this.scroller.dom.firstChild);
57470
57471         this.lockedWrap = new E(cs[3]);
57472         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57473         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57474
57475         this.mainWrap = new E(cs[4]);
57476         this.mainHd = new E(this.mainWrap.dom.firstChild);
57477         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57478
57479         this.footerPanel = new E(cs[5]);
57480         this.footerPanel.enableDisplayMode("block");
57481
57482         this.resizeProxy = new E(cs[6]);
57483
57484         this.headerSelector = String.format(
57485            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57486            this.lockedHd.id, this.mainHd.id
57487         );
57488
57489         this.splitterSelector = String.format(
57490            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57491            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57492         );
57493     },
57494     idToCssName : function(s)
57495     {
57496         return s.replace(/[^a-z0-9]+/ig, '-');
57497     },
57498
57499     getHeaderCell : function(index){
57500         return Roo.DomQuery.select(this.headerSelector)[index];
57501     },
57502
57503     getHeaderCellMeasure : function(index){
57504         return this.getHeaderCell(index).firstChild;
57505     },
57506
57507     getHeaderCellText : function(index){
57508         return this.getHeaderCell(index).firstChild.firstChild;
57509     },
57510
57511     getLockedTable : function(){
57512         return this.lockedBody.dom.firstChild;
57513     },
57514
57515     getBodyTable : function(){
57516         return this.mainBody.dom.firstChild;
57517     },
57518
57519     getLockedRow : function(index){
57520         return this.getLockedTable().rows[index];
57521     },
57522
57523     getRow : function(index){
57524         return this.getBodyTable().rows[index];
57525     },
57526
57527     getRowComposite : function(index){
57528         if(!this.rowEl){
57529             this.rowEl = new Roo.CompositeElementLite();
57530         }
57531         var els = [], lrow, mrow;
57532         if(lrow = this.getLockedRow(index)){
57533             els.push(lrow);
57534         }
57535         if(mrow = this.getRow(index)){
57536             els.push(mrow);
57537         }
57538         this.rowEl.elements = els;
57539         return this.rowEl;
57540     },
57541     /**
57542      * Gets the 'td' of the cell
57543      * 
57544      * @param {Integer} rowIndex row to select
57545      * @param {Integer} colIndex column to select
57546      * 
57547      * @return {Object} 
57548      */
57549     getCell : function(rowIndex, colIndex){
57550         var locked = this.cm.getLockedCount();
57551         var source;
57552         if(colIndex < locked){
57553             source = this.lockedBody.dom.firstChild;
57554         }else{
57555             source = this.mainBody.dom.firstChild;
57556             colIndex -= locked;
57557         }
57558         return source.rows[rowIndex].childNodes[colIndex];
57559     },
57560
57561     getCellText : function(rowIndex, colIndex){
57562         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
57563     },
57564
57565     getCellBox : function(cell){
57566         var b = this.fly(cell).getBox();
57567         if(Roo.isOpera){ // opera fails to report the Y
57568             b.y = cell.offsetTop + this.mainBody.getY();
57569         }
57570         return b;
57571     },
57572
57573     getCellIndex : function(cell){
57574         var id = String(cell.className).match(this.cellRE);
57575         if(id){
57576             return parseInt(id[1], 10);
57577         }
57578         return 0;
57579     },
57580
57581     findHeaderIndex : function(n){
57582         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57583         return r ? this.getCellIndex(r) : false;
57584     },
57585
57586     findHeaderCell : function(n){
57587         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57588         return r ? r : false;
57589     },
57590
57591     findRowIndex : function(n){
57592         if(!n){
57593             return false;
57594         }
57595         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
57596         return r ? r.rowIndex : false;
57597     },
57598
57599     findCellIndex : function(node){
57600         var stop = this.el.dom;
57601         while(node && node != stop){
57602             if(this.findRE.test(node.className)){
57603                 return this.getCellIndex(node);
57604             }
57605             node = node.parentNode;
57606         }
57607         return false;
57608     },
57609
57610     getColumnId : function(index){
57611         return this.cm.getColumnId(index);
57612     },
57613
57614     getSplitters : function()
57615     {
57616         if(this.splitterSelector){
57617            return Roo.DomQuery.select(this.splitterSelector);
57618         }else{
57619             return null;
57620       }
57621     },
57622
57623     getSplitter : function(index){
57624         return this.getSplitters()[index];
57625     },
57626
57627     onRowOver : function(e, t){
57628         var row;
57629         if((row = this.findRowIndex(t)) !== false){
57630             this.getRowComposite(row).addClass("x-grid-row-over");
57631         }
57632     },
57633
57634     onRowOut : function(e, t){
57635         var row;
57636         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
57637             this.getRowComposite(row).removeClass("x-grid-row-over");
57638         }
57639     },
57640
57641     renderHeaders : function(){
57642         var cm = this.cm;
57643         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
57644         var cb = [], lb = [], sb = [], lsb = [], p = {};
57645         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57646             p.cellId = "x-grid-hd-0-" + i;
57647             p.splitId = "x-grid-csplit-0-" + i;
57648             p.id = cm.getColumnId(i);
57649             p.value = cm.getColumnHeader(i) || "";
57650             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
57651             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
57652             if(!cm.isLocked(i)){
57653                 cb[cb.length] = ct.apply(p);
57654                 sb[sb.length] = st.apply(p);
57655             }else{
57656                 lb[lb.length] = ct.apply(p);
57657                 lsb[lsb.length] = st.apply(p);
57658             }
57659         }
57660         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
57661                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
57662     },
57663
57664     updateHeaders : function(){
57665         var html = this.renderHeaders();
57666         this.lockedHd.update(html[0]);
57667         this.mainHd.update(html[1]);
57668     },
57669
57670     /**
57671      * Focuses the specified row.
57672      * @param {Number} row The row index
57673      */
57674     focusRow : function(row)
57675     {
57676         //Roo.log('GridView.focusRow');
57677         var x = this.scroller.dom.scrollLeft;
57678         this.focusCell(row, 0, false);
57679         this.scroller.dom.scrollLeft = x;
57680     },
57681
57682     /**
57683      * Focuses the specified cell.
57684      * @param {Number} row The row index
57685      * @param {Number} col The column index
57686      * @param {Boolean} hscroll false to disable horizontal scrolling
57687      */
57688     focusCell : function(row, col, hscroll)
57689     {
57690         //Roo.log('GridView.focusCell');
57691         var el = this.ensureVisible(row, col, hscroll);
57692         this.focusEl.alignTo(el, "tl-tl");
57693         if(Roo.isGecko){
57694             this.focusEl.focus();
57695         }else{
57696             this.focusEl.focus.defer(1, this.focusEl);
57697         }
57698     },
57699
57700     /**
57701      * Scrolls the specified cell into view
57702      * @param {Number} row The row index
57703      * @param {Number} col The column index
57704      * @param {Boolean} hscroll false to disable horizontal scrolling
57705      */
57706     ensureVisible : function(row, col, hscroll)
57707     {
57708         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
57709         //return null; //disable for testing.
57710         if(typeof row != "number"){
57711             row = row.rowIndex;
57712         }
57713         if(row < 0 && row >= this.ds.getCount()){
57714             return  null;
57715         }
57716         col = (col !== undefined ? col : 0);
57717         var cm = this.grid.colModel;
57718         while(cm.isHidden(col)){
57719             col++;
57720         }
57721
57722         var el = this.getCell(row, col);
57723         if(!el){
57724             return null;
57725         }
57726         var c = this.scroller.dom;
57727
57728         var ctop = parseInt(el.offsetTop, 10);
57729         var cleft = parseInt(el.offsetLeft, 10);
57730         var cbot = ctop + el.offsetHeight;
57731         var cright = cleft + el.offsetWidth;
57732         
57733         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57734         var stop = parseInt(c.scrollTop, 10);
57735         var sleft = parseInt(c.scrollLeft, 10);
57736         var sbot = stop + ch;
57737         var sright = sleft + c.clientWidth;
57738         /*
57739         Roo.log('GridView.ensureVisible:' +
57740                 ' ctop:' + ctop +
57741                 ' c.clientHeight:' + c.clientHeight +
57742                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57743                 ' stop:' + stop +
57744                 ' cbot:' + cbot +
57745                 ' sbot:' + sbot +
57746                 ' ch:' + ch  
57747                 );
57748         */
57749         if(ctop < stop){
57750             c.scrollTop = ctop;
57751             //Roo.log("set scrolltop to ctop DISABLE?");
57752         }else if(cbot > sbot){
57753             //Roo.log("set scrolltop to cbot-ch");
57754             c.scrollTop = cbot-ch;
57755         }
57756         
57757         if(hscroll !== false){
57758             if(cleft < sleft){
57759                 c.scrollLeft = cleft;
57760             }else if(cright > sright){
57761                 c.scrollLeft = cright-c.clientWidth;
57762             }
57763         }
57764          
57765         return el;
57766     },
57767
57768     updateColumns : function(){
57769         this.grid.stopEditing();
57770         var cm = this.grid.colModel, colIds = this.getColumnIds();
57771         //var totalWidth = cm.getTotalWidth();
57772         var pos = 0;
57773         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57774             //if(cm.isHidden(i)) continue;
57775             var w = cm.getColumnWidth(i);
57776             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57777             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57778         }
57779         this.updateSplitters();
57780     },
57781
57782     generateRules : function(cm){
57783         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57784         Roo.util.CSS.removeStyleSheet(rulesId);
57785         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57786             var cid = cm.getColumnId(i);
57787             var align = '';
57788             if(cm.config[i].align){
57789                 align = 'text-align:'+cm.config[i].align+';';
57790             }
57791             var hidden = '';
57792             if(cm.isHidden(i)){
57793                 hidden = 'display:none;';
57794             }
57795             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57796             ruleBuf.push(
57797                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57798                     this.hdSelector, cid, " {\n", align, width, "}\n",
57799                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57800                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57801         }
57802         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57803     },
57804
57805     updateSplitters : function(){
57806         var cm = this.cm, s = this.getSplitters();
57807         if(s){ // splitters not created yet
57808             var pos = 0, locked = true;
57809             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57810                 if(cm.isHidden(i)) {
57811                     continue;
57812                 }
57813                 var w = cm.getColumnWidth(i); // make sure it's a number
57814                 if(!cm.isLocked(i) && locked){
57815                     pos = 0;
57816                     locked = false;
57817                 }
57818                 pos += w;
57819                 s[i].style.left = (pos-this.splitOffset) + "px";
57820             }
57821         }
57822     },
57823
57824     handleHiddenChange : function(colModel, colIndex, hidden){
57825         if(hidden){
57826             this.hideColumn(colIndex);
57827         }else{
57828             this.unhideColumn(colIndex);
57829         }
57830     },
57831
57832     hideColumn : function(colIndex){
57833         var cid = this.getColumnId(colIndex);
57834         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57835         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57836         if(Roo.isSafari){
57837             this.updateHeaders();
57838         }
57839         this.updateSplitters();
57840         this.layout();
57841     },
57842
57843     unhideColumn : function(colIndex){
57844         var cid = this.getColumnId(colIndex);
57845         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57846         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57847
57848         if(Roo.isSafari){
57849             this.updateHeaders();
57850         }
57851         this.updateSplitters();
57852         this.layout();
57853     },
57854
57855     insertRows : function(dm, firstRow, lastRow, isUpdate){
57856         if(firstRow == 0 && lastRow == dm.getCount()-1){
57857             this.refresh();
57858         }else{
57859             if(!isUpdate){
57860                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57861             }
57862             var s = this.getScrollState();
57863             var markup = this.renderRows(firstRow, lastRow);
57864             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57865             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57866             this.restoreScroll(s);
57867             if(!isUpdate){
57868                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57869                 this.syncRowHeights(firstRow, lastRow);
57870                 this.stripeRows(firstRow);
57871                 this.layout();
57872             }
57873         }
57874     },
57875
57876     bufferRows : function(markup, target, index){
57877         var before = null, trows = target.rows, tbody = target.tBodies[0];
57878         if(index < trows.length){
57879             before = trows[index];
57880         }
57881         var b = document.createElement("div");
57882         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57883         var rows = b.firstChild.rows;
57884         for(var i = 0, len = rows.length; i < len; i++){
57885             if(before){
57886                 tbody.insertBefore(rows[0], before);
57887             }else{
57888                 tbody.appendChild(rows[0]);
57889             }
57890         }
57891         b.innerHTML = "";
57892         b = null;
57893     },
57894
57895     deleteRows : function(dm, firstRow, lastRow){
57896         if(dm.getRowCount()<1){
57897             this.fireEvent("beforerefresh", this);
57898             this.mainBody.update("");
57899             this.lockedBody.update("");
57900             this.fireEvent("refresh", this);
57901         }else{
57902             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57903             var bt = this.getBodyTable();
57904             var tbody = bt.firstChild;
57905             var rows = bt.rows;
57906             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57907                 tbody.removeChild(rows[firstRow]);
57908             }
57909             this.stripeRows(firstRow);
57910             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57911         }
57912     },
57913
57914     updateRows : function(dataSource, firstRow, lastRow){
57915         var s = this.getScrollState();
57916         this.refresh();
57917         this.restoreScroll(s);
57918     },
57919
57920     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57921         if(!noRefresh){
57922            this.refresh();
57923         }
57924         this.updateHeaderSortState();
57925     },
57926
57927     getScrollState : function(){
57928         
57929         var sb = this.scroller.dom;
57930         return {left: sb.scrollLeft, top: sb.scrollTop};
57931     },
57932
57933     stripeRows : function(startRow){
57934         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57935             return;
57936         }
57937         startRow = startRow || 0;
57938         var rows = this.getBodyTable().rows;
57939         var lrows = this.getLockedTable().rows;
57940         var cls = ' x-grid-row-alt ';
57941         for(var i = startRow, len = rows.length; i < len; i++){
57942             var row = rows[i], lrow = lrows[i];
57943             var isAlt = ((i+1) % 2 == 0);
57944             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57945             if(isAlt == hasAlt){
57946                 continue;
57947             }
57948             if(isAlt){
57949                 row.className += " x-grid-row-alt";
57950             }else{
57951                 row.className = row.className.replace("x-grid-row-alt", "");
57952             }
57953             if(lrow){
57954                 lrow.className = row.className;
57955             }
57956         }
57957     },
57958
57959     restoreScroll : function(state){
57960         //Roo.log('GridView.restoreScroll');
57961         var sb = this.scroller.dom;
57962         sb.scrollLeft = state.left;
57963         sb.scrollTop = state.top;
57964         this.syncScroll();
57965     },
57966
57967     syncScroll : function(){
57968         //Roo.log('GridView.syncScroll');
57969         var sb = this.scroller.dom;
57970         var sh = this.mainHd.dom;
57971         var bs = this.mainBody.dom;
57972         var lv = this.lockedBody.dom;
57973         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57974         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57975     },
57976
57977     handleScroll : function(e){
57978         this.syncScroll();
57979         var sb = this.scroller.dom;
57980         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57981         e.stopEvent();
57982     },
57983
57984     handleWheel : function(e){
57985         var d = e.getWheelDelta();
57986         this.scroller.dom.scrollTop -= d*22;
57987         // set this here to prevent jumpy scrolling on large tables
57988         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57989         e.stopEvent();
57990     },
57991
57992     renderRows : function(startRow, endRow){
57993         // pull in all the crap needed to render rows
57994         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57995         var colCount = cm.getColumnCount();
57996
57997         if(ds.getCount() < 1){
57998             return ["", ""];
57999         }
58000
58001         // build a map for all the columns
58002         var cs = [];
58003         for(var i = 0; i < colCount; i++){
58004             var name = cm.getDataIndex(i);
58005             cs[i] = {
58006                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58007                 renderer : cm.getRenderer(i),
58008                 id : cm.getColumnId(i),
58009                 locked : cm.isLocked(i),
58010                 has_editor : cm.isCellEditable(i)
58011             };
58012         }
58013
58014         startRow = startRow || 0;
58015         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58016
58017         // records to render
58018         var rs = ds.getRange(startRow, endRow);
58019
58020         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58021     },
58022
58023     // As much as I hate to duplicate code, this was branched because FireFox really hates
58024     // [].join("") on strings. The performance difference was substantial enough to
58025     // branch this function
58026     doRender : Roo.isGecko ?
58027             function(cs, rs, ds, startRow, colCount, stripe){
58028                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58029                 // buffers
58030                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58031                 
58032                 var hasListener = this.grid.hasListener('rowclass');
58033                 var rowcfg = {};
58034                 for(var j = 0, len = rs.length; j < len; j++){
58035                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58036                     for(var i = 0; i < colCount; i++){
58037                         c = cs[i];
58038                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58039                         p.id = c.id;
58040                         p.css = p.attr = "";
58041                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58042                         if(p.value == undefined || p.value === "") {
58043                             p.value = "&#160;";
58044                         }
58045                         if(c.has_editor){
58046                             p.css += ' x-grid-editable-cell';
58047                         }
58048                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58049                             p.css +=  ' x-grid-dirty-cell';
58050                         }
58051                         var markup = ct.apply(p);
58052                         if(!c.locked){
58053                             cb+= markup;
58054                         }else{
58055                             lcb+= markup;
58056                         }
58057                     }
58058                     var alt = [];
58059                     if(stripe && ((rowIndex+1) % 2 == 0)){
58060                         alt.push("x-grid-row-alt")
58061                     }
58062                     if(r.dirty){
58063                         alt.push(  " x-grid-dirty-row");
58064                     }
58065                     rp.cells = lcb;
58066                     if(this.getRowClass){
58067                         alt.push(this.getRowClass(r, rowIndex));
58068                     }
58069                     if (hasListener) {
58070                         rowcfg = {
58071                              
58072                             record: r,
58073                             rowIndex : rowIndex,
58074                             rowClass : ''
58075                         };
58076                         this.grid.fireEvent('rowclass', this, rowcfg);
58077                         alt.push(rowcfg.rowClass);
58078                     }
58079                     rp.alt = alt.join(" ");
58080                     lbuf+= rt.apply(rp);
58081                     rp.cells = cb;
58082                     buf+=  rt.apply(rp);
58083                 }
58084                 return [lbuf, buf];
58085             } :
58086             function(cs, rs, ds, startRow, colCount, stripe){
58087                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58088                 // buffers
58089                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58090                 var hasListener = this.grid.hasListener('rowclass');
58091  
58092                 var rowcfg = {};
58093                 for(var j = 0, len = rs.length; j < len; j++){
58094                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58095                     for(var i = 0; i < colCount; i++){
58096                         c = cs[i];
58097                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58098                         p.id = c.id;
58099                         p.css = p.attr = "";
58100                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58101                         if(p.value == undefined || p.value === "") {
58102                             p.value = "&#160;";
58103                         }
58104                         //Roo.log(c);
58105                          if(c.has_editor){
58106                             p.css += ' x-grid-editable-cell';
58107                         }
58108                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58109                             p.css += ' x-grid-dirty-cell' 
58110                         }
58111                         
58112                         var markup = ct.apply(p);
58113                         if(!c.locked){
58114                             cb[cb.length] = markup;
58115                         }else{
58116                             lcb[lcb.length] = markup;
58117                         }
58118                     }
58119                     var alt = [];
58120                     if(stripe && ((rowIndex+1) % 2 == 0)){
58121                         alt.push( "x-grid-row-alt");
58122                     }
58123                     if(r.dirty){
58124                         alt.push(" x-grid-dirty-row");
58125                     }
58126                     rp.cells = lcb;
58127                     if(this.getRowClass){
58128                         alt.push( this.getRowClass(r, rowIndex));
58129                     }
58130                     if (hasListener) {
58131                         rowcfg = {
58132                              
58133                             record: r,
58134                             rowIndex : rowIndex,
58135                             rowClass : ''
58136                         };
58137                         this.grid.fireEvent('rowclass', this, rowcfg);
58138                         alt.push(rowcfg.rowClass);
58139                     }
58140                     
58141                     rp.alt = alt.join(" ");
58142                     rp.cells = lcb.join("");
58143                     lbuf[lbuf.length] = rt.apply(rp);
58144                     rp.cells = cb.join("");
58145                     buf[buf.length] =  rt.apply(rp);
58146                 }
58147                 return [lbuf.join(""), buf.join("")];
58148             },
58149
58150     renderBody : function(){
58151         var markup = this.renderRows();
58152         var bt = this.templates.body;
58153         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58154     },
58155
58156     /**
58157      * Refreshes the grid
58158      * @param {Boolean} headersToo
58159      */
58160     refresh : function(headersToo){
58161         this.fireEvent("beforerefresh", this);
58162         this.grid.stopEditing();
58163         var result = this.renderBody();
58164         this.lockedBody.update(result[0]);
58165         this.mainBody.update(result[1]);
58166         if(headersToo === true){
58167             this.updateHeaders();
58168             this.updateColumns();
58169             this.updateSplitters();
58170             this.updateHeaderSortState();
58171         }
58172         this.syncRowHeights();
58173         this.layout();
58174         this.fireEvent("refresh", this);
58175     },
58176
58177     handleColumnMove : function(cm, oldIndex, newIndex){
58178         this.indexMap = null;
58179         var s = this.getScrollState();
58180         this.refresh(true);
58181         this.restoreScroll(s);
58182         this.afterMove(newIndex);
58183     },
58184
58185     afterMove : function(colIndex){
58186         if(this.enableMoveAnim && Roo.enableFx){
58187             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58188         }
58189         // if multisort - fix sortOrder, and reload..
58190         if (this.grid.dataSource.multiSort) {
58191             // the we can call sort again..
58192             var dm = this.grid.dataSource;
58193             var cm = this.grid.colModel;
58194             var so = [];
58195             for(var i = 0; i < cm.config.length; i++ ) {
58196                 
58197                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58198                     continue; // dont' bother, it's not in sort list or being set.
58199                 }
58200                 
58201                 so.push(cm.config[i].dataIndex);
58202             };
58203             dm.sortOrder = so;
58204             dm.load(dm.lastOptions);
58205             
58206             
58207         }
58208         
58209     },
58210
58211     updateCell : function(dm, rowIndex, dataIndex){
58212         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58213         if(typeof colIndex == "undefined"){ // not present in grid
58214             return;
58215         }
58216         var cm = this.grid.colModel;
58217         var cell = this.getCell(rowIndex, colIndex);
58218         var cellText = this.getCellText(rowIndex, colIndex);
58219
58220         var p = {
58221             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58222             id : cm.getColumnId(colIndex),
58223             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58224         };
58225         var renderer = cm.getRenderer(colIndex);
58226         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58227         if(typeof val == "undefined" || val === "") {
58228             val = "&#160;";
58229         }
58230         cellText.innerHTML = val;
58231         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58232         this.syncRowHeights(rowIndex, rowIndex);
58233     },
58234
58235     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58236         var maxWidth = 0;
58237         if(this.grid.autoSizeHeaders){
58238             var h = this.getHeaderCellMeasure(colIndex);
58239             maxWidth = Math.max(maxWidth, h.scrollWidth);
58240         }
58241         var tb, index;
58242         if(this.cm.isLocked(colIndex)){
58243             tb = this.getLockedTable();
58244             index = colIndex;
58245         }else{
58246             tb = this.getBodyTable();
58247             index = colIndex - this.cm.getLockedCount();
58248         }
58249         if(tb && tb.rows){
58250             var rows = tb.rows;
58251             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58252             for(var i = 0; i < stopIndex; i++){
58253                 var cell = rows[i].childNodes[index].firstChild;
58254                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58255             }
58256         }
58257         return maxWidth + /*margin for error in IE*/ 5;
58258     },
58259     /**
58260      * Autofit a column to its content.
58261      * @param {Number} colIndex
58262      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58263      */
58264      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58265          if(this.cm.isHidden(colIndex)){
58266              return; // can't calc a hidden column
58267          }
58268         if(forceMinSize){
58269             var cid = this.cm.getColumnId(colIndex);
58270             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58271            if(this.grid.autoSizeHeaders){
58272                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58273            }
58274         }
58275         var newWidth = this.calcColumnWidth(colIndex);
58276         this.cm.setColumnWidth(colIndex,
58277             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58278         if(!suppressEvent){
58279             this.grid.fireEvent("columnresize", colIndex, newWidth);
58280         }
58281     },
58282
58283     /**
58284      * Autofits all columns to their content and then expands to fit any extra space in the grid
58285      */
58286      autoSizeColumns : function(){
58287         var cm = this.grid.colModel;
58288         var colCount = cm.getColumnCount();
58289         for(var i = 0; i < colCount; i++){
58290             this.autoSizeColumn(i, true, true);
58291         }
58292         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58293             this.fitColumns();
58294         }else{
58295             this.updateColumns();
58296             this.layout();
58297         }
58298     },
58299
58300     /**
58301      * Autofits all columns to the grid's width proportionate with their current size
58302      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58303      */
58304     fitColumns : function(reserveScrollSpace){
58305         var cm = this.grid.colModel;
58306         var colCount = cm.getColumnCount();
58307         var cols = [];
58308         var width = 0;
58309         var i, w;
58310         for (i = 0; i < colCount; i++){
58311             if(!cm.isHidden(i) && !cm.isFixed(i)){
58312                 w = cm.getColumnWidth(i);
58313                 cols.push(i);
58314                 cols.push(w);
58315                 width += w;
58316             }
58317         }
58318         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58319         if(reserveScrollSpace){
58320             avail -= 17;
58321         }
58322         var frac = (avail - cm.getTotalWidth())/width;
58323         while (cols.length){
58324             w = cols.pop();
58325             i = cols.pop();
58326             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58327         }
58328         this.updateColumns();
58329         this.layout();
58330     },
58331
58332     onRowSelect : function(rowIndex){
58333         var row = this.getRowComposite(rowIndex);
58334         row.addClass("x-grid-row-selected");
58335     },
58336
58337     onRowDeselect : function(rowIndex){
58338         var row = this.getRowComposite(rowIndex);
58339         row.removeClass("x-grid-row-selected");
58340     },
58341
58342     onCellSelect : function(row, col){
58343         var cell = this.getCell(row, col);
58344         if(cell){
58345             Roo.fly(cell).addClass("x-grid-cell-selected");
58346         }
58347     },
58348
58349     onCellDeselect : function(row, col){
58350         var cell = this.getCell(row, col);
58351         if(cell){
58352             Roo.fly(cell).removeClass("x-grid-cell-selected");
58353         }
58354     },
58355
58356     updateHeaderSortState : function(){
58357         
58358         // sort state can be single { field: xxx, direction : yyy}
58359         // or   { xxx=>ASC , yyy : DESC ..... }
58360         
58361         var mstate = {};
58362         if (!this.ds.multiSort) { 
58363             var state = this.ds.getSortState();
58364             if(!state){
58365                 return;
58366             }
58367             mstate[state.field] = state.direction;
58368             // FIXME... - this is not used here.. but might be elsewhere..
58369             this.sortState = state;
58370             
58371         } else {
58372             mstate = this.ds.sortToggle;
58373         }
58374         //remove existing sort classes..
58375         
58376         var sc = this.sortClasses;
58377         var hds = this.el.select(this.headerSelector).removeClass(sc);
58378         
58379         for(var f in mstate) {
58380         
58381             var sortColumn = this.cm.findColumnIndex(f);
58382             
58383             if(sortColumn != -1){
58384                 var sortDir = mstate[f];        
58385                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58386             }
58387         }
58388         
58389          
58390         
58391     },
58392
58393
58394     handleHeaderClick : function(g, index,e){
58395         
58396         Roo.log("header click");
58397         
58398         if (Roo.isTouch) {
58399             // touch events on header are handled by context
58400             this.handleHdCtx(g,index,e);
58401             return;
58402         }
58403         
58404         
58405         if(this.headersDisabled){
58406             return;
58407         }
58408         var dm = g.dataSource, cm = g.colModel;
58409         if(!cm.isSortable(index)){
58410             return;
58411         }
58412         g.stopEditing();
58413         
58414         if (dm.multiSort) {
58415             // update the sortOrder
58416             var so = [];
58417             for(var i = 0; i < cm.config.length; i++ ) {
58418                 
58419                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58420                     continue; // dont' bother, it's not in sort list or being set.
58421                 }
58422                 
58423                 so.push(cm.config[i].dataIndex);
58424             };
58425             dm.sortOrder = so;
58426         }
58427         
58428         
58429         dm.sort(cm.getDataIndex(index));
58430     },
58431
58432
58433     destroy : function(){
58434         if(this.colMenu){
58435             this.colMenu.removeAll();
58436             Roo.menu.MenuMgr.unregister(this.colMenu);
58437             this.colMenu.getEl().remove();
58438             delete this.colMenu;
58439         }
58440         if(this.hmenu){
58441             this.hmenu.removeAll();
58442             Roo.menu.MenuMgr.unregister(this.hmenu);
58443             this.hmenu.getEl().remove();
58444             delete this.hmenu;
58445         }
58446         if(this.grid.enableColumnMove){
58447             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58448             if(dds){
58449                 for(var dd in dds){
58450                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58451                         var elid = dds[dd].dragElId;
58452                         dds[dd].unreg();
58453                         Roo.get(elid).remove();
58454                     } else if(dds[dd].config.isTarget){
58455                         dds[dd].proxyTop.remove();
58456                         dds[dd].proxyBottom.remove();
58457                         dds[dd].unreg();
58458                     }
58459                     if(Roo.dd.DDM.locationCache[dd]){
58460                         delete Roo.dd.DDM.locationCache[dd];
58461                     }
58462                 }
58463                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58464             }
58465         }
58466         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58467         this.bind(null, null);
58468         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58469     },
58470
58471     handleLockChange : function(){
58472         this.refresh(true);
58473     },
58474
58475     onDenyColumnLock : function(){
58476
58477     },
58478
58479     onDenyColumnHide : function(){
58480
58481     },
58482
58483     handleHdMenuClick : function(item){
58484         var index = this.hdCtxIndex;
58485         var cm = this.cm, ds = this.ds;
58486         switch(item.id){
58487             case "asc":
58488                 ds.sort(cm.getDataIndex(index), "ASC");
58489                 break;
58490             case "desc":
58491                 ds.sort(cm.getDataIndex(index), "DESC");
58492                 break;
58493             case "lock":
58494                 var lc = cm.getLockedCount();
58495                 if(cm.getColumnCount(true) <= lc+1){
58496                     this.onDenyColumnLock();
58497                     return;
58498                 }
58499                 if(lc != index){
58500                     cm.setLocked(index, true, true);
58501                     cm.moveColumn(index, lc);
58502                     this.grid.fireEvent("columnmove", index, lc);
58503                 }else{
58504                     cm.setLocked(index, true);
58505                 }
58506             break;
58507             case "unlock":
58508                 var lc = cm.getLockedCount();
58509                 if((lc-1) != index){
58510                     cm.setLocked(index, false, true);
58511                     cm.moveColumn(index, lc-1);
58512                     this.grid.fireEvent("columnmove", index, lc-1);
58513                 }else{
58514                     cm.setLocked(index, false);
58515                 }
58516             break;
58517             case 'wider': // used to expand cols on touch..
58518             case 'narrow':
58519                 var cw = cm.getColumnWidth(index);
58520                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58521                 cw = Math.max(0, cw);
58522                 cw = Math.min(cw,4000);
58523                 cm.setColumnWidth(index, cw);
58524                 break;
58525                 
58526             default:
58527                 index = cm.getIndexById(item.id.substr(4));
58528                 if(index != -1){
58529                     if(item.checked && cm.getColumnCount(true) <= 1){
58530                         this.onDenyColumnHide();
58531                         return false;
58532                     }
58533                     cm.setHidden(index, item.checked);
58534                 }
58535         }
58536         return true;
58537     },
58538
58539     beforeColMenuShow : function(){
58540         var cm = this.cm,  colCount = cm.getColumnCount();
58541         this.colMenu.removeAll();
58542         for(var i = 0; i < colCount; i++){
58543             this.colMenu.add(new Roo.menu.CheckItem({
58544                 id: "col-"+cm.getColumnId(i),
58545                 text: cm.getColumnHeader(i),
58546                 checked: !cm.isHidden(i),
58547                 hideOnClick:false
58548             }));
58549         }
58550     },
58551
58552     handleHdCtx : function(g, index, e){
58553         e.stopEvent();
58554         var hd = this.getHeaderCell(index);
58555         this.hdCtxIndex = index;
58556         var ms = this.hmenu.items, cm = this.cm;
58557         ms.get("asc").setDisabled(!cm.isSortable(index));
58558         ms.get("desc").setDisabled(!cm.isSortable(index));
58559         if(this.grid.enableColLock !== false){
58560             ms.get("lock").setDisabled(cm.isLocked(index));
58561             ms.get("unlock").setDisabled(!cm.isLocked(index));
58562         }
58563         this.hmenu.show(hd, "tl-bl");
58564     },
58565
58566     handleHdOver : function(e){
58567         var hd = this.findHeaderCell(e.getTarget());
58568         if(hd && !this.headersDisabled){
58569             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
58570                this.fly(hd).addClass("x-grid-hd-over");
58571             }
58572         }
58573     },
58574
58575     handleHdOut : function(e){
58576         var hd = this.findHeaderCell(e.getTarget());
58577         if(hd){
58578             this.fly(hd).removeClass("x-grid-hd-over");
58579         }
58580     },
58581
58582     handleSplitDblClick : function(e, t){
58583         var i = this.getCellIndex(t);
58584         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
58585             this.autoSizeColumn(i, true);
58586             this.layout();
58587         }
58588     },
58589
58590     render : function(){
58591
58592         var cm = this.cm;
58593         var colCount = cm.getColumnCount();
58594
58595         if(this.grid.monitorWindowResize === true){
58596             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58597         }
58598         var header = this.renderHeaders();
58599         var body = this.templates.body.apply({rows:""});
58600         var html = this.templates.master.apply({
58601             lockedBody: body,
58602             body: body,
58603             lockedHeader: header[0],
58604             header: header[1]
58605         });
58606
58607         //this.updateColumns();
58608
58609         this.grid.getGridEl().dom.innerHTML = html;
58610
58611         this.initElements();
58612         
58613         // a kludge to fix the random scolling effect in webkit
58614         this.el.on("scroll", function() {
58615             this.el.dom.scrollTop=0; // hopefully not recursive..
58616         },this);
58617
58618         this.scroller.on("scroll", this.handleScroll, this);
58619         this.lockedBody.on("mousewheel", this.handleWheel, this);
58620         this.mainBody.on("mousewheel", this.handleWheel, this);
58621
58622         this.mainHd.on("mouseover", this.handleHdOver, this);
58623         this.mainHd.on("mouseout", this.handleHdOut, this);
58624         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
58625                 {delegate: "."+this.splitClass});
58626
58627         this.lockedHd.on("mouseover", this.handleHdOver, this);
58628         this.lockedHd.on("mouseout", this.handleHdOut, this);
58629         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
58630                 {delegate: "."+this.splitClass});
58631
58632         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
58633             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58634         }
58635
58636         this.updateSplitters();
58637
58638         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
58639             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58640             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58641         }
58642
58643         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
58644             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
58645             this.hmenu.add(
58646                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
58647                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
58648             );
58649             if(this.grid.enableColLock !== false){
58650                 this.hmenu.add('-',
58651                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
58652                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
58653                 );
58654             }
58655             if (Roo.isTouch) {
58656                  this.hmenu.add('-',
58657                     {id:"wider", text: this.columnsWiderText},
58658                     {id:"narrow", text: this.columnsNarrowText }
58659                 );
58660                 
58661                  
58662             }
58663             
58664             if(this.grid.enableColumnHide !== false){
58665
58666                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
58667                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
58668                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
58669
58670                 this.hmenu.add('-',
58671                     {id:"columns", text: this.columnsText, menu: this.colMenu}
58672                 );
58673             }
58674             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
58675
58676             this.grid.on("headercontextmenu", this.handleHdCtx, this);
58677         }
58678
58679         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
58680             this.dd = new Roo.grid.GridDragZone(this.grid, {
58681                 ddGroup : this.grid.ddGroup || 'GridDD'
58682             });
58683             
58684         }
58685
58686         /*
58687         for(var i = 0; i < colCount; i++){
58688             if(cm.isHidden(i)){
58689                 this.hideColumn(i);
58690             }
58691             if(cm.config[i].align){
58692                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
58693                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
58694             }
58695         }*/
58696         
58697         this.updateHeaderSortState();
58698
58699         this.beforeInitialResize();
58700         this.layout(true);
58701
58702         // two part rendering gives faster view to the user
58703         this.renderPhase2.defer(1, this);
58704     },
58705
58706     renderPhase2 : function(){
58707         // render the rows now
58708         this.refresh();
58709         if(this.grid.autoSizeColumns){
58710             this.autoSizeColumns();
58711         }
58712     },
58713
58714     beforeInitialResize : function(){
58715
58716     },
58717
58718     onColumnSplitterMoved : function(i, w){
58719         this.userResized = true;
58720         var cm = this.grid.colModel;
58721         cm.setColumnWidth(i, w, true);
58722         var cid = cm.getColumnId(i);
58723         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58724         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58725         this.updateSplitters();
58726         this.layout();
58727         this.grid.fireEvent("columnresize", i, w);
58728     },
58729
58730     syncRowHeights : function(startIndex, endIndex){
58731         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58732             startIndex = startIndex || 0;
58733             var mrows = this.getBodyTable().rows;
58734             var lrows = this.getLockedTable().rows;
58735             var len = mrows.length-1;
58736             endIndex = Math.min(endIndex || len, len);
58737             for(var i = startIndex; i <= endIndex; i++){
58738                 var m = mrows[i], l = lrows[i];
58739                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58740                 m.style.height = l.style.height = h + "px";
58741             }
58742         }
58743     },
58744
58745     layout : function(initialRender, is2ndPass)
58746     {
58747         var g = this.grid;
58748         var auto = g.autoHeight;
58749         var scrollOffset = 16;
58750         var c = g.getGridEl(), cm = this.cm,
58751                 expandCol = g.autoExpandColumn,
58752                 gv = this;
58753         //c.beginMeasure();
58754
58755         if(!c.dom.offsetWidth){ // display:none?
58756             if(initialRender){
58757                 this.lockedWrap.show();
58758                 this.mainWrap.show();
58759             }
58760             return;
58761         }
58762
58763         var hasLock = this.cm.isLocked(0);
58764
58765         var tbh = this.headerPanel.getHeight();
58766         var bbh = this.footerPanel.getHeight();
58767
58768         if(auto){
58769             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58770             var newHeight = ch + c.getBorderWidth("tb");
58771             if(g.maxHeight){
58772                 newHeight = Math.min(g.maxHeight, newHeight);
58773             }
58774             c.setHeight(newHeight);
58775         }
58776
58777         if(g.autoWidth){
58778             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58779         }
58780
58781         var s = this.scroller;
58782
58783         var csize = c.getSize(true);
58784
58785         this.el.setSize(csize.width, csize.height);
58786
58787         this.headerPanel.setWidth(csize.width);
58788         this.footerPanel.setWidth(csize.width);
58789
58790         var hdHeight = this.mainHd.getHeight();
58791         var vw = csize.width;
58792         var vh = csize.height - (tbh + bbh);
58793
58794         s.setSize(vw, vh);
58795
58796         var bt = this.getBodyTable();
58797         
58798         if(cm.getLockedCount() == cm.config.length){
58799             bt = this.getLockedTable();
58800         }
58801         
58802         var ltWidth = hasLock ?
58803                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58804
58805         var scrollHeight = bt.offsetHeight;
58806         var scrollWidth = ltWidth + bt.offsetWidth;
58807         var vscroll = false, hscroll = false;
58808
58809         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58810
58811         var lw = this.lockedWrap, mw = this.mainWrap;
58812         var lb = this.lockedBody, mb = this.mainBody;
58813
58814         setTimeout(function(){
58815             var t = s.dom.offsetTop;
58816             var w = s.dom.clientWidth,
58817                 h = s.dom.clientHeight;
58818
58819             lw.setTop(t);
58820             lw.setSize(ltWidth, h);
58821
58822             mw.setLeftTop(ltWidth, t);
58823             mw.setSize(w-ltWidth, h);
58824
58825             lb.setHeight(h-hdHeight);
58826             mb.setHeight(h-hdHeight);
58827
58828             if(is2ndPass !== true && !gv.userResized && expandCol){
58829                 // high speed resize without full column calculation
58830                 
58831                 var ci = cm.getIndexById(expandCol);
58832                 if (ci < 0) {
58833                     ci = cm.findColumnIndex(expandCol);
58834                 }
58835                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58836                 var expandId = cm.getColumnId(ci);
58837                 var  tw = cm.getTotalWidth(false);
58838                 var currentWidth = cm.getColumnWidth(ci);
58839                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58840                 if(currentWidth != cw){
58841                     cm.setColumnWidth(ci, cw, true);
58842                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58843                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58844                     gv.updateSplitters();
58845                     gv.layout(false, true);
58846                 }
58847             }
58848
58849             if(initialRender){
58850                 lw.show();
58851                 mw.show();
58852             }
58853             //c.endMeasure();
58854         }, 10);
58855     },
58856
58857     onWindowResize : function(){
58858         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58859             return;
58860         }
58861         this.layout();
58862     },
58863
58864     appendFooter : function(parentEl){
58865         return null;
58866     },
58867
58868     sortAscText : "Sort Ascending",
58869     sortDescText : "Sort Descending",
58870     lockText : "Lock Column",
58871     unlockText : "Unlock Column",
58872     columnsText : "Columns",
58873  
58874     columnsWiderText : "Wider",
58875     columnsNarrowText : "Thinner"
58876 });
58877
58878
58879 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58880     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58881     this.proxy.el.addClass('x-grid3-col-dd');
58882 };
58883
58884 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58885     handleMouseDown : function(e){
58886
58887     },
58888
58889     callHandleMouseDown : function(e){
58890         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58891     }
58892 });
58893 /*
58894  * Based on:
58895  * Ext JS Library 1.1.1
58896  * Copyright(c) 2006-2007, Ext JS, LLC.
58897  *
58898  * Originally Released Under LGPL - original licence link has changed is not relivant.
58899  *
58900  * Fork - LGPL
58901  * <script type="text/javascript">
58902  */
58903  /**
58904  * @extends Roo.dd.DDProxy
58905  * @class Roo.grid.SplitDragZone
58906  * Support for Column Header resizing
58907  * @constructor
58908  * @param {Object} config
58909  */
58910 // private
58911 // This is a support class used internally by the Grid components
58912 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58913     this.grid = grid;
58914     this.view = grid.getView();
58915     this.proxy = this.view.resizeProxy;
58916     Roo.grid.SplitDragZone.superclass.constructor.call(
58917         this,
58918         hd, // ID
58919         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58920         {  // CONFIG
58921             dragElId : Roo.id(this.proxy.dom),
58922             resizeFrame:false
58923         }
58924     );
58925     
58926     this.setHandleElId(Roo.id(hd));
58927     if (hd2 !== false) {
58928         this.setOuterHandleElId(Roo.id(hd2));
58929     }
58930     
58931     this.scroll = false;
58932 };
58933 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58934     fly: Roo.Element.fly,
58935
58936     b4StartDrag : function(x, y){
58937         this.view.headersDisabled = true;
58938         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58939                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58940         );
58941         this.proxy.setHeight(h);
58942         
58943         // for old system colWidth really stored the actual width?
58944         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58945         // which in reality did not work.. - it worked only for fixed sizes
58946         // for resizable we need to use actual sizes.
58947         var w = this.cm.getColumnWidth(this.cellIndex);
58948         if (!this.view.mainWrap) {
58949             // bootstrap.
58950             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58951         }
58952         
58953         
58954         
58955         // this was w-this.grid.minColumnWidth;
58956         // doesnt really make sense? - w = thie curren width or the rendered one?
58957         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58958         this.resetConstraints();
58959         this.setXConstraint(minw, 1000);
58960         this.setYConstraint(0, 0);
58961         this.minX = x - minw;
58962         this.maxX = x + 1000;
58963         this.startPos = x;
58964         if (!this.view.mainWrap) { // this is Bootstrap code..
58965             this.getDragEl().style.display='block';
58966         }
58967         
58968         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58969     },
58970
58971
58972     handleMouseDown : function(e){
58973         ev = Roo.EventObject.setEvent(e);
58974         var t = this.fly(ev.getTarget());
58975         if(t.hasClass("x-grid-split")){
58976             this.cellIndex = this.view.getCellIndex(t.dom);
58977             this.split = t.dom;
58978             this.cm = this.grid.colModel;
58979             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58980                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58981             }
58982         }
58983     },
58984
58985     endDrag : function(e){
58986         this.view.headersDisabled = false;
58987         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58988         var diff = endX - this.startPos;
58989         // 
58990         var w = this.cm.getColumnWidth(this.cellIndex);
58991         if (!this.view.mainWrap) {
58992             w = 0;
58993         }
58994         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58995     },
58996
58997     autoOffset : function(){
58998         this.setDelta(0,0);
58999     }
59000 });/*
59001  * Based on:
59002  * Ext JS Library 1.1.1
59003  * Copyright(c) 2006-2007, Ext JS, LLC.
59004  *
59005  * Originally Released Under LGPL - original licence link has changed is not relivant.
59006  *
59007  * Fork - LGPL
59008  * <script type="text/javascript">
59009  */
59010  
59011 // private
59012 // This is a support class used internally by the Grid components
59013 Roo.grid.GridDragZone = function(grid, config){
59014     this.view = grid.getView();
59015     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59016     if(this.view.lockedBody){
59017         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59018         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59019     }
59020     this.scroll = false;
59021     this.grid = grid;
59022     this.ddel = document.createElement('div');
59023     this.ddel.className = 'x-grid-dd-wrap';
59024 };
59025
59026 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59027     ddGroup : "GridDD",
59028
59029     getDragData : function(e){
59030         var t = Roo.lib.Event.getTarget(e);
59031         var rowIndex = this.view.findRowIndex(t);
59032         var sm = this.grid.selModel;
59033             
59034         //Roo.log(rowIndex);
59035         
59036         if (sm.getSelectedCell) {
59037             // cell selection..
59038             if (!sm.getSelectedCell()) {
59039                 return false;
59040             }
59041             if (rowIndex != sm.getSelectedCell()[0]) {
59042                 return false;
59043             }
59044         
59045         }
59046         if (sm.getSelections && sm.getSelections().length < 1) {
59047             return false;
59048         }
59049         
59050         
59051         // before it used to all dragging of unseleted... - now we dont do that.
59052         if(rowIndex !== false){
59053             
59054             // if editorgrid.. 
59055             
59056             
59057             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59058                
59059             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59060               //  
59061             //}
59062             if (e.hasModifier()){
59063                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59064             }
59065             
59066             Roo.log("getDragData");
59067             
59068             return {
59069                 grid: this.grid,
59070                 ddel: this.ddel,
59071                 rowIndex: rowIndex,
59072                 selections: sm.getSelections ? sm.getSelections() : (
59073                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59074             };
59075         }
59076         return false;
59077     },
59078     
59079     
59080     onInitDrag : function(e){
59081         var data = this.dragData;
59082         this.ddel.innerHTML = this.grid.getDragDropText();
59083         this.proxy.update(this.ddel);
59084         // fire start drag?
59085     },
59086
59087     afterRepair : function(){
59088         this.dragging = false;
59089     },
59090
59091     getRepairXY : function(e, data){
59092         return false;
59093     },
59094
59095     onEndDrag : function(data, e){
59096         // fire end drag?
59097     },
59098
59099     onValidDrop : function(dd, e, id){
59100         // fire drag drop?
59101         this.hideProxy();
59102     },
59103
59104     beforeInvalidDrop : function(e, id){
59105
59106     }
59107 });/*
59108  * Based on:
59109  * Ext JS Library 1.1.1
59110  * Copyright(c) 2006-2007, Ext JS, LLC.
59111  *
59112  * Originally Released Under LGPL - original licence link has changed is not relivant.
59113  *
59114  * Fork - LGPL
59115  * <script type="text/javascript">
59116  */
59117  
59118
59119 /**
59120  * @class Roo.grid.ColumnModel
59121  * @extends Roo.util.Observable
59122  * This is the default implementation of a ColumnModel used by the Grid. It defines
59123  * the columns in the grid.
59124  * <br>Usage:<br>
59125  <pre><code>
59126  var colModel = new Roo.grid.ColumnModel([
59127         {header: "Ticker", width: 60, sortable: true, locked: true},
59128         {header: "Company Name", width: 150, sortable: true},
59129         {header: "Market Cap.", width: 100, sortable: true},
59130         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59131         {header: "Employees", width: 100, sortable: true, resizable: false}
59132  ]);
59133  </code></pre>
59134  * <p>
59135  
59136  * The config options listed for this class are options which may appear in each
59137  * individual column definition.
59138  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59139  * @constructor
59140  * @param {Object} config An Array of column config objects. See this class's
59141  * config objects for details.
59142 */
59143 Roo.grid.ColumnModel = function(config){
59144         /**
59145      * The config passed into the constructor
59146      */
59147     this.config = []; //config;
59148     this.lookup = {};
59149
59150     // if no id, create one
59151     // if the column does not have a dataIndex mapping,
59152     // map it to the order it is in the config
59153     for(var i = 0, len = config.length; i < len; i++){
59154         this.addColumn(config[i]);
59155         
59156     }
59157
59158     /**
59159      * The width of columns which have no width specified (defaults to 100)
59160      * @type Number
59161      */
59162     this.defaultWidth = 100;
59163
59164     /**
59165      * Default sortable of columns which have no sortable specified (defaults to false)
59166      * @type Boolean
59167      */
59168     this.defaultSortable = false;
59169
59170     this.addEvents({
59171         /**
59172              * @event widthchange
59173              * Fires when the width of a column changes.
59174              * @param {ColumnModel} this
59175              * @param {Number} columnIndex The column index
59176              * @param {Number} newWidth The new width
59177              */
59178             "widthchange": true,
59179         /**
59180              * @event headerchange
59181              * Fires when the text of a header changes.
59182              * @param {ColumnModel} this
59183              * @param {Number} columnIndex The column index
59184              * @param {Number} newText The new header text
59185              */
59186             "headerchange": true,
59187         /**
59188              * @event hiddenchange
59189              * Fires when a column is hidden or "unhidden".
59190              * @param {ColumnModel} this
59191              * @param {Number} columnIndex The column index
59192              * @param {Boolean} hidden true if hidden, false otherwise
59193              */
59194             "hiddenchange": true,
59195             /**
59196          * @event columnmoved
59197          * Fires when a column is moved.
59198          * @param {ColumnModel} this
59199          * @param {Number} oldIndex
59200          * @param {Number} newIndex
59201          */
59202         "columnmoved" : true,
59203         /**
59204          * @event columlockchange
59205          * Fires when a column's locked state is changed
59206          * @param {ColumnModel} this
59207          * @param {Number} colIndex
59208          * @param {Boolean} locked true if locked
59209          */
59210         "columnlockchange" : true
59211     });
59212     Roo.grid.ColumnModel.superclass.constructor.call(this);
59213 };
59214 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59215     /**
59216      * @cfg {String} header The header text to display in the Grid view.
59217      */
59218         /**
59219      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59220      */
59221         /**
59222      * @cfg {String} smHeader Header at Bootsrap Small width
59223      */
59224         /**
59225      * @cfg {String} mdHeader Header at Bootsrap Medium width
59226      */
59227         /**
59228      * @cfg {String} lgHeader Header at Bootsrap Large width
59229      */
59230         /**
59231      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59232      */
59233     /**
59234      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59235      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59236      * specified, the column's index is used as an index into the Record's data Array.
59237      */
59238     /**
59239      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59240      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59241      */
59242     /**
59243      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59244      * Defaults to the value of the {@link #defaultSortable} property.
59245      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59246      */
59247     /**
59248      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59249      */
59250     /**
59251      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59252      */
59253     /**
59254      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59255      */
59256     /**
59257      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59258      */
59259     /**
59260      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59261      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59262      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59263      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59264      */
59265        /**
59266      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59267      */
59268     /**
59269      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59270      */
59271     /**
59272      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59273      */
59274     /**
59275      * @cfg {String} cursor (Optional)
59276      */
59277     /**
59278      * @cfg {String} tooltip (Optional)
59279      */
59280     /**
59281      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59282      */
59283     /**
59284      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59285      */
59286     /**
59287      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59288      */
59289     /**
59290      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59291      */
59292         /**
59293      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59294      */
59295     /**
59296      * Returns the id of the column at the specified index.
59297      * @param {Number} index The column index
59298      * @return {String} the id
59299      */
59300     getColumnId : function(index){
59301         return this.config[index].id;
59302     },
59303
59304     /**
59305      * Returns the column for a specified id.
59306      * @param {String} id The column id
59307      * @return {Object} the column
59308      */
59309     getColumnById : function(id){
59310         return this.lookup[id];
59311     },
59312
59313     
59314     /**
59315      * Returns the column Object for a specified dataIndex.
59316      * @param {String} dataIndex The column dataIndex
59317      * @return {Object|Boolean} the column or false if not found
59318      */
59319     getColumnByDataIndex: function(dataIndex){
59320         var index = this.findColumnIndex(dataIndex);
59321         return index > -1 ? this.config[index] : false;
59322     },
59323     
59324     /**
59325      * Returns the index for a specified column id.
59326      * @param {String} id The column id
59327      * @return {Number} the index, or -1 if not found
59328      */
59329     getIndexById : function(id){
59330         for(var i = 0, len = this.config.length; i < len; i++){
59331             if(this.config[i].id == id){
59332                 return i;
59333             }
59334         }
59335         return -1;
59336     },
59337     
59338     /**
59339      * Returns the index for a specified column dataIndex.
59340      * @param {String} dataIndex The column dataIndex
59341      * @return {Number} the index, or -1 if not found
59342      */
59343     
59344     findColumnIndex : function(dataIndex){
59345         for(var i = 0, len = this.config.length; i < len; i++){
59346             if(this.config[i].dataIndex == dataIndex){
59347                 return i;
59348             }
59349         }
59350         return -1;
59351     },
59352     
59353     
59354     moveColumn : function(oldIndex, newIndex){
59355         var c = this.config[oldIndex];
59356         this.config.splice(oldIndex, 1);
59357         this.config.splice(newIndex, 0, c);
59358         this.dataMap = null;
59359         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59360     },
59361
59362     isLocked : function(colIndex){
59363         return this.config[colIndex].locked === true;
59364     },
59365
59366     setLocked : function(colIndex, value, suppressEvent){
59367         if(this.isLocked(colIndex) == value){
59368             return;
59369         }
59370         this.config[colIndex].locked = value;
59371         if(!suppressEvent){
59372             this.fireEvent("columnlockchange", this, colIndex, value);
59373         }
59374     },
59375
59376     getTotalLockedWidth : function(){
59377         var totalWidth = 0;
59378         for(var i = 0; i < this.config.length; i++){
59379             if(this.isLocked(i) && !this.isHidden(i)){
59380                 this.totalWidth += this.getColumnWidth(i);
59381             }
59382         }
59383         return totalWidth;
59384     },
59385
59386     getLockedCount : function(){
59387         for(var i = 0, len = this.config.length; i < len; i++){
59388             if(!this.isLocked(i)){
59389                 return i;
59390             }
59391         }
59392         
59393         return this.config.length;
59394     },
59395
59396     /**
59397      * Returns the number of columns.
59398      * @return {Number}
59399      */
59400     getColumnCount : function(visibleOnly){
59401         if(visibleOnly === true){
59402             var c = 0;
59403             for(var i = 0, len = this.config.length; i < len; i++){
59404                 if(!this.isHidden(i)){
59405                     c++;
59406                 }
59407             }
59408             return c;
59409         }
59410         return this.config.length;
59411     },
59412
59413     /**
59414      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59415      * @param {Function} fn
59416      * @param {Object} scope (optional)
59417      * @return {Array} result
59418      */
59419     getColumnsBy : function(fn, scope){
59420         var r = [];
59421         for(var i = 0, len = this.config.length; i < len; i++){
59422             var c = this.config[i];
59423             if(fn.call(scope||this, c, i) === true){
59424                 r[r.length] = c;
59425             }
59426         }
59427         return r;
59428     },
59429
59430     /**
59431      * Returns true if the specified column is sortable.
59432      * @param {Number} col The column index
59433      * @return {Boolean}
59434      */
59435     isSortable : function(col){
59436         if(typeof this.config[col].sortable == "undefined"){
59437             return this.defaultSortable;
59438         }
59439         return this.config[col].sortable;
59440     },
59441
59442     /**
59443      * Returns the rendering (formatting) function defined for the column.
59444      * @param {Number} col The column index.
59445      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59446      */
59447     getRenderer : function(col){
59448         if(!this.config[col].renderer){
59449             return Roo.grid.ColumnModel.defaultRenderer;
59450         }
59451         return this.config[col].renderer;
59452     },
59453
59454     /**
59455      * Sets the rendering (formatting) function for a column.
59456      * @param {Number} col The column index
59457      * @param {Function} fn The function to use to process the cell's raw data
59458      * to return HTML markup for the grid view. The render function is called with
59459      * the following parameters:<ul>
59460      * <li>Data value.</li>
59461      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59462      * <li>css A CSS style string to apply to the table cell.</li>
59463      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59464      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59465      * <li>Row index</li>
59466      * <li>Column index</li>
59467      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59468      */
59469     setRenderer : function(col, fn){
59470         this.config[col].renderer = fn;
59471     },
59472
59473     /**
59474      * Returns the width for the specified column.
59475      * @param {Number} col The column index
59476      * @param (optional) {String} gridSize bootstrap width size.
59477      * @return {Number}
59478      */
59479     getColumnWidth : function(col, gridSize)
59480         {
59481                 var cfg = this.config[col];
59482                 
59483                 if (typeof(gridSize) == 'undefined') {
59484                         return cfg.width * 1 || this.defaultWidth;
59485                 }
59486                 if (gridSize === false) { // if we set it..
59487                         return cfg.width || false;
59488                 }
59489                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59490                 
59491                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59492                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59493                                 continue;
59494                         }
59495                         return cfg[ sizes[i] ];
59496                 }
59497                 return 1;
59498                 
59499     },
59500
59501     /**
59502      * Sets the width for a column.
59503      * @param {Number} col The column index
59504      * @param {Number} width The new width
59505      */
59506     setColumnWidth : function(col, width, suppressEvent){
59507         this.config[col].width = width;
59508         this.totalWidth = null;
59509         if(!suppressEvent){
59510              this.fireEvent("widthchange", this, col, width);
59511         }
59512     },
59513
59514     /**
59515      * Returns the total width of all columns.
59516      * @param {Boolean} includeHidden True to include hidden column widths
59517      * @return {Number}
59518      */
59519     getTotalWidth : function(includeHidden){
59520         if(!this.totalWidth){
59521             this.totalWidth = 0;
59522             for(var i = 0, len = this.config.length; i < len; i++){
59523                 if(includeHidden || !this.isHidden(i)){
59524                     this.totalWidth += this.getColumnWidth(i);
59525                 }
59526             }
59527         }
59528         return this.totalWidth;
59529     },
59530
59531     /**
59532      * Returns the header for the specified column.
59533      * @param {Number} col The column index
59534      * @return {String}
59535      */
59536     getColumnHeader : function(col){
59537         return this.config[col].header;
59538     },
59539
59540     /**
59541      * Sets the header for a column.
59542      * @param {Number} col The column index
59543      * @param {String} header The new header
59544      */
59545     setColumnHeader : function(col, header){
59546         this.config[col].header = header;
59547         this.fireEvent("headerchange", this, col, header);
59548     },
59549
59550     /**
59551      * Returns the tooltip for the specified column.
59552      * @param {Number} col The column index
59553      * @return {String}
59554      */
59555     getColumnTooltip : function(col){
59556             return this.config[col].tooltip;
59557     },
59558     /**
59559      * Sets the tooltip for a column.
59560      * @param {Number} col The column index
59561      * @param {String} tooltip The new tooltip
59562      */
59563     setColumnTooltip : function(col, tooltip){
59564             this.config[col].tooltip = tooltip;
59565     },
59566
59567     /**
59568      * Returns the dataIndex for the specified column.
59569      * @param {Number} col The column index
59570      * @return {Number}
59571      */
59572     getDataIndex : function(col){
59573         return this.config[col].dataIndex;
59574     },
59575
59576     /**
59577      * Sets the dataIndex for a column.
59578      * @param {Number} col The column index
59579      * @param {Number} dataIndex The new dataIndex
59580      */
59581     setDataIndex : function(col, dataIndex){
59582         this.config[col].dataIndex = dataIndex;
59583     },
59584
59585     
59586     
59587     /**
59588      * Returns true if the cell is editable.
59589      * @param {Number} colIndex The column index
59590      * @param {Number} rowIndex The row index - this is nto actually used..?
59591      * @return {Boolean}
59592      */
59593     isCellEditable : function(colIndex, rowIndex){
59594         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
59595     },
59596
59597     /**
59598      * Returns the editor defined for the cell/column.
59599      * return false or null to disable editing.
59600      * @param {Number} colIndex The column index
59601      * @param {Number} rowIndex The row index
59602      * @return {Object}
59603      */
59604     getCellEditor : function(colIndex, rowIndex){
59605         return this.config[colIndex].editor;
59606     },
59607
59608     /**
59609      * Sets if a column is editable.
59610      * @param {Number} col The column index
59611      * @param {Boolean} editable True if the column is editable
59612      */
59613     setEditable : function(col, editable){
59614         this.config[col].editable = editable;
59615     },
59616
59617
59618     /**
59619      * Returns true if the column is hidden.
59620      * @param {Number} colIndex The column index
59621      * @return {Boolean}
59622      */
59623     isHidden : function(colIndex){
59624         return this.config[colIndex].hidden;
59625     },
59626
59627
59628     /**
59629      * Returns true if the column width cannot be changed
59630      */
59631     isFixed : function(colIndex){
59632         return this.config[colIndex].fixed;
59633     },
59634
59635     /**
59636      * Returns true if the column can be resized
59637      * @return {Boolean}
59638      */
59639     isResizable : function(colIndex){
59640         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
59641     },
59642     /**
59643      * Sets if a column is hidden.
59644      * @param {Number} colIndex The column index
59645      * @param {Boolean} hidden True if the column is hidden
59646      */
59647     setHidden : function(colIndex, hidden){
59648         this.config[colIndex].hidden = hidden;
59649         this.totalWidth = null;
59650         this.fireEvent("hiddenchange", this, colIndex, hidden);
59651     },
59652
59653     /**
59654      * Sets the editor for a column.
59655      * @param {Number} col The column index
59656      * @param {Object} editor The editor object
59657      */
59658     setEditor : function(col, editor){
59659         this.config[col].editor = editor;
59660     },
59661     /**
59662      * Add a column (experimental...) - defaults to adding to the end..
59663      * @param {Object} config 
59664     */
59665     addColumn : function(c)
59666     {
59667     
59668         var i = this.config.length;
59669         this.config[i] = c;
59670         
59671         if(typeof c.dataIndex == "undefined"){
59672             c.dataIndex = i;
59673         }
59674         if(typeof c.renderer == "string"){
59675             c.renderer = Roo.util.Format[c.renderer];
59676         }
59677         if(typeof c.id == "undefined"){
59678             c.id = Roo.id();
59679         }
59680         if(c.editor && c.editor.xtype){
59681             c.editor  = Roo.factory(c.editor, Roo.grid);
59682         }
59683         if(c.editor && c.editor.isFormField){
59684             c.editor = new Roo.grid.GridEditor(c.editor);
59685         }
59686         this.lookup[c.id] = c;
59687     }
59688     
59689 });
59690
59691 Roo.grid.ColumnModel.defaultRenderer = function(value)
59692 {
59693     if(typeof value == "object") {
59694         return value;
59695     }
59696         if(typeof value == "string" && value.length < 1){
59697             return "&#160;";
59698         }
59699     
59700         return String.format("{0}", value);
59701 };
59702
59703 // Alias for backwards compatibility
59704 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
59705 /*
59706  * Based on:
59707  * Ext JS Library 1.1.1
59708  * Copyright(c) 2006-2007, Ext JS, LLC.
59709  *
59710  * Originally Released Under LGPL - original licence link has changed is not relivant.
59711  *
59712  * Fork - LGPL
59713  * <script type="text/javascript">
59714  */
59715
59716 /**
59717  * @class Roo.grid.AbstractSelectionModel
59718  * @extends Roo.util.Observable
59719  * @abstract
59720  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59721  * implemented by descendant classes.  This class should not be directly instantiated.
59722  * @constructor
59723  */
59724 Roo.grid.AbstractSelectionModel = function(){
59725     this.locked = false;
59726     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59727 };
59728
59729 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59730     /** @ignore Called by the grid automatically. Do not call directly. */
59731     init : function(grid){
59732         this.grid = grid;
59733         this.initEvents();
59734     },
59735
59736     /**
59737      * Locks the selections.
59738      */
59739     lock : function(){
59740         this.locked = true;
59741     },
59742
59743     /**
59744      * Unlocks the selections.
59745      */
59746     unlock : function(){
59747         this.locked = false;
59748     },
59749
59750     /**
59751      * Returns true if the selections are locked.
59752      * @return {Boolean}
59753      */
59754     isLocked : function(){
59755         return this.locked;
59756     }
59757 });/*
59758  * Based on:
59759  * Ext JS Library 1.1.1
59760  * Copyright(c) 2006-2007, Ext JS, LLC.
59761  *
59762  * Originally Released Under LGPL - original licence link has changed is not relivant.
59763  *
59764  * Fork - LGPL
59765  * <script type="text/javascript">
59766  */
59767 /**
59768  * @extends Roo.grid.AbstractSelectionModel
59769  * @class Roo.grid.RowSelectionModel
59770  * The default SelectionModel used by {@link Roo.grid.Grid}.
59771  * It supports multiple selections and keyboard selection/navigation. 
59772  * @constructor
59773  * @param {Object} config
59774  */
59775 Roo.grid.RowSelectionModel = function(config){
59776     Roo.apply(this, config);
59777     this.selections = new Roo.util.MixedCollection(false, function(o){
59778         return o.id;
59779     });
59780
59781     this.last = false;
59782     this.lastActive = false;
59783
59784     this.addEvents({
59785         /**
59786         * @event selectionchange
59787         * Fires when the selection changes
59788         * @param {SelectionModel} this
59789         */
59790        "selectionchange" : true,
59791        /**
59792         * @event afterselectionchange
59793         * Fires after the selection changes (eg. by key press or clicking)
59794         * @param {SelectionModel} this
59795         */
59796        "afterselectionchange" : true,
59797        /**
59798         * @event beforerowselect
59799         * Fires when a row is selected being selected, return false to cancel.
59800         * @param {SelectionModel} this
59801         * @param {Number} rowIndex The selected index
59802         * @param {Boolean} keepExisting False if other selections will be cleared
59803         */
59804        "beforerowselect" : true,
59805        /**
59806         * @event rowselect
59807         * Fires when a row is selected.
59808         * @param {SelectionModel} this
59809         * @param {Number} rowIndex The selected index
59810         * @param {Roo.data.Record} r The record
59811         */
59812        "rowselect" : true,
59813        /**
59814         * @event rowdeselect
59815         * Fires when a row is deselected.
59816         * @param {SelectionModel} this
59817         * @param {Number} rowIndex The selected index
59818         */
59819         "rowdeselect" : true
59820     });
59821     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59822     this.locked = false;
59823 };
59824
59825 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59826     /**
59827      * @cfg {Boolean} singleSelect
59828      * True to allow selection of only one row at a time (defaults to false)
59829      */
59830     singleSelect : false,
59831
59832     // private
59833     initEvents : function(){
59834
59835         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59836             this.grid.on("mousedown", this.handleMouseDown, this);
59837         }else{ // allow click to work like normal
59838             this.grid.on("rowclick", this.handleDragableRowClick, this);
59839         }
59840         // bootstrap does not have a view..
59841         var view = this.grid.view ? this.grid.view : this.grid;
59842         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59843             "up" : function(e){
59844                 if(!e.shiftKey){
59845                     this.selectPrevious(e.shiftKey);
59846                 }else if(this.last !== false && this.lastActive !== false){
59847                     var last = this.last;
59848                     this.selectRange(this.last,  this.lastActive-1);
59849                     view.focusRow(this.lastActive);
59850                     if(last !== false){
59851                         this.last = last;
59852                     }
59853                 }else{
59854                     this.selectFirstRow();
59855                 }
59856                 this.fireEvent("afterselectionchange", this);
59857             },
59858             "down" : function(e){
59859                 if(!e.shiftKey){
59860                     this.selectNext(e.shiftKey);
59861                 }else if(this.last !== false && this.lastActive !== false){
59862                     var last = this.last;
59863                     this.selectRange(this.last,  this.lastActive+1);
59864                     view.focusRow(this.lastActive);
59865                     if(last !== false){
59866                         this.last = last;
59867                     }
59868                 }else{
59869                     this.selectFirstRow();
59870                 }
59871                 this.fireEvent("afterselectionchange", this);
59872             },
59873             scope: this
59874         });
59875
59876          
59877         view.on("refresh", this.onRefresh, this);
59878         view.on("rowupdated", this.onRowUpdated, this);
59879         view.on("rowremoved", this.onRemove, this);
59880     },
59881
59882     // private
59883     onRefresh : function(){
59884         var ds = this.grid.ds, i, v = this.grid.view;
59885         var s = this.selections;
59886         s.each(function(r){
59887             if((i = ds.indexOfId(r.id)) != -1){
59888                 v.onRowSelect(i);
59889                 s.add(ds.getAt(i)); // updating the selection relate data
59890             }else{
59891                 s.remove(r);
59892             }
59893         });
59894     },
59895
59896     // private
59897     onRemove : function(v, index, r){
59898         this.selections.remove(r);
59899     },
59900
59901     // private
59902     onRowUpdated : function(v, index, r){
59903         if(this.isSelected(r)){
59904             v.onRowSelect(index);
59905         }
59906     },
59907
59908     /**
59909      * Select records.
59910      * @param {Array} records The records to select
59911      * @param {Boolean} keepExisting (optional) True to keep existing selections
59912      */
59913     selectRecords : function(records, keepExisting){
59914         if(!keepExisting){
59915             this.clearSelections();
59916         }
59917         var ds = this.grid.ds;
59918         for(var i = 0, len = records.length; i < len; i++){
59919             this.selectRow(ds.indexOf(records[i]), true);
59920         }
59921     },
59922
59923     /**
59924      * Gets the number of selected rows.
59925      * @return {Number}
59926      */
59927     getCount : function(){
59928         return this.selections.length;
59929     },
59930
59931     /**
59932      * Selects the first row in the grid.
59933      */
59934     selectFirstRow : function(){
59935         this.selectRow(0);
59936     },
59937
59938     /**
59939      * Select the last row.
59940      * @param {Boolean} keepExisting (optional) True to keep existing selections
59941      */
59942     selectLastRow : function(keepExisting){
59943         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59944     },
59945
59946     /**
59947      * Selects the row immediately following the last selected row.
59948      * @param {Boolean} keepExisting (optional) True to keep existing selections
59949      */
59950     selectNext : function(keepExisting){
59951         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59952             this.selectRow(this.last+1, keepExisting);
59953             var view = this.grid.view ? this.grid.view : this.grid;
59954             view.focusRow(this.last);
59955         }
59956     },
59957
59958     /**
59959      * Selects the row that precedes the last selected row.
59960      * @param {Boolean} keepExisting (optional) True to keep existing selections
59961      */
59962     selectPrevious : function(keepExisting){
59963         if(this.last){
59964             this.selectRow(this.last-1, keepExisting);
59965             var view = this.grid.view ? this.grid.view : this.grid;
59966             view.focusRow(this.last);
59967         }
59968     },
59969
59970     /**
59971      * Returns the selected records
59972      * @return {Array} Array of selected records
59973      */
59974     getSelections : function(){
59975         return [].concat(this.selections.items);
59976     },
59977
59978     /**
59979      * Returns the first selected record.
59980      * @return {Record}
59981      */
59982     getSelected : function(){
59983         return this.selections.itemAt(0);
59984     },
59985
59986
59987     /**
59988      * Clears all selections.
59989      */
59990     clearSelections : function(fast){
59991         if(this.locked) {
59992             return;
59993         }
59994         if(fast !== true){
59995             var ds = this.grid.ds;
59996             var s = this.selections;
59997             s.each(function(r){
59998                 this.deselectRow(ds.indexOfId(r.id));
59999             }, this);
60000             s.clear();
60001         }else{
60002             this.selections.clear();
60003         }
60004         this.last = false;
60005     },
60006
60007
60008     /**
60009      * Selects all rows.
60010      */
60011     selectAll : function(){
60012         if(this.locked) {
60013             return;
60014         }
60015         this.selections.clear();
60016         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60017             this.selectRow(i, true);
60018         }
60019     },
60020
60021     /**
60022      * Returns True if there is a selection.
60023      * @return {Boolean}
60024      */
60025     hasSelection : function(){
60026         return this.selections.length > 0;
60027     },
60028
60029     /**
60030      * Returns True if the specified row is selected.
60031      * @param {Number/Record} record The record or index of the record to check
60032      * @return {Boolean}
60033      */
60034     isSelected : function(index){
60035         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60036         return (r && this.selections.key(r.id) ? true : false);
60037     },
60038
60039     /**
60040      * Returns True if the specified record id is selected.
60041      * @param {String} id The id of record to check
60042      * @return {Boolean}
60043      */
60044     isIdSelected : function(id){
60045         return (this.selections.key(id) ? true : false);
60046     },
60047
60048     // private
60049     handleMouseDown : function(e, t)
60050     {
60051         var view = this.grid.view ? this.grid.view : this.grid;
60052         var rowIndex;
60053         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60054             return;
60055         };
60056         if(e.shiftKey && this.last !== false){
60057             var last = this.last;
60058             this.selectRange(last, rowIndex, e.ctrlKey);
60059             this.last = last; // reset the last
60060             view.focusRow(rowIndex);
60061         }else{
60062             var isSelected = this.isSelected(rowIndex);
60063             if(e.button !== 0 && isSelected){
60064                 view.focusRow(rowIndex);
60065             }else if(e.ctrlKey && isSelected){
60066                 this.deselectRow(rowIndex);
60067             }else if(!isSelected){
60068                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60069                 view.focusRow(rowIndex);
60070             }
60071         }
60072         this.fireEvent("afterselectionchange", this);
60073     },
60074     // private
60075     handleDragableRowClick :  function(grid, rowIndex, e) 
60076     {
60077         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60078             this.selectRow(rowIndex, false);
60079             var view = this.grid.view ? this.grid.view : this.grid;
60080             view.focusRow(rowIndex);
60081              this.fireEvent("afterselectionchange", this);
60082         }
60083     },
60084     
60085     /**
60086      * Selects multiple rows.
60087      * @param {Array} rows Array of the indexes of the row to select
60088      * @param {Boolean} keepExisting (optional) True to keep existing selections
60089      */
60090     selectRows : function(rows, keepExisting){
60091         if(!keepExisting){
60092             this.clearSelections();
60093         }
60094         for(var i = 0, len = rows.length; i < len; i++){
60095             this.selectRow(rows[i], true);
60096         }
60097     },
60098
60099     /**
60100      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60101      * @param {Number} startRow The index of the first row in the range
60102      * @param {Number} endRow The index of the last row in the range
60103      * @param {Boolean} keepExisting (optional) True to retain existing selections
60104      */
60105     selectRange : function(startRow, endRow, keepExisting){
60106         if(this.locked) {
60107             return;
60108         }
60109         if(!keepExisting){
60110             this.clearSelections();
60111         }
60112         if(startRow <= endRow){
60113             for(var i = startRow; i <= endRow; i++){
60114                 this.selectRow(i, true);
60115             }
60116         }else{
60117             for(var i = startRow; i >= endRow; i--){
60118                 this.selectRow(i, true);
60119             }
60120         }
60121     },
60122
60123     /**
60124      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60125      * @param {Number} startRow The index of the first row in the range
60126      * @param {Number} endRow The index of the last row in the range
60127      */
60128     deselectRange : function(startRow, endRow, preventViewNotify){
60129         if(this.locked) {
60130             return;
60131         }
60132         for(var i = startRow; i <= endRow; i++){
60133             this.deselectRow(i, preventViewNotify);
60134         }
60135     },
60136
60137     /**
60138      * Selects a row.
60139      * @param {Number} row The index of the row to select
60140      * @param {Boolean} keepExisting (optional) True to keep existing selections
60141      */
60142     selectRow : function(index, keepExisting, preventViewNotify){
60143         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60144             return;
60145         }
60146         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60147             if(!keepExisting || this.singleSelect){
60148                 this.clearSelections();
60149             }
60150             var r = this.grid.ds.getAt(index);
60151             this.selections.add(r);
60152             this.last = this.lastActive = index;
60153             if(!preventViewNotify){
60154                 var view = this.grid.view ? this.grid.view : this.grid;
60155                 view.onRowSelect(index);
60156             }
60157             this.fireEvent("rowselect", this, index, r);
60158             this.fireEvent("selectionchange", this);
60159         }
60160     },
60161
60162     /**
60163      * Deselects a row.
60164      * @param {Number} row The index of the row to deselect
60165      */
60166     deselectRow : function(index, preventViewNotify){
60167         if(this.locked) {
60168             return;
60169         }
60170         if(this.last == index){
60171             this.last = false;
60172         }
60173         if(this.lastActive == index){
60174             this.lastActive = false;
60175         }
60176         var r = this.grid.ds.getAt(index);
60177         this.selections.remove(r);
60178         if(!preventViewNotify){
60179             var view = this.grid.view ? this.grid.view : this.grid;
60180             view.onRowDeselect(index);
60181         }
60182         this.fireEvent("rowdeselect", this, index);
60183         this.fireEvent("selectionchange", this);
60184     },
60185
60186     // private
60187     restoreLast : function(){
60188         if(this._last){
60189             this.last = this._last;
60190         }
60191     },
60192
60193     // private
60194     acceptsNav : function(row, col, cm){
60195         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60196     },
60197
60198     // private
60199     onEditorKey : function(field, e){
60200         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60201         if(k == e.TAB){
60202             e.stopEvent();
60203             ed.completeEdit();
60204             if(e.shiftKey){
60205                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60206             }else{
60207                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60208             }
60209         }else if(k == e.ENTER && !e.ctrlKey){
60210             e.stopEvent();
60211             ed.completeEdit();
60212             if(e.shiftKey){
60213                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60214             }else{
60215                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60216             }
60217         }else if(k == e.ESC){
60218             ed.cancelEdit();
60219         }
60220         if(newCell){
60221             g.startEditing(newCell[0], newCell[1]);
60222         }
60223     }
60224 });/*
60225  * Based on:
60226  * Ext JS Library 1.1.1
60227  * Copyright(c) 2006-2007, Ext JS, LLC.
60228  *
60229  * Originally Released Under LGPL - original licence link has changed is not relivant.
60230  *
60231  * Fork - LGPL
60232  * <script type="text/javascript">
60233  */
60234 /**
60235  * @class Roo.grid.CellSelectionModel
60236  * @extends Roo.grid.AbstractSelectionModel
60237  * This class provides the basic implementation for cell selection in a grid.
60238  * @constructor
60239  * @param {Object} config The object containing the configuration of this model.
60240  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60241  */
60242 Roo.grid.CellSelectionModel = function(config){
60243     Roo.apply(this, config);
60244
60245     this.selection = null;
60246
60247     this.addEvents({
60248         /**
60249              * @event beforerowselect
60250              * Fires before a cell is selected.
60251              * @param {SelectionModel} this
60252              * @param {Number} rowIndex The selected row index
60253              * @param {Number} colIndex The selected cell index
60254              */
60255             "beforecellselect" : true,
60256         /**
60257              * @event cellselect
60258              * Fires when a cell is selected.
60259              * @param {SelectionModel} this
60260              * @param {Number} rowIndex The selected row index
60261              * @param {Number} colIndex The selected cell index
60262              */
60263             "cellselect" : true,
60264         /**
60265              * @event selectionchange
60266              * Fires when the active selection changes.
60267              * @param {SelectionModel} this
60268              * @param {Object} selection null for no selection or an object (o) with two properties
60269                 <ul>
60270                 <li>o.record: the record object for the row the selection is in</li>
60271                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60272                 </ul>
60273              */
60274             "selectionchange" : true,
60275         /**
60276              * @event tabend
60277              * Fires when the tab (or enter) was pressed on the last editable cell
60278              * You can use this to trigger add new row.
60279              * @param {SelectionModel} this
60280              */
60281             "tabend" : true,
60282          /**
60283              * @event beforeeditnext
60284              * Fires before the next editable sell is made active
60285              * You can use this to skip to another cell or fire the tabend
60286              *    if you set cell to false
60287              * @param {Object} eventdata object : { cell : [ row, col ] } 
60288              */
60289             "beforeeditnext" : true
60290     });
60291     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60292 };
60293
60294 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60295     
60296     enter_is_tab: false,
60297
60298     /** @ignore */
60299     initEvents : function(){
60300         this.grid.on("mousedown", this.handleMouseDown, this);
60301         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60302         var view = this.grid.view;
60303         view.on("refresh", this.onViewChange, this);
60304         view.on("rowupdated", this.onRowUpdated, this);
60305         view.on("beforerowremoved", this.clearSelections, this);
60306         view.on("beforerowsinserted", this.clearSelections, this);
60307         if(this.grid.isEditor){
60308             this.grid.on("beforeedit", this.beforeEdit,  this);
60309         }
60310     },
60311
60312         //private
60313     beforeEdit : function(e){
60314         this.select(e.row, e.column, false, true, e.record);
60315     },
60316
60317         //private
60318     onRowUpdated : function(v, index, r){
60319         if(this.selection && this.selection.record == r){
60320             v.onCellSelect(index, this.selection.cell[1]);
60321         }
60322     },
60323
60324         //private
60325     onViewChange : function(){
60326         this.clearSelections(true);
60327     },
60328
60329         /**
60330          * Returns the currently selected cell,.
60331          * @return {Array} The selected cell (row, column) or null if none selected.
60332          */
60333     getSelectedCell : function(){
60334         return this.selection ? this.selection.cell : null;
60335     },
60336
60337     /**
60338      * Clears all selections.
60339      * @param {Boolean} true to prevent the gridview from being notified about the change.
60340      */
60341     clearSelections : function(preventNotify){
60342         var s = this.selection;
60343         if(s){
60344             if(preventNotify !== true){
60345                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60346             }
60347             this.selection = null;
60348             this.fireEvent("selectionchange", this, null);
60349         }
60350     },
60351
60352     /**
60353      * Returns true if there is a selection.
60354      * @return {Boolean}
60355      */
60356     hasSelection : function(){
60357         return this.selection ? true : false;
60358     },
60359
60360     /** @ignore */
60361     handleMouseDown : function(e, t){
60362         var v = this.grid.getView();
60363         if(this.isLocked()){
60364             return;
60365         };
60366         var row = v.findRowIndex(t);
60367         var cell = v.findCellIndex(t);
60368         if(row !== false && cell !== false){
60369             this.select(row, cell);
60370         }
60371     },
60372
60373     /**
60374      * Selects a cell.
60375      * @param {Number} rowIndex
60376      * @param {Number} collIndex
60377      */
60378     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60379         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60380             this.clearSelections();
60381             r = r || this.grid.dataSource.getAt(rowIndex);
60382             this.selection = {
60383                 record : r,
60384                 cell : [rowIndex, colIndex]
60385             };
60386             if(!preventViewNotify){
60387                 var v = this.grid.getView();
60388                 v.onCellSelect(rowIndex, colIndex);
60389                 if(preventFocus !== true){
60390                     v.focusCell(rowIndex, colIndex);
60391                 }
60392             }
60393             this.fireEvent("cellselect", this, rowIndex, colIndex);
60394             this.fireEvent("selectionchange", this, this.selection);
60395         }
60396     },
60397
60398         //private
60399     isSelectable : function(rowIndex, colIndex, cm){
60400         return !cm.isHidden(colIndex);
60401     },
60402
60403     /** @ignore */
60404     handleKeyDown : function(e){
60405         //Roo.log('Cell Sel Model handleKeyDown');
60406         if(!e.isNavKeyPress()){
60407             return;
60408         }
60409         var g = this.grid, s = this.selection;
60410         if(!s){
60411             e.stopEvent();
60412             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60413             if(cell){
60414                 this.select(cell[0], cell[1]);
60415             }
60416             return;
60417         }
60418         var sm = this;
60419         var walk = function(row, col, step){
60420             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60421         };
60422         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60423         var newCell;
60424
60425       
60426
60427         switch(k){
60428             case e.TAB:
60429                 // handled by onEditorKey
60430                 if (g.isEditor && g.editing) {
60431                     return;
60432                 }
60433                 if(e.shiftKey) {
60434                     newCell = walk(r, c-1, -1);
60435                 } else {
60436                     newCell = walk(r, c+1, 1);
60437                 }
60438                 break;
60439             
60440             case e.DOWN:
60441                newCell = walk(r+1, c, 1);
60442                 break;
60443             
60444             case e.UP:
60445                 newCell = walk(r-1, c, -1);
60446                 break;
60447             
60448             case e.RIGHT:
60449                 newCell = walk(r, c+1, 1);
60450                 break;
60451             
60452             case e.LEFT:
60453                 newCell = walk(r, c-1, -1);
60454                 break;
60455             
60456             case e.ENTER:
60457                 
60458                 if(g.isEditor && !g.editing){
60459                    g.startEditing(r, c);
60460                    e.stopEvent();
60461                    return;
60462                 }
60463                 
60464                 
60465              break;
60466         };
60467         if(newCell){
60468             this.select(newCell[0], newCell[1]);
60469             e.stopEvent();
60470             
60471         }
60472     },
60473
60474     acceptsNav : function(row, col, cm){
60475         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60476     },
60477     /**
60478      * Selects a cell.
60479      * @param {Number} field (not used) - as it's normally used as a listener
60480      * @param {Number} e - event - fake it by using
60481      *
60482      * var e = Roo.EventObjectImpl.prototype;
60483      * e.keyCode = e.TAB
60484      *
60485      * 
60486      */
60487     onEditorKey : function(field, e){
60488         
60489         var k = e.getKey(),
60490             newCell,
60491             g = this.grid,
60492             ed = g.activeEditor,
60493             forward = false;
60494         ///Roo.log('onEditorKey' + k);
60495         
60496         
60497         if (this.enter_is_tab && k == e.ENTER) {
60498             k = e.TAB;
60499         }
60500         
60501         if(k == e.TAB){
60502             if(e.shiftKey){
60503                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60504             }else{
60505                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60506                 forward = true;
60507             }
60508             
60509             e.stopEvent();
60510             
60511         } else if(k == e.ENTER &&  !e.ctrlKey){
60512             ed.completeEdit();
60513             e.stopEvent();
60514             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60515         
60516                 } else if(k == e.ESC){
60517             ed.cancelEdit();
60518         }
60519                 
60520         if (newCell) {
60521             var ecall = { cell : newCell, forward : forward };
60522             this.fireEvent('beforeeditnext', ecall );
60523             newCell = ecall.cell;
60524                         forward = ecall.forward;
60525         }
60526                 
60527         if(newCell){
60528             //Roo.log('next cell after edit');
60529             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60530         } else if (forward) {
60531             // tabbed past last
60532             this.fireEvent.defer(100, this, ['tabend',this]);
60533         }
60534     }
60535 });/*
60536  * Based on:
60537  * Ext JS Library 1.1.1
60538  * Copyright(c) 2006-2007, Ext JS, LLC.
60539  *
60540  * Originally Released Under LGPL - original licence link has changed is not relivant.
60541  *
60542  * Fork - LGPL
60543  * <script type="text/javascript">
60544  */
60545  
60546 /**
60547  * @class Roo.grid.EditorGrid
60548  * @extends Roo.grid.Grid
60549  * Class for creating and editable grid.
60550  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60551  * The container MUST have some type of size defined for the grid to fill. The container will be 
60552  * automatically set to position relative if it isn't already.
60553  * @param {Object} dataSource The data model to bind to
60554  * @param {Object} colModel The column model with info about this grid's columns
60555  */
60556 Roo.grid.EditorGrid = function(container, config){
60557     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
60558     this.getGridEl().addClass("xedit-grid");
60559
60560     if(!this.selModel){
60561         this.selModel = new Roo.grid.CellSelectionModel();
60562     }
60563
60564     this.activeEditor = null;
60565
60566         this.addEvents({
60567             /**
60568              * @event beforeedit
60569              * Fires before cell editing is triggered. The edit event object has the following properties <br />
60570              * <ul style="padding:5px;padding-left:16px;">
60571              * <li>grid - This grid</li>
60572              * <li>record - The record being edited</li>
60573              * <li>field - The field name being edited</li>
60574              * <li>value - The value for the field being edited.</li>
60575              * <li>row - The grid row index</li>
60576              * <li>column - The grid column index</li>
60577              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60578              * </ul>
60579              * @param {Object} e An edit event (see above for description)
60580              */
60581             "beforeedit" : true,
60582             /**
60583              * @event afteredit
60584              * Fires after a cell is edited. <br />
60585              * <ul style="padding:5px;padding-left:16px;">
60586              * <li>grid - This grid</li>
60587              * <li>record - The record being edited</li>
60588              * <li>field - The field name being edited</li>
60589              * <li>value - The value being set</li>
60590              * <li>originalValue - The original value for the field, before the edit.</li>
60591              * <li>row - The grid row index</li>
60592              * <li>column - The grid column index</li>
60593              * </ul>
60594              * @param {Object} e An edit event (see above for description)
60595              */
60596             "afteredit" : true,
60597             /**
60598              * @event validateedit
60599              * Fires after a cell is edited, but before the value is set in the record. 
60600          * You can use this to modify the value being set in the field, Return false
60601              * to cancel the change. The edit event object has the following properties <br />
60602              * <ul style="padding:5px;padding-left:16px;">
60603          * <li>editor - This editor</li>
60604              * <li>grid - This grid</li>
60605              * <li>record - The record being edited</li>
60606              * <li>field - The field name being edited</li>
60607              * <li>value - The value being set</li>
60608              * <li>originalValue - The original value for the field, before the edit.</li>
60609              * <li>row - The grid row index</li>
60610              * <li>column - The grid column index</li>
60611              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60612              * </ul>
60613              * @param {Object} e An edit event (see above for description)
60614              */
60615             "validateedit" : true
60616         });
60617     this.on("bodyscroll", this.stopEditing,  this);
60618     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
60619 };
60620
60621 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
60622     /**
60623      * @cfg {Number} clicksToEdit
60624      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
60625      */
60626     clicksToEdit: 2,
60627
60628     // private
60629     isEditor : true,
60630     // private
60631     trackMouseOver: false, // causes very odd FF errors
60632
60633     onCellDblClick : function(g, row, col){
60634         this.startEditing(row, col);
60635     },
60636
60637     onEditComplete : function(ed, value, startValue){
60638         this.editing = false;
60639         this.activeEditor = null;
60640         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
60641         var r = ed.record;
60642         var field = this.colModel.getDataIndex(ed.col);
60643         var e = {
60644             grid: this,
60645             record: r,
60646             field: field,
60647             originalValue: startValue,
60648             value: value,
60649             row: ed.row,
60650             column: ed.col,
60651             cancel:false,
60652             editor: ed
60653         };
60654         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
60655         cell.show();
60656           
60657         if(String(value) !== String(startValue)){
60658             
60659             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
60660                 r.set(field, e.value);
60661                 // if we are dealing with a combo box..
60662                 // then we also set the 'name' colum to be the displayField
60663                 if (ed.field.displayField && ed.field.name) {
60664                     r.set(ed.field.name, ed.field.el.dom.value);
60665                 }
60666                 
60667                 delete e.cancel; //?? why!!!
60668                 this.fireEvent("afteredit", e);
60669             }
60670         } else {
60671             this.fireEvent("afteredit", e); // always fire it!
60672         }
60673         this.view.focusCell(ed.row, ed.col);
60674     },
60675
60676     /**
60677      * Starts editing the specified for the specified row/column
60678      * @param {Number} rowIndex
60679      * @param {Number} colIndex
60680      */
60681     startEditing : function(row, col){
60682         this.stopEditing();
60683         if(this.colModel.isCellEditable(col, row)){
60684             this.view.ensureVisible(row, col, true);
60685           
60686             var r = this.dataSource.getAt(row);
60687             var field = this.colModel.getDataIndex(col);
60688             var cell = Roo.get(this.view.getCell(row,col));
60689             var e = {
60690                 grid: this,
60691                 record: r,
60692                 field: field,
60693                 value: r.data[field],
60694                 row: row,
60695                 column: col,
60696                 cancel:false 
60697             };
60698             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
60699                 this.editing = true;
60700                 var ed = this.colModel.getCellEditor(col, row);
60701                 
60702                 if (!ed) {
60703                     return;
60704                 }
60705                 if(!ed.rendered){
60706                     ed.render(ed.parentEl || document.body);
60707                 }
60708                 ed.field.reset();
60709                
60710                 cell.hide();
60711                 
60712                 (function(){ // complex but required for focus issues in safari, ie and opera
60713                     ed.row = row;
60714                     ed.col = col;
60715                     ed.record = r;
60716                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60717                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60718                     this.activeEditor = ed;
60719                     var v = r.data[field];
60720                     ed.startEdit(this.view.getCell(row, col), v);
60721                     // combo's with 'displayField and name set
60722                     if (ed.field.displayField && ed.field.name) {
60723                         ed.field.el.dom.value = r.data[ed.field.name];
60724                     }
60725                     
60726                     
60727                 }).defer(50, this);
60728             }
60729         }
60730     },
60731         
60732     /**
60733      * Stops any active editing
60734      */
60735     stopEditing : function(){
60736         if(this.activeEditor){
60737             this.activeEditor.completeEdit();
60738         }
60739         this.activeEditor = null;
60740     },
60741         
60742          /**
60743      * Called to get grid's drag proxy text, by default returns this.ddText.
60744      * @return {String}
60745      */
60746     getDragDropText : function(){
60747         var count = this.selModel.getSelectedCell() ? 1 : 0;
60748         return String.format(this.ddText, count, count == 1 ? '' : 's');
60749     }
60750         
60751 });/*
60752  * Based on:
60753  * Ext JS Library 1.1.1
60754  * Copyright(c) 2006-2007, Ext JS, LLC.
60755  *
60756  * Originally Released Under LGPL - original licence link has changed is not relivant.
60757  *
60758  * Fork - LGPL
60759  * <script type="text/javascript">
60760  */
60761
60762 // private - not really -- you end up using it !
60763 // This is a support class used internally by the Grid components
60764
60765 /**
60766  * @class Roo.grid.GridEditor
60767  * @extends Roo.Editor
60768  * Class for creating and editable grid elements.
60769  * @param {Object} config any settings (must include field)
60770  */
60771 Roo.grid.GridEditor = function(field, config){
60772     if (!config && field.field) {
60773         config = field;
60774         field = Roo.factory(config.field, Roo.form);
60775     }
60776     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60777     field.monitorTab = false;
60778 };
60779
60780 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60781     
60782     /**
60783      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60784      */
60785     
60786     alignment: "tl-tl",
60787     autoSize: "width",
60788     hideEl : false,
60789     cls: "x-small-editor x-grid-editor",
60790     shim:false,
60791     shadow:"frame"
60792 });/*
60793  * Based on:
60794  * Ext JS Library 1.1.1
60795  * Copyright(c) 2006-2007, Ext JS, LLC.
60796  *
60797  * Originally Released Under LGPL - original licence link has changed is not relivant.
60798  *
60799  * Fork - LGPL
60800  * <script type="text/javascript">
60801  */
60802   
60803
60804   
60805 Roo.grid.PropertyRecord = Roo.data.Record.create([
60806     {name:'name',type:'string'},  'value'
60807 ]);
60808
60809
60810 Roo.grid.PropertyStore = function(grid, source){
60811     this.grid = grid;
60812     this.store = new Roo.data.Store({
60813         recordType : Roo.grid.PropertyRecord
60814     });
60815     this.store.on('update', this.onUpdate,  this);
60816     if(source){
60817         this.setSource(source);
60818     }
60819     Roo.grid.PropertyStore.superclass.constructor.call(this);
60820 };
60821
60822
60823
60824 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60825     setSource : function(o){
60826         this.source = o;
60827         this.store.removeAll();
60828         var data = [];
60829         for(var k in o){
60830             if(this.isEditableValue(o[k])){
60831                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60832             }
60833         }
60834         this.store.loadRecords({records: data}, {}, true);
60835     },
60836
60837     onUpdate : function(ds, record, type){
60838         if(type == Roo.data.Record.EDIT){
60839             var v = record.data['value'];
60840             var oldValue = record.modified['value'];
60841             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60842                 this.source[record.id] = v;
60843                 record.commit();
60844                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60845             }else{
60846                 record.reject();
60847             }
60848         }
60849     },
60850
60851     getProperty : function(row){
60852        return this.store.getAt(row);
60853     },
60854
60855     isEditableValue: function(val){
60856         if(val && val instanceof Date){
60857             return true;
60858         }else if(typeof val == 'object' || typeof val == 'function'){
60859             return false;
60860         }
60861         return true;
60862     },
60863
60864     setValue : function(prop, value){
60865         this.source[prop] = value;
60866         this.store.getById(prop).set('value', value);
60867     },
60868
60869     getSource : function(){
60870         return this.source;
60871     }
60872 });
60873
60874 Roo.grid.PropertyColumnModel = function(grid, store){
60875     this.grid = grid;
60876     var g = Roo.grid;
60877     g.PropertyColumnModel.superclass.constructor.call(this, [
60878         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60879         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60880     ]);
60881     this.store = store;
60882     this.bselect = Roo.DomHelper.append(document.body, {
60883         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60884             {tag: 'option', value: 'true', html: 'true'},
60885             {tag: 'option', value: 'false', html: 'false'}
60886         ]
60887     });
60888     Roo.id(this.bselect);
60889     var f = Roo.form;
60890     this.editors = {
60891         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60892         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60893         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60894         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60895         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60896     };
60897     this.renderCellDelegate = this.renderCell.createDelegate(this);
60898     this.renderPropDelegate = this.renderProp.createDelegate(this);
60899 };
60900
60901 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60902     
60903     
60904     nameText : 'Name',
60905     valueText : 'Value',
60906     
60907     dateFormat : 'm/j/Y',
60908     
60909     
60910     renderDate : function(dateVal){
60911         return dateVal.dateFormat(this.dateFormat);
60912     },
60913
60914     renderBool : function(bVal){
60915         return bVal ? 'true' : 'false';
60916     },
60917
60918     isCellEditable : function(colIndex, rowIndex){
60919         return colIndex == 1;
60920     },
60921
60922     getRenderer : function(col){
60923         return col == 1 ?
60924             this.renderCellDelegate : this.renderPropDelegate;
60925     },
60926
60927     renderProp : function(v){
60928         return this.getPropertyName(v);
60929     },
60930
60931     renderCell : function(val){
60932         var rv = val;
60933         if(val instanceof Date){
60934             rv = this.renderDate(val);
60935         }else if(typeof val == 'boolean'){
60936             rv = this.renderBool(val);
60937         }
60938         return Roo.util.Format.htmlEncode(rv);
60939     },
60940
60941     getPropertyName : function(name){
60942         var pn = this.grid.propertyNames;
60943         return pn && pn[name] ? pn[name] : name;
60944     },
60945
60946     getCellEditor : function(colIndex, rowIndex){
60947         var p = this.store.getProperty(rowIndex);
60948         var n = p.data['name'], val = p.data['value'];
60949         
60950         if(typeof(this.grid.customEditors[n]) == 'string'){
60951             return this.editors[this.grid.customEditors[n]];
60952         }
60953         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60954             return this.grid.customEditors[n];
60955         }
60956         if(val instanceof Date){
60957             return this.editors['date'];
60958         }else if(typeof val == 'number'){
60959             return this.editors['number'];
60960         }else if(typeof val == 'boolean'){
60961             return this.editors['boolean'];
60962         }else{
60963             return this.editors['string'];
60964         }
60965     }
60966 });
60967
60968 /**
60969  * @class Roo.grid.PropertyGrid
60970  * @extends Roo.grid.EditorGrid
60971  * This class represents the  interface of a component based property grid control.
60972  * <br><br>Usage:<pre><code>
60973  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60974       
60975  });
60976  // set any options
60977  grid.render();
60978  * </code></pre>
60979   
60980  * @constructor
60981  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60982  * The container MUST have some type of size defined for the grid to fill. The container will be
60983  * automatically set to position relative if it isn't already.
60984  * @param {Object} config A config object that sets properties on this grid.
60985  */
60986 Roo.grid.PropertyGrid = function(container, config){
60987     config = config || {};
60988     var store = new Roo.grid.PropertyStore(this);
60989     this.store = store;
60990     var cm = new Roo.grid.PropertyColumnModel(this, store);
60991     store.store.sort('name', 'ASC');
60992     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60993         ds: store.store,
60994         cm: cm,
60995         enableColLock:false,
60996         enableColumnMove:false,
60997         stripeRows:false,
60998         trackMouseOver: false,
60999         clicksToEdit:1
61000     }, config));
61001     this.getGridEl().addClass('x-props-grid');
61002     this.lastEditRow = null;
61003     this.on('columnresize', this.onColumnResize, this);
61004     this.addEvents({
61005          /**
61006              * @event beforepropertychange
61007              * Fires before a property changes (return false to stop?)
61008              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61009              * @param {String} id Record Id
61010              * @param {String} newval New Value
61011          * @param {String} oldval Old Value
61012              */
61013         "beforepropertychange": true,
61014         /**
61015              * @event propertychange
61016              * Fires after a property changes
61017              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61018              * @param {String} id Record Id
61019              * @param {String} newval New Value
61020          * @param {String} oldval Old Value
61021              */
61022         "propertychange": true
61023     });
61024     this.customEditors = this.customEditors || {};
61025 };
61026 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61027     
61028      /**
61029      * @cfg {Object} customEditors map of colnames=> custom editors.
61030      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61031      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61032      * false disables editing of the field.
61033          */
61034     
61035       /**
61036      * @cfg {Object} propertyNames map of property Names to their displayed value
61037          */
61038     
61039     render : function(){
61040         Roo.grid.PropertyGrid.superclass.render.call(this);
61041         this.autoSize.defer(100, this);
61042     },
61043
61044     autoSize : function(){
61045         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61046         if(this.view){
61047             this.view.fitColumns();
61048         }
61049     },
61050
61051     onColumnResize : function(){
61052         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61053         this.autoSize();
61054     },
61055     /**
61056      * Sets the data for the Grid
61057      * accepts a Key => Value object of all the elements avaiable.
61058      * @param {Object} data  to appear in grid.
61059      */
61060     setSource : function(source){
61061         this.store.setSource(source);
61062         //this.autoSize();
61063     },
61064     /**
61065      * Gets all the data from the grid.
61066      * @return {Object} data  data stored in grid
61067      */
61068     getSource : function(){
61069         return this.store.getSource();
61070     }
61071 });/*
61072   
61073  * Licence LGPL
61074  
61075  */
61076  
61077 /**
61078  * @class Roo.grid.Calendar
61079  * @extends Roo.grid.Grid
61080  * This class extends the Grid to provide a calendar widget
61081  * <br><br>Usage:<pre><code>
61082  var grid = new Roo.grid.Calendar("my-container-id", {
61083      ds: myDataStore,
61084      cm: myColModel,
61085      selModel: mySelectionModel,
61086      autoSizeColumns: true,
61087      monitorWindowResize: false,
61088      trackMouseOver: true
61089      eventstore : real data store..
61090  });
61091  // set any options
61092  grid.render();
61093   
61094   * @constructor
61095  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61096  * The container MUST have some type of size defined for the grid to fill. The container will be
61097  * automatically set to position relative if it isn't already.
61098  * @param {Object} config A config object that sets properties on this grid.
61099  */
61100 Roo.grid.Calendar = function(container, config){
61101         // initialize the container
61102         this.container = Roo.get(container);
61103         this.container.update("");
61104         this.container.setStyle("overflow", "hidden");
61105     this.container.addClass('x-grid-container');
61106
61107     this.id = this.container.id;
61108
61109     Roo.apply(this, config);
61110     // check and correct shorthanded configs
61111     
61112     var rows = [];
61113     var d =1;
61114     for (var r = 0;r < 6;r++) {
61115         
61116         rows[r]=[];
61117         for (var c =0;c < 7;c++) {
61118             rows[r][c]= '';
61119         }
61120     }
61121     if (this.eventStore) {
61122         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61123         this.eventStore.on('load',this.onLoad, this);
61124         this.eventStore.on('beforeload',this.clearEvents, this);
61125          
61126     }
61127     
61128     this.dataSource = new Roo.data.Store({
61129             proxy: new Roo.data.MemoryProxy(rows),
61130             reader: new Roo.data.ArrayReader({}, [
61131                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61132     });
61133
61134     this.dataSource.load();
61135     this.ds = this.dataSource;
61136     this.ds.xmodule = this.xmodule || false;
61137     
61138     
61139     var cellRender = function(v,x,r)
61140     {
61141         return String.format(
61142             '<div class="fc-day  fc-widget-content"><div>' +
61143                 '<div class="fc-event-container"></div>' +
61144                 '<div class="fc-day-number">{0}</div>'+
61145                 
61146                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61147             '</div></div>', v);
61148     
61149     }
61150     
61151     
61152     this.colModel = new Roo.grid.ColumnModel( [
61153         {
61154             xtype: 'ColumnModel',
61155             xns: Roo.grid,
61156             dataIndex : 'weekday0',
61157             header : 'Sunday',
61158             renderer : cellRender
61159         },
61160         {
61161             xtype: 'ColumnModel',
61162             xns: Roo.grid,
61163             dataIndex : 'weekday1',
61164             header : 'Monday',
61165             renderer : cellRender
61166         },
61167         {
61168             xtype: 'ColumnModel',
61169             xns: Roo.grid,
61170             dataIndex : 'weekday2',
61171             header : 'Tuesday',
61172             renderer : cellRender
61173         },
61174         {
61175             xtype: 'ColumnModel',
61176             xns: Roo.grid,
61177             dataIndex : 'weekday3',
61178             header : 'Wednesday',
61179             renderer : cellRender
61180         },
61181         {
61182             xtype: 'ColumnModel',
61183             xns: Roo.grid,
61184             dataIndex : 'weekday4',
61185             header : 'Thursday',
61186             renderer : cellRender
61187         },
61188         {
61189             xtype: 'ColumnModel',
61190             xns: Roo.grid,
61191             dataIndex : 'weekday5',
61192             header : 'Friday',
61193             renderer : cellRender
61194         },
61195         {
61196             xtype: 'ColumnModel',
61197             xns: Roo.grid,
61198             dataIndex : 'weekday6',
61199             header : 'Saturday',
61200             renderer : cellRender
61201         }
61202     ]);
61203     this.cm = this.colModel;
61204     this.cm.xmodule = this.xmodule || false;
61205  
61206         
61207           
61208     //this.selModel = new Roo.grid.CellSelectionModel();
61209     //this.sm = this.selModel;
61210     //this.selModel.init(this);
61211     
61212     
61213     if(this.width){
61214         this.container.setWidth(this.width);
61215     }
61216
61217     if(this.height){
61218         this.container.setHeight(this.height);
61219     }
61220     /** @private */
61221         this.addEvents({
61222         // raw events
61223         /**
61224          * @event click
61225          * The raw click event for the entire grid.
61226          * @param {Roo.EventObject} e
61227          */
61228         "click" : true,
61229         /**
61230          * @event dblclick
61231          * The raw dblclick event for the entire grid.
61232          * @param {Roo.EventObject} e
61233          */
61234         "dblclick" : true,
61235         /**
61236          * @event contextmenu
61237          * The raw contextmenu event for the entire grid.
61238          * @param {Roo.EventObject} e
61239          */
61240         "contextmenu" : true,
61241         /**
61242          * @event mousedown
61243          * The raw mousedown event for the entire grid.
61244          * @param {Roo.EventObject} e
61245          */
61246         "mousedown" : true,
61247         /**
61248          * @event mouseup
61249          * The raw mouseup event for the entire grid.
61250          * @param {Roo.EventObject} e
61251          */
61252         "mouseup" : true,
61253         /**
61254          * @event mouseover
61255          * The raw mouseover event for the entire grid.
61256          * @param {Roo.EventObject} e
61257          */
61258         "mouseover" : true,
61259         /**
61260          * @event mouseout
61261          * The raw mouseout event for the entire grid.
61262          * @param {Roo.EventObject} e
61263          */
61264         "mouseout" : true,
61265         /**
61266          * @event keypress
61267          * The raw keypress event for the entire grid.
61268          * @param {Roo.EventObject} e
61269          */
61270         "keypress" : true,
61271         /**
61272          * @event keydown
61273          * The raw keydown event for the entire grid.
61274          * @param {Roo.EventObject} e
61275          */
61276         "keydown" : true,
61277
61278         // custom events
61279
61280         /**
61281          * @event cellclick
61282          * Fires when a cell is clicked
61283          * @param {Grid} this
61284          * @param {Number} rowIndex
61285          * @param {Number} columnIndex
61286          * @param {Roo.EventObject} e
61287          */
61288         "cellclick" : true,
61289         /**
61290          * @event celldblclick
61291          * Fires when a cell is double clicked
61292          * @param {Grid} this
61293          * @param {Number} rowIndex
61294          * @param {Number} columnIndex
61295          * @param {Roo.EventObject} e
61296          */
61297         "celldblclick" : true,
61298         /**
61299          * @event rowclick
61300          * Fires when a row is clicked
61301          * @param {Grid} this
61302          * @param {Number} rowIndex
61303          * @param {Roo.EventObject} e
61304          */
61305         "rowclick" : true,
61306         /**
61307          * @event rowdblclick
61308          * Fires when a row is double clicked
61309          * @param {Grid} this
61310          * @param {Number} rowIndex
61311          * @param {Roo.EventObject} e
61312          */
61313         "rowdblclick" : true,
61314         /**
61315          * @event headerclick
61316          * Fires when a header is clicked
61317          * @param {Grid} this
61318          * @param {Number} columnIndex
61319          * @param {Roo.EventObject} e
61320          */
61321         "headerclick" : true,
61322         /**
61323          * @event headerdblclick
61324          * Fires when a header cell is double clicked
61325          * @param {Grid} this
61326          * @param {Number} columnIndex
61327          * @param {Roo.EventObject} e
61328          */
61329         "headerdblclick" : true,
61330         /**
61331          * @event rowcontextmenu
61332          * Fires when a row is right clicked
61333          * @param {Grid} this
61334          * @param {Number} rowIndex
61335          * @param {Roo.EventObject} e
61336          */
61337         "rowcontextmenu" : true,
61338         /**
61339          * @event cellcontextmenu
61340          * Fires when a cell is right clicked
61341          * @param {Grid} this
61342          * @param {Number} rowIndex
61343          * @param {Number} cellIndex
61344          * @param {Roo.EventObject} e
61345          */
61346          "cellcontextmenu" : true,
61347         /**
61348          * @event headercontextmenu
61349          * Fires when a header is right clicked
61350          * @param {Grid} this
61351          * @param {Number} columnIndex
61352          * @param {Roo.EventObject} e
61353          */
61354         "headercontextmenu" : true,
61355         /**
61356          * @event bodyscroll
61357          * Fires when the body element is scrolled
61358          * @param {Number} scrollLeft
61359          * @param {Number} scrollTop
61360          */
61361         "bodyscroll" : true,
61362         /**
61363          * @event columnresize
61364          * Fires when the user resizes a column
61365          * @param {Number} columnIndex
61366          * @param {Number} newSize
61367          */
61368         "columnresize" : true,
61369         /**
61370          * @event columnmove
61371          * Fires when the user moves a column
61372          * @param {Number} oldIndex
61373          * @param {Number} newIndex
61374          */
61375         "columnmove" : true,
61376         /**
61377          * @event startdrag
61378          * Fires when row(s) start being dragged
61379          * @param {Grid} this
61380          * @param {Roo.GridDD} dd The drag drop object
61381          * @param {event} e The raw browser event
61382          */
61383         "startdrag" : true,
61384         /**
61385          * @event enddrag
61386          * Fires when a drag operation is complete
61387          * @param {Grid} this
61388          * @param {Roo.GridDD} dd The drag drop object
61389          * @param {event} e The raw browser event
61390          */
61391         "enddrag" : true,
61392         /**
61393          * @event dragdrop
61394          * Fires when dragged row(s) are dropped on a valid DD target
61395          * @param {Grid} this
61396          * @param {Roo.GridDD} dd The drag drop object
61397          * @param {String} targetId The target drag drop object
61398          * @param {event} e The raw browser event
61399          */
61400         "dragdrop" : true,
61401         /**
61402          * @event dragover
61403          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61404          * @param {Grid} this
61405          * @param {Roo.GridDD} dd The drag drop object
61406          * @param {String} targetId The target drag drop object
61407          * @param {event} e The raw browser event
61408          */
61409         "dragover" : true,
61410         /**
61411          * @event dragenter
61412          *  Fires when the dragged row(s) first cross another DD target while being dragged
61413          * @param {Grid} this
61414          * @param {Roo.GridDD} dd The drag drop object
61415          * @param {String} targetId The target drag drop object
61416          * @param {event} e The raw browser event
61417          */
61418         "dragenter" : true,
61419         /**
61420          * @event dragout
61421          * Fires when the dragged row(s) leave another DD target while being dragged
61422          * @param {Grid} this
61423          * @param {Roo.GridDD} dd The drag drop object
61424          * @param {String} targetId The target drag drop object
61425          * @param {event} e The raw browser event
61426          */
61427         "dragout" : true,
61428         /**
61429          * @event rowclass
61430          * Fires when a row is rendered, so you can change add a style to it.
61431          * @param {GridView} gridview   The grid view
61432          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61433          */
61434         'rowclass' : true,
61435
61436         /**
61437          * @event render
61438          * Fires when the grid is rendered
61439          * @param {Grid} grid
61440          */
61441         'render' : true,
61442             /**
61443              * @event select
61444              * Fires when a date is selected
61445              * @param {DatePicker} this
61446              * @param {Date} date The selected date
61447              */
61448         'select': true,
61449         /**
61450              * @event monthchange
61451              * Fires when the displayed month changes 
61452              * @param {DatePicker} this
61453              * @param {Date} date The selected month
61454              */
61455         'monthchange': true,
61456         /**
61457              * @event evententer
61458              * Fires when mouse over an event
61459              * @param {Calendar} this
61460              * @param {event} Event
61461              */
61462         'evententer': true,
61463         /**
61464              * @event eventleave
61465              * Fires when the mouse leaves an
61466              * @param {Calendar} this
61467              * @param {event}
61468              */
61469         'eventleave': true,
61470         /**
61471              * @event eventclick
61472              * Fires when the mouse click an
61473              * @param {Calendar} this
61474              * @param {event}
61475              */
61476         'eventclick': true,
61477         /**
61478              * @event eventrender
61479              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61480              * @param {Calendar} this
61481              * @param {data} data to be modified
61482              */
61483         'eventrender': true
61484         
61485     });
61486
61487     Roo.grid.Grid.superclass.constructor.call(this);
61488     this.on('render', function() {
61489         this.view.el.addClass('x-grid-cal'); 
61490         
61491         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61492
61493     },this);
61494     
61495     if (!Roo.grid.Calendar.style) {
61496         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61497             
61498             
61499             '.x-grid-cal .x-grid-col' :  {
61500                 height: 'auto !important',
61501                 'vertical-align': 'top'
61502             },
61503             '.x-grid-cal  .fc-event-hori' : {
61504                 height: '14px'
61505             }
61506              
61507             
61508         }, Roo.id());
61509     }
61510
61511     
61512     
61513 };
61514 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61515     /**
61516      * @cfg {Store} eventStore The store that loads events.
61517      */
61518     eventStore : 25,
61519
61520      
61521     activeDate : false,
61522     startDay : 0,
61523     autoWidth : true,
61524     monitorWindowResize : false,
61525
61526     
61527     resizeColumns : function() {
61528         var col = (this.view.el.getWidth() / 7) - 3;
61529         // loop through cols, and setWidth
61530         for(var i =0 ; i < 7 ; i++){
61531             this.cm.setColumnWidth(i, col);
61532         }
61533     },
61534      setDate :function(date) {
61535         
61536         Roo.log('setDate?');
61537         
61538         this.resizeColumns();
61539         var vd = this.activeDate;
61540         this.activeDate = date;
61541 //        if(vd && this.el){
61542 //            var t = date.getTime();
61543 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61544 //                Roo.log('using add remove');
61545 //                
61546 //                this.fireEvent('monthchange', this, date);
61547 //                
61548 //                this.cells.removeClass("fc-state-highlight");
61549 //                this.cells.each(function(c){
61550 //                   if(c.dateValue == t){
61551 //                       c.addClass("fc-state-highlight");
61552 //                       setTimeout(function(){
61553 //                            try{c.dom.firstChild.focus();}catch(e){}
61554 //                       }, 50);
61555 //                       return false;
61556 //                   }
61557 //                   return true;
61558 //                });
61559 //                return;
61560 //            }
61561 //        }
61562         
61563         var days = date.getDaysInMonth();
61564         
61565         var firstOfMonth = date.getFirstDateOfMonth();
61566         var startingPos = firstOfMonth.getDay()-this.startDay;
61567         
61568         if(startingPos < this.startDay){
61569             startingPos += 7;
61570         }
61571         
61572         var pm = date.add(Date.MONTH, -1);
61573         var prevStart = pm.getDaysInMonth()-startingPos;
61574 //        
61575         
61576         
61577         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61578         
61579         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
61580         //this.cells.addClassOnOver('fc-state-hover');
61581         
61582         var cells = this.cells.elements;
61583         var textEls = this.textNodes;
61584         
61585         //Roo.each(cells, function(cell){
61586         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
61587         //});
61588         
61589         days += startingPos;
61590
61591         // convert everything to numbers so it's fast
61592         var day = 86400000;
61593         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
61594         //Roo.log(d);
61595         //Roo.log(pm);
61596         //Roo.log(prevStart);
61597         
61598         var today = new Date().clearTime().getTime();
61599         var sel = date.clearTime().getTime();
61600         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
61601         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
61602         var ddMatch = this.disabledDatesRE;
61603         var ddText = this.disabledDatesText;
61604         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
61605         var ddaysText = this.disabledDaysText;
61606         var format = this.format;
61607         
61608         var setCellClass = function(cal, cell){
61609             
61610             //Roo.log('set Cell Class');
61611             cell.title = "";
61612             var t = d.getTime();
61613             
61614             //Roo.log(d);
61615             
61616             
61617             cell.dateValue = t;
61618             if(t == today){
61619                 cell.className += " fc-today";
61620                 cell.className += " fc-state-highlight";
61621                 cell.title = cal.todayText;
61622             }
61623             if(t == sel){
61624                 // disable highlight in other month..
61625                 cell.className += " fc-state-highlight";
61626                 
61627             }
61628             // disabling
61629             if(t < min) {
61630                 //cell.className = " fc-state-disabled";
61631                 cell.title = cal.minText;
61632                 return;
61633             }
61634             if(t > max) {
61635                 //cell.className = " fc-state-disabled";
61636                 cell.title = cal.maxText;
61637                 return;
61638             }
61639             if(ddays){
61640                 if(ddays.indexOf(d.getDay()) != -1){
61641                     // cell.title = ddaysText;
61642                    // cell.className = " fc-state-disabled";
61643                 }
61644             }
61645             if(ddMatch && format){
61646                 var fvalue = d.dateFormat(format);
61647                 if(ddMatch.test(fvalue)){
61648                     cell.title = ddText.replace("%0", fvalue);
61649                    cell.className = " fc-state-disabled";
61650                 }
61651             }
61652             
61653             if (!cell.initialClassName) {
61654                 cell.initialClassName = cell.dom.className;
61655             }
61656             
61657             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
61658         };
61659
61660         var i = 0;
61661         
61662         for(; i < startingPos; i++) {
61663             cells[i].dayName =  (++prevStart);
61664             Roo.log(textEls[i]);
61665             d.setDate(d.getDate()+1);
61666             
61667             //cells[i].className = "fc-past fc-other-month";
61668             setCellClass(this, cells[i]);
61669         }
61670         
61671         var intDay = 0;
61672         
61673         for(; i < days; i++){
61674             intDay = i - startingPos + 1;
61675             cells[i].dayName =  (intDay);
61676             d.setDate(d.getDate()+1);
61677             
61678             cells[i].className = ''; // "x-date-active";
61679             setCellClass(this, cells[i]);
61680         }
61681         var extraDays = 0;
61682         
61683         for(; i < 42; i++) {
61684             //textEls[i].innerHTML = (++extraDays);
61685             
61686             d.setDate(d.getDate()+1);
61687             cells[i].dayName = (++extraDays);
61688             cells[i].className = "fc-future fc-other-month";
61689             setCellClass(this, cells[i]);
61690         }
61691         
61692         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
61693         
61694         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
61695         
61696         // this will cause all the cells to mis
61697         var rows= [];
61698         var i =0;
61699         for (var r = 0;r < 6;r++) {
61700             for (var c =0;c < 7;c++) {
61701                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
61702             }    
61703         }
61704         
61705         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61706         for(i=0;i<cells.length;i++) {
61707             
61708             this.cells.elements[i].dayName = cells[i].dayName ;
61709             this.cells.elements[i].className = cells[i].className;
61710             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
61711             this.cells.elements[i].title = cells[i].title ;
61712             this.cells.elements[i].dateValue = cells[i].dateValue ;
61713         }
61714         
61715         
61716         
61717         
61718         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61719         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61720         
61721         ////if(totalRows != 6){
61722             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61723            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61724        // }
61725         
61726         this.fireEvent('monthchange', this, date);
61727         
61728         
61729     },
61730  /**
61731      * Returns the grid's SelectionModel.
61732      * @return {SelectionModel}
61733      */
61734     getSelectionModel : function(){
61735         if(!this.selModel){
61736             this.selModel = new Roo.grid.CellSelectionModel();
61737         }
61738         return this.selModel;
61739     },
61740
61741     load: function() {
61742         this.eventStore.load()
61743         
61744         
61745         
61746     },
61747     
61748     findCell : function(dt) {
61749         dt = dt.clearTime().getTime();
61750         var ret = false;
61751         this.cells.each(function(c){
61752             //Roo.log("check " +c.dateValue + '?=' + dt);
61753             if(c.dateValue == dt){
61754                 ret = c;
61755                 return false;
61756             }
61757             return true;
61758         });
61759         
61760         return ret;
61761     },
61762     
61763     findCells : function(rec) {
61764         var s = rec.data.start_dt.clone().clearTime().getTime();
61765        // Roo.log(s);
61766         var e= rec.data.end_dt.clone().clearTime().getTime();
61767        // Roo.log(e);
61768         var ret = [];
61769         this.cells.each(function(c){
61770              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61771             
61772             if(c.dateValue > e){
61773                 return ;
61774             }
61775             if(c.dateValue < s){
61776                 return ;
61777             }
61778             ret.push(c);
61779         });
61780         
61781         return ret;    
61782     },
61783     
61784     findBestRow: function(cells)
61785     {
61786         var ret = 0;
61787         
61788         for (var i =0 ; i < cells.length;i++) {
61789             ret  = Math.max(cells[i].rows || 0,ret);
61790         }
61791         return ret;
61792         
61793     },
61794     
61795     
61796     addItem : function(rec)
61797     {
61798         // look for vertical location slot in
61799         var cells = this.findCells(rec);
61800         
61801         rec.row = this.findBestRow(cells);
61802         
61803         // work out the location.
61804         
61805         var crow = false;
61806         var rows = [];
61807         for(var i =0; i < cells.length; i++) {
61808             if (!crow) {
61809                 crow = {
61810                     start : cells[i],
61811                     end :  cells[i]
61812                 };
61813                 continue;
61814             }
61815             if (crow.start.getY() == cells[i].getY()) {
61816                 // on same row.
61817                 crow.end = cells[i];
61818                 continue;
61819             }
61820             // different row.
61821             rows.push(crow);
61822             crow = {
61823                 start: cells[i],
61824                 end : cells[i]
61825             };
61826             
61827         }
61828         
61829         rows.push(crow);
61830         rec.els = [];
61831         rec.rows = rows;
61832         rec.cells = cells;
61833         for (var i = 0; i < cells.length;i++) {
61834             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61835             
61836         }
61837         
61838         
61839     },
61840     
61841     clearEvents: function() {
61842         
61843         if (!this.eventStore.getCount()) {
61844             return;
61845         }
61846         // reset number of rows in cells.
61847         Roo.each(this.cells.elements, function(c){
61848             c.rows = 0;
61849         });
61850         
61851         this.eventStore.each(function(e) {
61852             this.clearEvent(e);
61853         },this);
61854         
61855     },
61856     
61857     clearEvent : function(ev)
61858     {
61859         if (ev.els) {
61860             Roo.each(ev.els, function(el) {
61861                 el.un('mouseenter' ,this.onEventEnter, this);
61862                 el.un('mouseleave' ,this.onEventLeave, this);
61863                 el.remove();
61864             },this);
61865             ev.els = [];
61866         }
61867     },
61868     
61869     
61870     renderEvent : function(ev,ctr) {
61871         if (!ctr) {
61872              ctr = this.view.el.select('.fc-event-container',true).first();
61873         }
61874         
61875          
61876         this.clearEvent(ev);
61877             //code
61878        
61879         
61880         
61881         ev.els = [];
61882         var cells = ev.cells;
61883         var rows = ev.rows;
61884         this.fireEvent('eventrender', this, ev);
61885         
61886         for(var i =0; i < rows.length; i++) {
61887             
61888             cls = '';
61889             if (i == 0) {
61890                 cls += ' fc-event-start';
61891             }
61892             if ((i+1) == rows.length) {
61893                 cls += ' fc-event-end';
61894             }
61895             
61896             //Roo.log(ev.data);
61897             // how many rows should it span..
61898             var cg = this.eventTmpl.append(ctr,Roo.apply({
61899                 fccls : cls
61900                 
61901             }, ev.data) , true);
61902             
61903             
61904             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61905             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61906             cg.on('click', this.onEventClick, this, ev);
61907             
61908             ev.els.push(cg);
61909             
61910             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61911             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61912             //Roo.log(cg);
61913              
61914             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61915             cg.setWidth(ebox.right - sbox.x -2);
61916         }
61917     },
61918     
61919     renderEvents: function()
61920     {   
61921         // first make sure there is enough space..
61922         
61923         if (!this.eventTmpl) {
61924             this.eventTmpl = new Roo.Template(
61925                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61926                     '<div class="fc-event-inner">' +
61927                         '<span class="fc-event-time">{time}</span>' +
61928                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61929                     '</div>' +
61930                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61931                 '</div>'
61932             );
61933                 
61934         }
61935                
61936         
61937         
61938         this.cells.each(function(c) {
61939             //Roo.log(c.select('.fc-day-content div',true).first());
61940             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61941         });
61942         
61943         var ctr = this.view.el.select('.fc-event-container',true).first();
61944         
61945         var cls;
61946         this.eventStore.each(function(ev){
61947             
61948             this.renderEvent(ev);
61949              
61950              
61951         }, this);
61952         this.view.layout();
61953         
61954     },
61955     
61956     onEventEnter: function (e, el,event,d) {
61957         this.fireEvent('evententer', this, el, event);
61958     },
61959     
61960     onEventLeave: function (e, el,event,d) {
61961         this.fireEvent('eventleave', this, el, event);
61962     },
61963     
61964     onEventClick: function (e, el,event,d) {
61965         this.fireEvent('eventclick', this, el, event);
61966     },
61967     
61968     onMonthChange: function () {
61969         this.store.load();
61970     },
61971     
61972     onLoad: function () {
61973         
61974         //Roo.log('calendar onload');
61975 //         
61976         if(this.eventStore.getCount() > 0){
61977             
61978            
61979             
61980             this.eventStore.each(function(d){
61981                 
61982                 
61983                 // FIXME..
61984                 var add =   d.data;
61985                 if (typeof(add.end_dt) == 'undefined')  {
61986                     Roo.log("Missing End time in calendar data: ");
61987                     Roo.log(d);
61988                     return;
61989                 }
61990                 if (typeof(add.start_dt) == 'undefined')  {
61991                     Roo.log("Missing Start time in calendar data: ");
61992                     Roo.log(d);
61993                     return;
61994                 }
61995                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61996                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61997                 add.id = add.id || d.id;
61998                 add.title = add.title || '??';
61999                 
62000                 this.addItem(d);
62001                 
62002              
62003             },this);
62004         }
62005         
62006         this.renderEvents();
62007     }
62008     
62009
62010 });
62011 /*
62012  grid : {
62013                 xtype: 'Grid',
62014                 xns: Roo.grid,
62015                 listeners : {
62016                     render : function ()
62017                     {
62018                         _this.grid = this;
62019                         
62020                         if (!this.view.el.hasClass('course-timesheet')) {
62021                             this.view.el.addClass('course-timesheet');
62022                         }
62023                         if (this.tsStyle) {
62024                             this.ds.load({});
62025                             return; 
62026                         }
62027                         Roo.log('width');
62028                         Roo.log(_this.grid.view.el.getWidth());
62029                         
62030                         
62031                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62032                             '.course-timesheet .x-grid-row' : {
62033                                 height: '80px'
62034                             },
62035                             '.x-grid-row td' : {
62036                                 'vertical-align' : 0
62037                             },
62038                             '.course-edit-link' : {
62039                                 'color' : 'blue',
62040                                 'text-overflow' : 'ellipsis',
62041                                 'overflow' : 'hidden',
62042                                 'white-space' : 'nowrap',
62043                                 'cursor' : 'pointer'
62044                             },
62045                             '.sub-link' : {
62046                                 'color' : 'green'
62047                             },
62048                             '.de-act-sup-link' : {
62049                                 'color' : 'purple',
62050                                 'text-decoration' : 'line-through'
62051                             },
62052                             '.de-act-link' : {
62053                                 'color' : 'red',
62054                                 'text-decoration' : 'line-through'
62055                             },
62056                             '.course-timesheet .course-highlight' : {
62057                                 'border-top-style': 'dashed !important',
62058                                 'border-bottom-bottom': 'dashed !important'
62059                             },
62060                             '.course-timesheet .course-item' : {
62061                                 'font-family'   : 'tahoma, arial, helvetica',
62062                                 'font-size'     : '11px',
62063                                 'overflow'      : 'hidden',
62064                                 'padding-left'  : '10px',
62065                                 'padding-right' : '10px',
62066                                 'padding-top' : '10px' 
62067                             }
62068                             
62069                         }, Roo.id());
62070                                 this.ds.load({});
62071                     }
62072                 },
62073                 autoWidth : true,
62074                 monitorWindowResize : false,
62075                 cellrenderer : function(v,x,r)
62076                 {
62077                     return v;
62078                 },
62079                 sm : {
62080                     xtype: 'CellSelectionModel',
62081                     xns: Roo.grid
62082                 },
62083                 dataSource : {
62084                     xtype: 'Store',
62085                     xns: Roo.data,
62086                     listeners : {
62087                         beforeload : function (_self, options)
62088                         {
62089                             options.params = options.params || {};
62090                             options.params._month = _this.monthField.getValue();
62091                             options.params.limit = 9999;
62092                             options.params['sort'] = 'when_dt';    
62093                             options.params['dir'] = 'ASC';    
62094                             this.proxy.loadResponse = this.loadResponse;
62095                             Roo.log("load?");
62096                             //this.addColumns();
62097                         },
62098                         load : function (_self, records, options)
62099                         {
62100                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62101                                 // if you click on the translation.. you can edit it...
62102                                 var el = Roo.get(this);
62103                                 var id = el.dom.getAttribute('data-id');
62104                                 var d = el.dom.getAttribute('data-date');
62105                                 var t = el.dom.getAttribute('data-time');
62106                                 //var id = this.child('span').dom.textContent;
62107                                 
62108                                 //Roo.log(this);
62109                                 Pman.Dialog.CourseCalendar.show({
62110                                     id : id,
62111                                     when_d : d,
62112                                     when_t : t,
62113                                     productitem_active : id ? 1 : 0
62114                                 }, function() {
62115                                     _this.grid.ds.load({});
62116                                 });
62117                            
62118                            });
62119                            
62120                            _this.panel.fireEvent('resize', [ '', '' ]);
62121                         }
62122                     },
62123                     loadResponse : function(o, success, response){
62124                             // this is overridden on before load..
62125                             
62126                             Roo.log("our code?");       
62127                             //Roo.log(success);
62128                             //Roo.log(response)
62129                             delete this.activeRequest;
62130                             if(!success){
62131                                 this.fireEvent("loadexception", this, o, response);
62132                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62133                                 return;
62134                             }
62135                             var result;
62136                             try {
62137                                 result = o.reader.read(response);
62138                             }catch(e){
62139                                 Roo.log("load exception?");
62140                                 this.fireEvent("loadexception", this, o, response, e);
62141                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62142                                 return;
62143                             }
62144                             Roo.log("ready...");        
62145                             // loop through result.records;
62146                             // and set this.tdate[date] = [] << array of records..
62147                             _this.tdata  = {};
62148                             Roo.each(result.records, function(r){
62149                                 //Roo.log(r.data);
62150                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62151                                     _this.tdata[r.data.when_dt.format('j')] = [];
62152                                 }
62153                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62154                             });
62155                             
62156                             //Roo.log(_this.tdata);
62157                             
62158                             result.records = [];
62159                             result.totalRecords = 6;
62160                     
62161                             // let's generate some duumy records for the rows.
62162                             //var st = _this.dateField.getValue();
62163                             
62164                             // work out monday..
62165                             //st = st.add(Date.DAY, -1 * st.format('w'));
62166                             
62167                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62168                             
62169                             var firstOfMonth = date.getFirstDayOfMonth();
62170                             var days = date.getDaysInMonth();
62171                             var d = 1;
62172                             var firstAdded = false;
62173                             for (var i = 0; i < result.totalRecords ; i++) {
62174                                 //var d= st.add(Date.DAY, i);
62175                                 var row = {};
62176                                 var added = 0;
62177                                 for(var w = 0 ; w < 7 ; w++){
62178                                     if(!firstAdded && firstOfMonth != w){
62179                                         continue;
62180                                     }
62181                                     if(d > days){
62182                                         continue;
62183                                     }
62184                                     firstAdded = true;
62185                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62186                                     row['weekday'+w] = String.format(
62187                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62188                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62189                                                     d,
62190                                                     date.format('Y-m-')+dd
62191                                                 );
62192                                     added++;
62193                                     if(typeof(_this.tdata[d]) != 'undefined'){
62194                                         Roo.each(_this.tdata[d], function(r){
62195                                             var is_sub = '';
62196                                             var deactive = '';
62197                                             var id = r.id;
62198                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62199                                             if(r.parent_id*1>0){
62200                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62201                                                 id = r.parent_id;
62202                                             }
62203                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62204                                                 deactive = 'de-act-link';
62205                                             }
62206                                             
62207                                             row['weekday'+w] += String.format(
62208                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62209                                                     id, //0
62210                                                     r.product_id_name, //1
62211                                                     r.when_dt.format('h:ia'), //2
62212                                                     is_sub, //3
62213                                                     deactive, //4
62214                                                     desc // 5
62215                                             );
62216                                         });
62217                                     }
62218                                     d++;
62219                                 }
62220                                 
62221                                 // only do this if something added..
62222                                 if(added > 0){ 
62223                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62224                                 }
62225                                 
62226                                 
62227                                 // push it twice. (second one with an hour..
62228                                 
62229                             }
62230                             //Roo.log(result);
62231                             this.fireEvent("load", this, o, o.request.arg);
62232                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62233                         },
62234                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62235                     proxy : {
62236                         xtype: 'HttpProxy',
62237                         xns: Roo.data,
62238                         method : 'GET',
62239                         url : baseURL + '/Roo/Shop_course.php'
62240                     },
62241                     reader : {
62242                         xtype: 'JsonReader',
62243                         xns: Roo.data,
62244                         id : 'id',
62245                         fields : [
62246                             {
62247                                 'name': 'id',
62248                                 'type': 'int'
62249                             },
62250                             {
62251                                 'name': 'when_dt',
62252                                 'type': 'string'
62253                             },
62254                             {
62255                                 'name': 'end_dt',
62256                                 'type': 'string'
62257                             },
62258                             {
62259                                 'name': 'parent_id',
62260                                 'type': 'int'
62261                             },
62262                             {
62263                                 'name': 'product_id',
62264                                 'type': 'int'
62265                             },
62266                             {
62267                                 'name': 'productitem_id',
62268                                 'type': 'int'
62269                             },
62270                             {
62271                                 'name': 'guid',
62272                                 'type': 'int'
62273                             }
62274                         ]
62275                     }
62276                 },
62277                 toolbar : {
62278                     xtype: 'Toolbar',
62279                     xns: Roo,
62280                     items : [
62281                         {
62282                             xtype: 'Button',
62283                             xns: Roo.Toolbar,
62284                             listeners : {
62285                                 click : function (_self, e)
62286                                 {
62287                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62288                                     sd.setMonth(sd.getMonth()-1);
62289                                     _this.monthField.setValue(sd.format('Y-m-d'));
62290                                     _this.grid.ds.load({});
62291                                 }
62292                             },
62293                             text : "Back"
62294                         },
62295                         {
62296                             xtype: 'Separator',
62297                             xns: Roo.Toolbar
62298                         },
62299                         {
62300                             xtype: 'MonthField',
62301                             xns: Roo.form,
62302                             listeners : {
62303                                 render : function (_self)
62304                                 {
62305                                     _this.monthField = _self;
62306                                    // _this.monthField.set  today
62307                                 },
62308                                 select : function (combo, date)
62309                                 {
62310                                     _this.grid.ds.load({});
62311                                 }
62312                             },
62313                             value : (function() { return new Date(); })()
62314                         },
62315                         {
62316                             xtype: 'Separator',
62317                             xns: Roo.Toolbar
62318                         },
62319                         {
62320                             xtype: 'TextItem',
62321                             xns: Roo.Toolbar,
62322                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62323                         },
62324                         {
62325                             xtype: 'Fill',
62326                             xns: Roo.Toolbar
62327                         },
62328                         {
62329                             xtype: 'Button',
62330                             xns: Roo.Toolbar,
62331                             listeners : {
62332                                 click : function (_self, e)
62333                                 {
62334                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62335                                     sd.setMonth(sd.getMonth()+1);
62336                                     _this.monthField.setValue(sd.format('Y-m-d'));
62337                                     _this.grid.ds.load({});
62338                                 }
62339                             },
62340                             text : "Next"
62341                         }
62342                     ]
62343                 },
62344                  
62345             }
62346         };
62347         
62348         *//*
62349  * Based on:
62350  * Ext JS Library 1.1.1
62351  * Copyright(c) 2006-2007, Ext JS, LLC.
62352  *
62353  * Originally Released Under LGPL - original licence link has changed is not relivant.
62354  *
62355  * Fork - LGPL
62356  * <script type="text/javascript">
62357  */
62358  
62359 /**
62360  * @class Roo.LoadMask
62361  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62362  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62363  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62364  * element's UpdateManager load indicator and will be destroyed after the initial load.
62365  * @constructor
62366  * Create a new LoadMask
62367  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62368  * @param {Object} config The config object
62369  */
62370 Roo.LoadMask = function(el, config){
62371     this.el = Roo.get(el);
62372     Roo.apply(this, config);
62373     if(this.store){
62374         this.store.on('beforeload', this.onBeforeLoad, this);
62375         this.store.on('load', this.onLoad, this);
62376         this.store.on('loadexception', this.onLoadException, this);
62377         this.removeMask = false;
62378     }else{
62379         var um = this.el.getUpdateManager();
62380         um.showLoadIndicator = false; // disable the default indicator
62381         um.on('beforeupdate', this.onBeforeLoad, this);
62382         um.on('update', this.onLoad, this);
62383         um.on('failure', this.onLoad, this);
62384         this.removeMask = true;
62385     }
62386 };
62387
62388 Roo.LoadMask.prototype = {
62389     /**
62390      * @cfg {Boolean} removeMask
62391      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62392      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62393      */
62394     removeMask : false,
62395     /**
62396      * @cfg {String} msg
62397      * The text to display in a centered loading message box (defaults to 'Loading...')
62398      */
62399     msg : 'Loading...',
62400     /**
62401      * @cfg {String} msgCls
62402      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62403      */
62404     msgCls : 'x-mask-loading',
62405
62406     /**
62407      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62408      * @type Boolean
62409      */
62410     disabled: false,
62411
62412     /**
62413      * Disables the mask to prevent it from being displayed
62414      */
62415     disable : function(){
62416        this.disabled = true;
62417     },
62418
62419     /**
62420      * Enables the mask so that it can be displayed
62421      */
62422     enable : function(){
62423         this.disabled = false;
62424     },
62425     
62426     onLoadException : function()
62427     {
62428         Roo.log(arguments);
62429         
62430         if (typeof(arguments[3]) != 'undefined') {
62431             Roo.MessageBox.alert("Error loading",arguments[3]);
62432         } 
62433         /*
62434         try {
62435             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62436                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62437             }   
62438         } catch(e) {
62439             
62440         }
62441         */
62442     
62443         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62444     },
62445     // private
62446     onLoad : function()
62447     {
62448         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62449     },
62450
62451     // private
62452     onBeforeLoad : function(){
62453         if(!this.disabled){
62454             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62455         }
62456     },
62457
62458     // private
62459     destroy : function(){
62460         if(this.store){
62461             this.store.un('beforeload', this.onBeforeLoad, this);
62462             this.store.un('load', this.onLoad, this);
62463             this.store.un('loadexception', this.onLoadException, this);
62464         }else{
62465             var um = this.el.getUpdateManager();
62466             um.un('beforeupdate', this.onBeforeLoad, this);
62467             um.un('update', this.onLoad, this);
62468             um.un('failure', this.onLoad, this);
62469         }
62470     }
62471 };/*
62472  * Based on:
62473  * Ext JS Library 1.1.1
62474  * Copyright(c) 2006-2007, Ext JS, LLC.
62475  *
62476  * Originally Released Under LGPL - original licence link has changed is not relivant.
62477  *
62478  * Fork - LGPL
62479  * <script type="text/javascript">
62480  */
62481
62482
62483 /**
62484  * @class Roo.XTemplate
62485  * @extends Roo.Template
62486  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62487 <pre><code>
62488 var t = new Roo.XTemplate(
62489         '&lt;select name="{name}"&gt;',
62490                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62491         '&lt;/select&gt;'
62492 );
62493  
62494 // then append, applying the master template values
62495  </code></pre>
62496  *
62497  * Supported features:
62498  *
62499  *  Tags:
62500
62501 <pre><code>
62502       {a_variable} - output encoded.
62503       {a_variable.format:("Y-m-d")} - call a method on the variable
62504       {a_variable:raw} - unencoded output
62505       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62506       {a_variable:this.method_on_template(...)} - call a method on the template object.
62507  
62508 </code></pre>
62509  *  The tpl tag:
62510 <pre><code>
62511         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62512         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62513         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62514         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62515   
62516         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62517         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62518 </code></pre>
62519  *      
62520  */
62521 Roo.XTemplate = function()
62522 {
62523     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62524     if (this.html) {
62525         this.compile();
62526     }
62527 };
62528
62529
62530 Roo.extend(Roo.XTemplate, Roo.Template, {
62531
62532     /**
62533      * The various sub templates
62534      */
62535     tpls : false,
62536     /**
62537      *
62538      * basic tag replacing syntax
62539      * WORD:WORD()
62540      *
62541      * // you can fake an object call by doing this
62542      *  x.t:(test,tesT) 
62543      * 
62544      */
62545     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62546
62547     /**
62548      * compile the template
62549      *
62550      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62551      *
62552      */
62553     compile: function()
62554     {
62555         var s = this.html;
62556      
62557         s = ['<tpl>', s, '</tpl>'].join('');
62558     
62559         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
62560             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
62561             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
62562             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
62563             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
62564             m,
62565             id     = 0,
62566             tpls   = [];
62567     
62568         while(true == !!(m = s.match(re))){
62569             var forMatch   = m[0].match(nameRe),
62570                 ifMatch   = m[0].match(ifRe),
62571                 execMatch   = m[0].match(execRe),
62572                 namedMatch   = m[0].match(namedRe),
62573                 
62574                 exp  = null, 
62575                 fn   = null,
62576                 exec = null,
62577                 name = forMatch && forMatch[1] ? forMatch[1] : '';
62578                 
62579             if (ifMatch) {
62580                 // if - puts fn into test..
62581                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
62582                 if(exp){
62583                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
62584                 }
62585             }
62586             
62587             if (execMatch) {
62588                 // exec - calls a function... returns empty if true is  returned.
62589                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
62590                 if(exp){
62591                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
62592                 }
62593             }
62594             
62595             
62596             if (name) {
62597                 // for = 
62598                 switch(name){
62599                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
62600                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
62601                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
62602                 }
62603             }
62604             var uid = namedMatch ? namedMatch[1] : id;
62605             
62606             
62607             tpls.push({
62608                 id:     namedMatch ? namedMatch[1] : id,
62609                 target: name,
62610                 exec:   exec,
62611                 test:   fn,
62612                 body:   m[1] || ''
62613             });
62614             if (namedMatch) {
62615                 s = s.replace(m[0], '');
62616             } else { 
62617                 s = s.replace(m[0], '{xtpl'+ id + '}');
62618             }
62619             ++id;
62620         }
62621         this.tpls = [];
62622         for(var i = tpls.length-1; i >= 0; --i){
62623             this.compileTpl(tpls[i]);
62624             this.tpls[tpls[i].id] = tpls[i];
62625         }
62626         this.master = tpls[tpls.length-1];
62627         return this;
62628     },
62629     /**
62630      * same as applyTemplate, except it's done to one of the subTemplates
62631      * when using named templates, you can do:
62632      *
62633      * var str = pl.applySubTemplate('your-name', values);
62634      *
62635      * 
62636      * @param {Number} id of the template
62637      * @param {Object} values to apply to template
62638      * @param {Object} parent (normaly the instance of this object)
62639      */
62640     applySubTemplate : function(id, values, parent)
62641     {
62642         
62643         
62644         var t = this.tpls[id];
62645         
62646         
62647         try { 
62648             if(t.test && !t.test.call(this, values, parent)){
62649                 return '';
62650             }
62651         } catch(e) {
62652             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
62653             Roo.log(e.toString());
62654             Roo.log(t.test);
62655             return ''
62656         }
62657         try { 
62658             
62659             if(t.exec && t.exec.call(this, values, parent)){
62660                 return '';
62661             }
62662         } catch(e) {
62663             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
62664             Roo.log(e.toString());
62665             Roo.log(t.exec);
62666             return ''
62667         }
62668         try {
62669             var vs = t.target ? t.target.call(this, values, parent) : values;
62670             parent = t.target ? values : parent;
62671             if(t.target && vs instanceof Array){
62672                 var buf = [];
62673                 for(var i = 0, len = vs.length; i < len; i++){
62674                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
62675                 }
62676                 return buf.join('');
62677             }
62678             return t.compiled.call(this, vs, parent);
62679         } catch (e) {
62680             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
62681             Roo.log(e.toString());
62682             Roo.log(t.compiled);
62683             return '';
62684         }
62685     },
62686
62687     compileTpl : function(tpl)
62688     {
62689         var fm = Roo.util.Format;
62690         var useF = this.disableFormats !== true;
62691         var sep = Roo.isGecko ? "+" : ",";
62692         var undef = function(str) {
62693             Roo.log("Property not found :"  + str);
62694             return '';
62695         };
62696         
62697         var fn = function(m, name, format, args)
62698         {
62699             //Roo.log(arguments);
62700             args = args ? args.replace(/\\'/g,"'") : args;
62701             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
62702             if (typeof(format) == 'undefined') {
62703                 format= 'htmlEncode';
62704             }
62705             if (format == 'raw' ) {
62706                 format = false;
62707             }
62708             
62709             if(name.substr(0, 4) == 'xtpl'){
62710                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
62711             }
62712             
62713             // build an array of options to determine if value is undefined..
62714             
62715             // basically get 'xxxx.yyyy' then do
62716             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62717             //    (function () { Roo.log("Property not found"); return ''; })() :
62718             //    ......
62719             
62720             var udef_ar = [];
62721             var lookfor = '';
62722             Roo.each(name.split('.'), function(st) {
62723                 lookfor += (lookfor.length ? '.': '') + st;
62724                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62725             });
62726             
62727             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62728             
62729             
62730             if(format && useF){
62731                 
62732                 args = args ? ',' + args : "";
62733                  
62734                 if(format.substr(0, 5) != "this."){
62735                     format = "fm." + format + '(';
62736                 }else{
62737                     format = 'this.call("'+ format.substr(5) + '", ';
62738                     args = ", values";
62739                 }
62740                 
62741                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62742             }
62743              
62744             if (args.length) {
62745                 // called with xxyx.yuu:(test,test)
62746                 // change to ()
62747                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62748             }
62749             // raw.. - :raw modifier..
62750             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62751             
62752         };
62753         var body;
62754         // branched to use + in gecko and [].join() in others
62755         if(Roo.isGecko){
62756             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62757                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62758                     "';};};";
62759         }else{
62760             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62761             body.push(tpl.body.replace(/(\r\n|\n)/g,
62762                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62763             body.push("'].join('');};};");
62764             body = body.join('');
62765         }
62766         
62767         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62768        
62769         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62770         eval(body);
62771         
62772         return this;
62773     },
62774
62775     applyTemplate : function(values){
62776         return this.master.compiled.call(this, values, {});
62777         //var s = this.subs;
62778     },
62779
62780     apply : function(){
62781         return this.applyTemplate.apply(this, arguments);
62782     }
62783
62784  });
62785
62786 Roo.XTemplate.from = function(el){
62787     el = Roo.getDom(el);
62788     return new Roo.XTemplate(el.value || el.innerHTML);
62789 };